# -*- coding: utf-8 -*- """ 출력 모듈 - 번역 결과를 시각적으로 비교하고 출력 원본, 인페인팅, 최종 결과를 나란히 배치하여 한눈에 볼 수 있게 합니다. """ import cv2 import numpy as np from typing import List, Dict, Any, Tuple, Optional import os from datetime import datetime import json class OutputModule: def __init__(self): """출력 모듈 초기화""" self.output_history = [] print("출력 모듈 초기화 완료") def create_comparison_output(self, original_path: str, inpainted_image: np.ndarray, final_image: np.ndarray, output_dir: str, inpainting_method: str = "unknown") -> str: """ 원본, 인페인팅, 최종 결과를 비교하는 출력 이미지 생성 Args: original_path (str): 원본 이미지 경로 inpainted_image (np.ndarray): 인페인팅된 이미지 final_image (np.ndarray): 최종 번역 결과 이미지 output_dir (str): 출력 디렉토리 inpainting_method (str): 사용된 인페인팅 방법 Returns: str: 생성된 비교 이미지 경로 """ # 원본 이미지 로드 original_image = cv2.imread(original_path) if original_image is None: print(f"원본 이미지를 읽을 수 없습니다: {original_path}") return None # 출력 디렉토리 생성 os.makedirs(output_dir, exist_ok=True) # 비교 이미지 생성 comparison_image = self._create_three_panel_comparison( original_image, inpainted_image, final_image, inpainting_method ) # 파일명 생성 (타임스탬프 포함) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") original_name = os.path.splitext(os.path.basename(original_path))[0] # 개별 이미지 저장 original_img_path = os.path.join(output_dir, f"{original_name}_{inpainting_method}_{timestamp}_original.jpg") inpainted_img_path = os.path.join(output_dir, f"{original_name}_{inpainting_method}_{timestamp}_inpainted.jpg") final_img_path = os.path.join(output_dir, f"{original_name}_{inpainting_method}_{timestamp}_final.jpg") cv2.imwrite(original_img_path, original_image) cv2.imwrite(inpainted_img_path, inpainted_image) cv2.imwrite(final_img_path, final_image) # 비교 이미지 저장 (기존처럼) output_filename = f"{original_name}_{inpainting_method}_{timestamp}_comparison.jpg" output_path = os.path.join(output_dir, output_filename) cv2.imwrite(output_path, comparison_image) # 메타데이터 저장 self._save_metadata(output_dir, output_filename, { 'original_path': original_path, 'inpainting_method': inpainting_method, 'timestamp': timestamp, 'output_path': output_path, 'original_img_path': original_img_path, 'inpainted_img_path': inpainted_img_path, 'final_img_path': final_img_path }) # 히스토리에 추가 self.output_history.append({ 'timestamp': timestamp, 'original_path': original_path, 'output_path': output_path, 'inpainting_method': inpainting_method, 'original_img_path': original_img_path, 'inpainted_img_path': inpainted_img_path, 'final_img_path': final_img_path }) print(f"개별 이미지 저장 완료: {original_img_path}, {inpainted_img_path}, {final_img_path}") print(f"비교 이미지 생성 완료: {output_path}") return output_path def _create_three_panel_comparison(self, original: np.ndarray, inpainted: np.ndarray, final: np.ndarray, method: str) -> np.ndarray: """ 3개 패널 비교 이미지 생성 Args: original (np.ndarray): 원본 이미지 inpainted (np.ndarray): 인페인팅된 이미지 final (np.ndarray): 최종 결과 이미지 method (str): 인페인팅 방법 Returns: np.ndarray: 비교 이미지 """ # 이미지 크기 통일 target_height = 400 # 원본 이미지 크기에 맞춰 너비 계산 original_height, original_width = original.shape[:2] target_width = int(target_height * original_width / original_height) # 각 이미지 리사이즈 original_resized = cv2.resize(original, (target_width, target_height)) inpainted_resized = cv2.resize(inpainted, (target_width, target_height)) final_resized = cv2.resize(final, (target_width, target_height)) # 비교 이미지 캔버스 생성 panel_spacing = 10 header_height = 60 footer_height = 40 total_width = target_width * 3 + panel_spacing * 2 total_height = target_height + header_height + footer_height comparison = np.ones((total_height, total_width, 3), dtype=np.uint8) * 255 # 헤더 영역 (제목) cv2.rectangle(comparison, (0, 0), (total_width, header_height), (240, 240, 240), -1) # 제목 텍스트 title = f"Image Translation Result - Method: {method.upper()}" title_size = cv2.getTextSize(title, cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2)[0] title_x = (total_width - title_size[0]) // 2 cv2.putText(comparison, title, (title_x, 35), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (50, 50, 50), 2) # 이미지 배치 y_offset = header_height # 원본 이미지 x1 = 0 comparison[y_offset:y_offset+target_height, x1:x1+target_width] = original_resized # 인페인팅된 이미지 x2 = target_width + panel_spacing comparison[y_offset:y_offset+target_height, x2:x2+target_width] = inpainted_resized # 최종 결과 이미지 x3 = target_width * 2 + panel_spacing * 2 comparison[y_offset:y_offset+target_height, x3:x3+target_width] = final_resized # 라벨 추가 labels = ["Original", "Inpainted", "Final Result"] label_positions = [x1, x2, x3] for label, x_pos in zip(labels, label_positions): # 라벨 배경 label_bg_y = y_offset + target_height + 5 cv2.rectangle(comparison, (x_pos, label_bg_y), (x_pos + target_width, label_bg_y + 30), (220, 220, 220), -1) # 라벨 텍스트 label_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)[0] label_x = x_pos + (target_width - label_size[0]) // 2 cv2.putText(comparison, label, (label_x, label_bg_y + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (50, 50, 50), 2) # 구분선 추가 line_color = (200, 200, 200) cv2.line(comparison, (target_width + panel_spacing//2, header_height), (target_width + panel_spacing//2, y_offset + target_height), line_color, 2) cv2.line(comparison, (target_width*2 + panel_spacing + panel_spacing//2, header_height), (target_width*2 + panel_spacing + panel_spacing//2, y_offset + target_height), line_color, 2) return comparison def create_detailed_output(self, original_path: str, ocr_results: List[Dict], translated_texts: List[str], inpainted_image: np.ndarray, final_image: np.ndarray, output_dir: str, inpainting_method: str) -> str: """ 상세 정보가 포함된 출력 생성 Args: original_path (str): 원본 이미지 경로 ocr_results (List[Dict]): OCR 결과 translated_texts (List[str]): 번역 결과 inpainted_image (np.ndarray): 인페인팅된 이미지 final_image (np.ndarray): 최종 결과 output_dir (str): 출력 디렉토리 inpainting_method (str): 인페인팅 방법 Returns: str: 생성된 상세 출력 경로 """ # 기본 비교 이미지 생성 comparison_path = self.create_comparison_output( original_path, inpainted_image, final_image, output_dir, inpainting_method ) # 상세 정보 이미지 생성 detail_image = self._create_detail_panel(ocr_results, translated_texts, inpainting_method) # 전체 이미지 결합 comparison_image = cv2.imread(comparison_path) combined_image = self._combine_comparison_and_detail(comparison_image, detail_image) # 상세 출력 저장 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") original_name = os.path.splitext(os.path.basename(original_path))[0] detail_filename = f"{original_name}_{inpainting_method}_{timestamp}_detailed.jpg" detail_path = os.path.join(output_dir, detail_filename) cv2.imwrite(detail_path, combined_image) print(f"상세 출력 생성 완료: {detail_path}") return detail_path def _create_detail_panel(self, ocr_results: List[Dict], translated_texts: List[str], method: str) -> np.ndarray: """ 상세 정보 패널 생성 Args: ocr_results (List[Dict]): OCR 결과 translated_texts (List[str]): 번역 결과 method (str): 인페인팅 방법 Returns: np.ndarray: 상세 정보 패널 이미지 """ # 패널 크기 계산 panel_width = 400 line_height = 25 header_height = 40 padding = 20 num_texts = len(ocr_results) panel_height = header_height + (num_texts * line_height * 3) + padding * 2 # 패널 생성 panel = np.ones((panel_height, panel_width, 3), dtype=np.uint8) * 250 # 헤더 cv2.rectangle(panel, (0, 0), (panel_width, header_height), (200, 200, 200), -1) cv2.putText(panel, "Translation Details", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (50, 50, 50), 2) # 방법 정보 method_text = f"Method: {method}" cv2.putText(panel, method_text, (10, header_height + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (100, 100, 100), 1) # 번역 정보 y_offset = header_height + 45 for i, (ocr_result, translated_text) in enumerate(zip(ocr_results, translated_texts)): # 원본 텍스트 original_text = ocr_result['text'] confidence = ocr_result['confidence'] # 텍스트가 너무 길면 줄임 if len(original_text) > 30: original_text = original_text[:30] + "..." if len(translated_text) > 30: translated_text = translated_text[:30] + "..." # 번호 cv2.putText(panel, f"{i+1}.", (10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 1) # 원본 텍스트 cv2.putText(panel, f"CN: {original_text}", (30, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 150), 1) # 번역된 텍스트 cv2.putText(panel, f"KR: {translated_text}", (30, y_offset + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (150, 0, 0), 1) # 신뢰도 cv2.putText(panel, f"Conf: {confidence:.2f}", (30, y_offset + 30), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (100, 100, 100), 1) y_offset += line_height * 3 + 10 # 구분선 if i < len(ocr_results) - 1: cv2.line(panel, (20, y_offset - 5), (panel_width - 20, y_offset - 5), (220, 220, 220), 1) return panel def _combine_comparison_and_detail(self, comparison: np.ndarray, detail: np.ndarray) -> np.ndarray: """비교 이미지와 상세 정보 패널 결합""" comp_height, comp_width = comparison.shape[:2] detail_height, detail_width = detail.shape[:2] # 높이 맞추기 if comp_height > detail_height: # 상세 패널 높이 늘리기 extended_detail = np.ones((comp_height, detail_width, 3), dtype=np.uint8) * 250 extended_detail[:detail_height, :] = detail detail = extended_detail elif detail_height > comp_height: # 비교 이미지 높이 늘리기 extended_comparison = np.ones((detail_height, comp_width, 3), dtype=np.uint8) * 255 extended_comparison[:comp_height, :] = comparison comparison = extended_comparison # 수평으로 결합 combined = np.hstack([comparison, detail]) return combined def _save_metadata(self, output_dir: str, filename: str, metadata: Dict[str, Any]): """메타데이터를 JSON 파일로 저장""" metadata_filename = os.path.splitext(filename)[0] + "_metadata.json" metadata_path = os.path.join(output_dir, metadata_filename) with open(metadata_path, 'w', encoding='utf-8') as f: json.dump(metadata, f, ensure_ascii=False, indent=2) def create_batch_summary(self, output_dir: str, batch_results: List[Dict]) -> str: """ 일괄 처리 결과 요약 생성 Args: output_dir (str): 출력 디렉토리 batch_results (List[Dict]): 일괄 처리 결과 Returns: str: 요약 보고서 경로 """ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") summary_filename = f"batch_summary_{timestamp}.html" summary_path = os.path.join(output_dir, summary_filename) # HTML 보고서 생성 html_content = self._generate_html_summary(batch_results) with open(summary_path, 'w', encoding='utf-8') as f: f.write(html_content) print(f"일괄 처리 요약 생성 완료: {summary_path}") return summary_path def _generate_html_summary(self, batch_results: List[Dict]) -> str: """ 일괄 처리 결과의 HTML 요약 생성 Args: batch_results (List[Dict]): 일괄 처리 결과 리스트 Returns: str: HTML 내용 """ html = """
Generated: {timestamp}
Total Images: {total_count}
Successful: {success_count}
Failed: {failed_count}
Status: {'Success' if result.get('success', False) else 'Failed'}
Processing Time: {result.get('processing_time', 'N/A')}
Detected Texts: {result.get('text_count', 0)}