#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 마스크 CLI 도구 - 마스크 모듈의 모든 기능을 커맨드라인에서 사용 """ import argparse import sys import os import json from pathlib import Path # 모듈 경로 추가 sys.path.append(str(Path(__file__).parent / "test" / "img_test2" / "modules")) from ocr_module import OCRModule from mask_module import MaskModule def create_parser(): """커맨드라인 파서 생성""" parser = argparse.ArgumentParser( description="마스크 생성 및 처리 도구", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 사용 예시: # 기본 마스크 생성 python mask_cli.py basic -i image.jpg -o output/ # 강화된 블러 마스크 python mask_cli.py enhanced -i image.jpg -o output/ --expansion 15 --blur 25 # 초강력 블러 마스크 python mask_cli.py ultra -i image.jpg -o output/ --expansion 30 --blur 40 # 고급 마스크 (모폴로지) python mask_cli.py advanced -i image.jpg -o output/ --method morphology # 페더링 마스크 python mask_cli.py feathered -i image.jpg -o output/ --feather 20 # 마스크 통계 확인 python mask_cli.py stats -i image.jpg # 여러 방법으로 일괄 처리 python mask_cli.py batch -i image.jpg -o output/ --methods basic,enhanced,ultra # OCR 결과 저장 python mask_cli.py ocr-only -i image.jpg -o results.json """ ) subparsers = parser.add_subparsers(dest='command', help='사용할 마스크 방법') # 공통 인수 def add_common_args(parser): parser.add_argument('-i', '--input', required=True, help='입력 이미지 파일') parser.add_argument('-o', '--output', help='출력 디렉토리 또는 파일') parser.add_argument('--ocr-results', help='OCR 결과 JSON 파일 (선택사항)') parser.add_argument('--lang', default='korean', help='OCR 언어 (기본: korean)') parser.add_argument('--verbose', '-v', action='store_true', help='상세 출력') # 1. 기본 마스크 basic_parser = subparsers.add_parser('basic', help='기본 마스크 생성') add_common_args(basic_parser) basic_parser.add_argument('--expansion', type=int, default=10, help='확장 크기 (기본: 10)') basic_parser.add_argument('--blur', type=int, default=15, help='블러 크기 (기본: 15)') # 2. 강화된 블러 마스크 enhanced_parser = subparsers.add_parser('enhanced', help='강화된 블러 마스크 생성') add_common_args(enhanced_parser) enhanced_parser.add_argument('--expansion', type=int, default=12, help='확장 크기 (기본: 12)') enhanced_parser.add_argument('--blur', type=int, default=20, help='블러 크기 (기본: 20)') # 3. 초강력 블러 마스크 ultra_parser = subparsers.add_parser('ultra', help='초강력 블러 마스크 생성') add_common_args(ultra_parser) ultra_parser.add_argument('--expansion', type=int, default=25, help='확장 크기 (기본: 25)') ultra_parser.add_argument('--blur', type=int, default=35, help='블러 크기 (기본: 35)') # 4. 고급 마스크 advanced_parser = subparsers.add_parser('advanced', help='고급 마스크 생성') add_common_args(advanced_parser) advanced_parser.add_argument('--method', choices=['morphology', 'adaptive', 'contour'], default='morphology', help='고급 마스크 방법') # 5. 페더링 마스크 feathered_parser = subparsers.add_parser('feathered', help='페더링 마스크 생성') add_common_args(feathered_parser) feathered_parser.add_argument('--feather', type=int, default=10, help='페더링 크기 (기본: 10)') feathered_parser.add_argument('--expansion', type=int, default=10, help='기본 확장 크기 (기본: 10)') feathered_parser.add_argument('--blur', type=int, default=15, help='기본 블러 크기 (기본: 15)') # 6. 마스크 통계 stats_parser = subparsers.add_parser('stats', help='마스크 통계 정보') add_common_args(stats_parser) stats_parser.add_argument('--expansion', type=int, default=10, help='확장 크기 (기본: 10)') stats_parser.add_argument('--blur', type=int, default=15, help='블러 크기 (기본: 15)') # 7. 일괄 처리 batch_parser = subparsers.add_parser('batch', help='여러 방법으로 일괄 처리') add_common_args(batch_parser) batch_parser.add_argument('--methods', default='basic,enhanced,ultra', help='사용할 방법들 (쉼표로 구분, 기본: basic,enhanced,ultra)') batch_parser.add_argument('--expansion', type=int, default=15, help='확장 크기 (기본: 15)') batch_parser.add_argument('--blur', type=int, default=20, help='블러 크기 (기본: 20)') # 8. OCR만 실행 ocr_parser = subparsers.add_parser('ocr-only', help='OCR만 실행하고 결과 저장') add_common_args(ocr_parser) # 9. 시각화 viz_parser = subparsers.add_parser('visualize', help='마스크 시각화') add_common_args(viz_parser) viz_parser.add_argument('--expansion', type=int, default=10, help='확장 크기 (기본: 10)') viz_parser.add_argument('--blur', type=int, default=15, help='블러 크기 (기본: 15)') viz_parser.add_argument('--alpha', type=float, default=0.5, help='투명도 (기본: 0.5)') # 10. 테스트 test_parser = subparsers.add_parser('test', help='마스크 모듈 테스트') test_parser.add_argument('-o', '--output', default='test_output', help='테스트 출력 디렉토리') return parser def load_ocr_results(ocr_results_path): """OCR 결과 JSON 파일 로드""" try: with open(ocr_results_path, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: print(f"❌ OCR 결과 파일 로드 실패: {e}") return None def save_ocr_results(ocr_results, output_path): """OCR 결과를 JSON 파일로 저장""" try: with open(output_path, 'w', encoding='utf-8') as f: json.dump(ocr_results, f, ensure_ascii=False, indent=2) print(f"💾 OCR 결과 저장: {output_path}") except Exception as e: print(f"❌ OCR 결과 저장 실패: {e}") def run_ocr(image_path, lang='korean', ocr_results_path=None): """OCR 실행""" if ocr_results_path and os.path.exists(ocr_results_path): print(f"📁 기존 OCR 결과 사용: {ocr_results_path}") return load_ocr_results(ocr_results_path) print(f"🔍 OCR 실행 중: {image_path}") ocr_module = OCRModule(lang=lang) ocr_results = ocr_module.detect_text(image_path, method='polygon') if not ocr_results: print("❌ OCR 결과가 없습니다.") return None print(f"✅ OCR 완료: {len(ocr_results)}개 텍스트 영역 발견") return ocr_results def print_mask_stats(mask_module, mask): """마스크 통계 출력""" stats = mask_module.get_mask_statistics(mask) print(f"\n📊 마스크 통계:") print(f" - 총 픽셀: {stats['total_pixels']:,}") print(f" - 마스크 픽셀: {stats['mask_pixels']:,}") print(f" - 커버리지: {stats['coverage_percent']:.2f}%") print(f" - 영역 수: {stats['num_regions']}") print(f" - 최대 영역 크기: {stats['largest_region_area']:,}") def basic_mask(args): """기본 마스크 생성""" print(f"🎭 기본 마스크 생성 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() mask = mask_module.create_masks(args.input, ocr_results, args.expansion, args.blur) if mask is None: print("❌ 마스크 생성 실패") return if args.verbose: print_mask_stats(mask_module, mask) if args.output: os.makedirs(args.output, exist_ok=True) mask, mask_path = mask_module.create_masks_with_save( args.input, ocr_results, args.output, args.expansion, args.blur ) print(f"✅ 기본 마스크 완료: {args.output}") def enhanced_mask(args): """강화된 블러 마스크 생성""" print(f"🌟 강화된 블러 마스크 생성 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() # 기본 마스크 생성 후 강화된 블러 적용 import cv2 import numpy as np image = cv2.imread(args.input) height, width = image.shape[:2] base_mask = np.zeros((height, width), dtype=np.uint8) for result in ocr_results: polygon = result['polygon'] polygon_np = np.array(polygon, dtype=np.int32) cv2.fillPoly(base_mask, [polygon_np], 255) enhanced_mask = mask_module.process_mask_with_enhanced_blur( base_mask, args.expansion, args.blur ) if args.verbose: print_mask_stats(mask_module, enhanced_mask) if args.output: os.makedirs(args.output, exist_ok=True) base_name = os.path.splitext(os.path.basename(args.input))[0] # 마스크 저장 mask_path = os.path.join(args.output, f"{base_name}_enhanced_mask.jpg") cv2.imwrite(mask_path, enhanced_mask) print(f"💾 강화된 마스크 저장: {mask_path}") # 시각화 저장 viz_path = os.path.join(args.output, f"{base_name}_enhanced_visualization.jpg") mask_module.visualize_mask(args.input, enhanced_mask, viz_path) print(f"✅ 강화된 블러 마스크 완료: {args.output}") def ultra_mask(args): """초강력 블러 마스크 생성""" print(f"🚀 초강력 블러 마스크 생성 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() ultra_mask = mask_module.create_ultra_blur_mask( args.input, ocr_results, args.expansion, args.blur ) if ultra_mask is None: print("❌ 초강력 블러 마스크 생성 실패") return if args.verbose: print_mask_stats(mask_module, ultra_mask) if args.output: os.makedirs(args.output, exist_ok=True) base_name = os.path.splitext(os.path.basename(args.input))[0] # 마스크 저장 import cv2 mask_path = os.path.join(args.output, f"{base_name}_ultra_mask.jpg") cv2.imwrite(mask_path, ultra_mask) print(f"💾 초강력 마스크 저장: {mask_path}") # 시각화 저장 viz_path = os.path.join(args.output, f"{base_name}_ultra_visualization.jpg") mask_module.visualize_mask(args.input, ultra_mask, viz_path) print(f"✅ 초강력 블러 마스크 완료: {args.output}") def advanced_mask(args): """고급 마스크 생성""" print(f"🔧 고급 마스크 생성 시작 ({args.method} 방법)...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() advanced_mask = mask_module.create_advanced_mask( args.input, ocr_results, args.method ) if advanced_mask is None: print("❌ 고급 마스크 생성 실패") return if args.verbose: print_mask_stats(mask_module, advanced_mask) if args.output: os.makedirs(args.output, exist_ok=True) base_name = os.path.splitext(os.path.basename(args.input))[0] # 마스크 저장 import cv2 mask_path = os.path.join(args.output, f"{base_name}_{args.method}_mask.jpg") cv2.imwrite(mask_path, advanced_mask) print(f"💾 {args.method} 마스크 저장: {mask_path}") # 시각화 저장 viz_path = os.path.join(args.output, f"{base_name}_{args.method}_visualization.jpg") mask_module.visualize_mask(args.input, advanced_mask, viz_path) print(f"✅ 고급 마스크 완료 ({args.method}): {args.output}") def feathered_mask(args): """페더링 마스크 생성""" print(f"✨ 페더링 마스크 생성 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() # 기본 마스크 생성 base_mask = mask_module.create_masks(args.input, ocr_results, args.expansion, args.blur) if base_mask is None: print("❌ 기본 마스크 생성 실패") return # 페더링 적용 feathered_mask = mask_module.create_feathered_mask(base_mask, args.feather) if args.verbose: print_mask_stats(mask_module, feathered_mask) if args.output: os.makedirs(args.output, exist_ok=True) base_name = os.path.splitext(os.path.basename(args.input))[0] # 마스크 저장 import cv2 mask_path = os.path.join(args.output, f"{base_name}_feathered_mask.jpg") cv2.imwrite(mask_path, feathered_mask) print(f"💾 페더링 마스크 저장: {mask_path}") # 시각화 저장 viz_path = os.path.join(args.output, f"{base_name}_feathered_visualization.jpg") mask_module.visualize_mask(args.input, feathered_mask, viz_path) print(f"✅ 페더링 마스크 완료: {args.output}") def stats_mask(args): """마스크 통계 정보""" print(f"📊 마스크 통계 분석 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() mask = mask_module.create_masks(args.input, ocr_results, args.expansion, args.blur) if mask is None: print("❌ 마스크 생성 실패") return print_mask_stats(mask_module, mask) # OCR 결과 정보도 출력 print(f"\n🔍 OCR 결과 정보:") for i, result in enumerate(ocr_results, 1): print(f" 영역 #{i}: '{result['text']}' (신뢰도: {result['confidence']:.1%})") def batch_mask(args): """일괄 처리""" print(f"🔄 일괄 처리 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return methods = args.methods.split(',') mask_module = MaskModule() if args.output: os.makedirs(args.output, exist_ok=True) base_name = os.path.splitext(os.path.basename(args.input))[0] for method in methods: method = method.strip() print(f"\n🔹 {method} 방법 처리 중...") if method == 'basic': mask = mask_module.create_masks(args.input, ocr_results, args.expansion, args.blur) elif method == 'enhanced': import cv2 import numpy as np image = cv2.imread(args.input) height, width = image.shape[:2] base_mask = np.zeros((height, width), dtype=np.uint8) for result in ocr_results: polygon = result['polygon'] polygon_np = np.array(polygon, dtype=np.int32) cv2.fillPoly(base_mask, [polygon_np], 255) mask = mask_module.process_mask_with_enhanced_blur(base_mask, args.expansion, args.blur) elif method == 'ultra': mask = mask_module.create_ultra_blur_mask(args.input, ocr_results, args.expansion, args.blur) elif method in ['morphology', 'adaptive', 'contour']: mask = mask_module.create_advanced_mask(args.input, ocr_results, method) elif method == 'feathered': base_mask = mask_module.create_masks(args.input, ocr_results, args.expansion, args.blur) mask = mask_module.create_feathered_mask(base_mask, 10) else: print(f" ❌ 알 수 없는 방법: {method}") continue if mask is not None and args.output: import cv2 mask_path = os.path.join(args.output, f"{base_name}_{method}_mask.jpg") cv2.imwrite(mask_path, mask) viz_path = os.path.join(args.output, f"{base_name}_{method}_visualization.jpg") mask_module.visualize_mask(args.input, mask, viz_path) print(f" ✅ {method} 완료") print(f"\n🎉 일괄 처리 완료: {args.output}") def ocr_only(args): """OCR만 실행""" print(f"🔍 OCR만 실행...") ocr_results = run_ocr(args.input, args.lang) if not ocr_results: return # 결과 출력 print(f"\n📋 OCR 결과 ({len(ocr_results)}개 영역):") for i, result in enumerate(ocr_results, 1): print(f" {i}. '{result['text']}' (신뢰도: {result['confidence']:.1%})") print(f" 위치: {result['bbox']}") print(f" 폴리곤: {result['polygon']}") # JSON 저장 if args.output: save_ocr_results(ocr_results, args.output) def visualize_mask(args): """마스크 시각화""" print(f"🎨 마스크 시각화 시작...") ocr_results = run_ocr(args.input, args.lang, args.ocr_results) if not ocr_results: return mask_module = MaskModule() mask = mask_module.create_masks(args.input, ocr_results, args.expansion, args.blur) if mask is None: print("❌ 마스크 생성 실패") return if args.output: os.makedirs(os.path.dirname(args.output) if os.path.dirname(args.output) else '.', exist_ok=True) mask_module.visualize_mask(args.input, mask, args.output, args.alpha) print(f"✅ 시각화 완료: {args.output}") else: # 기본 출력 경로 base_name = os.path.splitext(os.path.basename(args.input))[0] output_path = f"{base_name}_visualization.jpg" mask_module.visualize_mask(args.input, mask, output_path, args.alpha) print(f"✅ 시각화 완료: {output_path}") def test_mask(args): """마스크 모듈 테스트""" print(f"🧪 마스크 모듈 테스트 시작...") mask_module = MaskModule() # 출력 디렉토리 설정 original_test_dir = "test_output" if args.output != original_test_dir: # test_module 메서드에서 사용하는 경로를 변경하기 위해 임시로 변경 import tempfile import shutil # 임시 디렉토리에서 테스트 실행 with tempfile.TemporaryDirectory() as temp_dir: temp_test_dir = os.path.join(temp_dir, "test_output") os.makedirs(temp_test_dir, exist_ok=True) # 현재 디렉토리 변경 original_cwd = os.getcwd() os.chdir(temp_dir) try: mask_module.test_module() # 결과를 원하는 출력 디렉토리로 복사 os.makedirs(args.output, exist_ok=True) for file in os.listdir(temp_test_dir): shutil.copy2(os.path.join(temp_test_dir, file), args.output) print(f"✅ 테스트 완료: {args.output}") finally: os.chdir(original_cwd) else: mask_module.test_module() print(f"✅ 테스트 완료: {args.output}") def main(): """메인 함수""" parser = create_parser() args = parser.parse_args() if not args.command: parser.print_help() return # 입력 파일 확인 (test 명령어 제외) if args.command != 'test' and hasattr(args, 'input') and not os.path.exists(args.input): print(f"❌ 입력 파일이 존재하지 않습니다: {args.input}") return # 명령어 실행 try: if args.command == 'basic': basic_mask(args) elif args.command == 'enhanced': enhanced_mask(args) elif args.command == 'ultra': ultra_mask(args) elif args.command == 'advanced': advanced_mask(args) elif args.command == 'feathered': feathered_mask(args) elif args.command == 'stats': stats_mask(args) elif args.command == 'batch': batch_mask(args) elif args.command == 'ocr-only': ocr_only(args) elif args.command == 'visualize': visualize_mask(args) elif args.command == 'test': test_mask(args) else: print(f"❌ 알 수 없는 명령어: {args.command}") parser.print_help() except KeyboardInterrupt: print("\n⚠️ 사용자에 의해 중단되었습니다.") except Exception as e: print(f"❌ 오류 발생: {e}") if args.verbose if hasattr(args, 'verbose') else False: import traceback traceback.print_exc() if __name__ == "__main__": main()