EpiText-Hanja-OCR / ai_modules /preprocessor_unified.py
donghyun
Add OCR code, modules, and weights
8672bad
# Epitext_Back/ai_modules/preprocessor_unified.py
# -*- coding: utf-8 -*-
"""
================================================================================
Unified Image Preprocessing Module for Epitext AI Project
================================================================================
๋ชจ๋“ˆ๋ช…: preprocessor_unified.py (v1.0.0 - Production Ready)
์ž‘์„ฑ์ผ: 2025-12-02
๋ชฉ์ : ํ•œ์ž ์ด๋ฏธ์ง€๋ฅผ Swin Gray์™€ OCR์šฉ์œผ๋กœ ๋™์‹œ์— ์ „์ฒ˜๋ฆฌ
์ƒํƒœ: Production Ready
ํ•ต์‹ฌ ๊ธฐ๋Šฅ:
ํ•œ ๋ฒˆ์— ๋‘ ๊ฐ€์ง€ ์ „์ฒ˜๋ฆฌ ์™„๋ฃŒ:
1. Swin Gray: ๊ทธ๋ ˆ์ด ๋น„์ด์ง„ํ™” -> 3์ฑ„๋„ (์ •๋ณด ์†์‹ค ์ตœ์†Œ)
2. OCR: ์ด์ง„ํ™” -> 1์ฑ„๋„ (๋ช…ํ™•ํ•œ ํ‘๋ฐฑ)
์ž๋™ ๋ฐฐ๊ฒฝ ๋ณด์žฅ:
- Swin: ๋ฐ์€๋ฐฐ๊ฒฝ (>=127)
- OCR: ํฐ๋ฐฐ๊ฒฝ + ๊ฒ€์ •๊ธ€์ž (255/0)
ํƒ๋ณธ ์ž๋™ ๊ฒ€์ถœ: ํฐ ์–ด๋‘์šด ์˜์—ญ ์‹๋ณ„
์˜์—ญ ๊ฒ€์ถœ 1ํšŒ: ํšจ์œจ์„ฑ
์„ค์ • ํŒŒ์ผ ์ง€์›: JSON ๊ธฐ๋ฐ˜ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
๋กœ๊น… ์ง€์›: DEBUG, INFO, WARNING, ERROR
์˜์กด์„ฑ:
- opencv-python >= 4.8.0
- numpy >= 1.24.0
๋‹จ์ผ ํ•จ์ˆ˜:
preprocess_image_unified(input_path, output_swin_path, output_ocr_path, ...)
์‚ฌ์šฉ ์˜ˆ์‹œ:
>>> from ai_modules.preprocessor_unified import preprocess_image_unified
>>> result = preprocess_image_unified(
... "input.jpg",
... "swin.jpg",
... "ocr.png"
... )
================================================================================
"""
import cv2
import numpy as np
from pathlib import Path
import json
import logging
from typing import Dict, Optional, Tuple
# ================================================================================
# Logging Configuration
# ================================================================================
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - [%(levelname)s] %(message)s'
)
logger = logging.getLogger(__name__)
# ================================================================================
# Constants
# ================================================================================
# ๊ธฐ๋ณธ ์„ค์ •๊ฐ’
DEFAULT_MARGIN = 10
DEFAULT_BRIGHTNESS_THRESHOLD = 127
DEFAULT_RUBBING_MIN_AREA_RATIO = 0.1
DEFAULT_TEXT_MIN_AREA = 16
DEFAULT_TEXT_AREA_RATIO = 0.00005
DEFAULT_MORPHOLOGY_KERNEL_SIZE = (2, 2)
DEFAULT_MORPHOLOGY_CLOSE_ITERATIONS = 3
DEFAULT_MORPHOLOGY_OPEN_ITERATIONS = 2
DEFAULT_RUBBING_KERNEL_SIZE = (5, 5)
DEFAULT_RUBBING_CLOSE_ITERATIONS = 10
DEFAULT_RUBBING_OPEN_ITERATIONS = 5
# ================================================================================
# Main Preprocessing Class
# ================================================================================
class UnifiedImagePreprocessor:
"""
ํ†ตํ•ฉ ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ ํด๋ž˜์Šค (Swin + OCR)
ํ•œ ๋ฒˆ์˜ ์ฒ˜๋ฆฌ๋กœ Swin Gray์™€ OCR์šฉ ์ด๋ฏธ์ง€๋ฅผ ๋ชจ๋‘ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
Attributes:
config (dict): ์ „์ฒ˜๋ฆฌ ์„ค์ • ํŒŒ๋ผ๋ฏธํ„ฐ
Example:
>>> prep = UnifiedImagePreprocessor()
>>> result = prep.preprocess_unified("input.jpg", "swin.jpg", "ocr.png")
"""
def __init__(self, config_path: Optional[str] = None) -> None:
"""
UnifiedImagePreprocessor ์ดˆ๊ธฐํ™”
Args:
config_path (str, optional): ์„ค์ • ํŒŒ์ผ ๊ฒฝ๋กœ (JSON)
"""
self.config = self._load_config(config_path)
logger.info("[INIT] UnifiedImagePreprocessor v1.0.0 ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
def _load_config(self, config_path: Optional[str]) -> Dict:
"""์„ค์ • ํŒŒ์ผ ๋กœ๋“œ"""
default_config = {
"margin": DEFAULT_MARGIN,
"brightness_threshold": DEFAULT_BRIGHTNESS_THRESHOLD,
"rubbing_min_area_ratio": DEFAULT_RUBBING_MIN_AREA_RATIO,
"text_min_area": DEFAULT_TEXT_MIN_AREA,
"text_area_ratio": DEFAULT_TEXT_AREA_RATIO,
"morphology_kernel_size": DEFAULT_MORPHOLOGY_KERNEL_SIZE,
"morphology_close_iterations": DEFAULT_MORPHOLOGY_CLOSE_ITERATIONS,
"morphology_open_iterations": DEFAULT_MORPHOLOGY_OPEN_ITERATIONS,
"rubbing_kernel_size": DEFAULT_RUBBING_KERNEL_SIZE,
"rubbing_close_iterations": DEFAULT_RUBBING_CLOSE_ITERATIONS,
"rubbing_open_iterations": DEFAULT_RUBBING_OPEN_ITERATIONS,
}
# ๊ธฐ๋ณธ ์„ค์ • ํŒŒ์ผ ๊ฒฝ๋กœ (config_path๊ฐ€ ์—†์„ ๋•Œ)
if config_path is None:
default_config_path = Path(__file__).parent / "config" / "preprocess_config.json"
if default_config_path.exists():
config_path = str(default_config_path)
if config_path and Path(config_path).exists():
try:
with open(config_path, 'r', encoding='utf-8') as f:
user_config = json.load(f)
# _description ํ•„๋“œ๋Š” ์ œ์™ธํ•˜๊ณ  ์—…๋ฐ์ดํŠธ
user_config_clean = {k: v for k, v in user_config.items() if not k.startswith('_')}
default_config.update(user_config_clean)
logger.info(f"[CONFIG] ์„ค์ • ํŒŒ์ผ ๋กœ๋“œ: {config_path}")
except Exception as e:
logger.warning(f"[CONFIG] ์„ค์ • ํŒŒ์ผ ๋กœ๋“œ ์‹คํŒจ: {e} - ๊ธฐ๋ณธ ์„ค์ • ์‚ฌ์šฉ")
return default_config
def _find_rubbing_bbox(self, gray_image: np.ndarray) -> Optional[Tuple[int, int, int, int]]:
"""
ํƒ๋ณธ ์˜์—ญ ๊ฒ€์ถœ (ํฐ ์–ด๋‘์šด ์‚ฌ๊ฐํ˜• ์ฐพ๊ธฐ)
Args:
gray_image (np.ndarray): ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ์ด๋ฏธ์ง€
Returns:
tuple: (x, y, w, h) ๋˜๋Š” None
"""
H_img, W_img = gray_image.shape
# Step 1: ์–ด๋‘์šด ์˜์—ญ ์ถ”์ถœ
_, dark_mask = cv2.threshold(gray_image, 127, 255, cv2.THRESH_BINARY_INV)
# Step 2: ๋ชจํด๋กœ์ง€ ์—ฐ์‚ฐ
kernel_rub = np.ones(self.config["rubbing_kernel_size"], np.uint8)
dark_mask = cv2.morphologyEx(
dark_mask, cv2.MORPH_CLOSE, kernel_rub,
iterations=self.config["rubbing_close_iterations"]
)
dark_mask = cv2.morphologyEx(
dark_mask, cv2.MORPH_OPEN, kernel_rub,
iterations=self.config["rubbing_open_iterations"]
)
# Step 3: ์ปจํˆฌ์–ด ๊ฒ€์ถœ
contours, _ = cv2.findContours(dark_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
return None
# Step 4: ๊ฐ€์žฅ ํฐ ์ปจํˆฌ์–ด
largest = max(contours, key=cv2.contourArea)
area = cv2.contourArea(largest)
# Step 5: ๋ฉด์  ๊ฒ€์ฆ
min_area = (H_img * W_img) * self.config["rubbing_min_area_ratio"]
if area < min_area:
return None
return cv2.boundingRect(largest)
def _find_text_bbox(self, gray_image: np.ndarray) -> Tuple[int, int, int, int]:
"""
ํ…์ŠคํŠธ ์˜์—ญ ๊ฒ€์ถœ
Args:
gray_image (np.ndarray): ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ์ด๋ฏธ์ง€
Returns:
tuple: (x, y, w, h)
"""
H_img, W_img = gray_image.shape
# Step 1: Otsu ์ด์ง„ํ™”
_, binary = cv2.threshold(
gray_image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU
)
# Step 2: ๋ชจํด๋กœ์ง€ ์—ฐ์‚ฐ
kernel_morph = np.ones(self.config["morphology_kernel_size"], np.uint8)
binary = cv2.morphologyEx(
binary, cv2.MORPH_CLOSE, kernel_morph,
iterations=self.config["morphology_close_iterations"]
)
binary = cv2.morphologyEx(
binary, cv2.MORPH_OPEN, kernel_morph,
iterations=self.config["morphology_open_iterations"]
)
# Step 3: ์ปจํˆฌ์–ด ๊ฒ€์ถœ
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Step 4: ์ตœ์†Œ ๋ฉด์  ์„ค์ •
min_area = max(
self.config["text_min_area"],
int((H_img * W_img) * self.config["text_area_ratio"])
)
# Step 5: ์œ ํšจํ•œ ์ปจํˆฌ์–ด ํ•„ํ„ฐ๋ง
valid_contours = [
cnt for cnt in contours
if cv2.contourArea(cv2.boundingRect(cnt)) >= min_area
]
# Step 6: ๊ฒฝ๊ณ„๋ฐ•์Šค ๊ณ„์‚ฐ
if valid_contours:
all_points = np.vstack(valid_contours)
return cv2.boundingRect(all_points)
else:
return (0, 0, W_img, H_img)
def _apply_margin(
self,
bbox: Tuple[int, int, int, int],
gray_image: np.ndarray,
margin_val: int
) -> Tuple[int, int, int, int]:
"""์—ฌ๋ฐฑ ์ถ”๊ฐ€"""
x, y, w, h = bbox
H_img, W_img = gray_image.shape
x_new = max(0, x - margin_val)
y_new = max(0, y - margin_val)
w_new = min(W_img - x_new, w + 2 * margin_val)
h_new = min(H_img - y_new, h + 2 * margin_val)
return (x_new, y_new, w_new, h_new)
def _ensure_bright_background(
self,
gray_cropped: np.ndarray
) -> Tuple[np.ndarray, Dict]:
"""
๋ฐ์€๋ฐฐ๊ฒฝ ๋ณด์žฅ (Swin์šฉ)
Returns:
tuple: (์ฒ˜๋ฆฌ๋œ ๊ทธ๋ ˆ์ด ์ด๋ฏธ์ง€, ์ฒ˜๋ฆฌ ์ •๋ณด)
"""
mean_brightness = np.mean(gray_cropped)
is_inverted = False
if mean_brightness < self.config["brightness_threshold"]:
gray_bright = cv2.bitwise_not(gray_cropped)
is_inverted = True
else:
gray_bright = gray_cropped.copy()
# ์žฌํ™•์ธ
final_brightness = np.mean(gray_bright)
if final_brightness < self.config["brightness_threshold"]:
gray_bright = cv2.bitwise_not(gray_bright)
is_inverted = not is_inverted
final_brightness = np.mean(gray_bright)
return gray_bright, {
"mean_brightness_before": float(mean_brightness),
"mean_brightness_after": float(final_brightness),
"is_inverted": is_inverted,
"is_bright_bg": final_brightness >= self.config["brightness_threshold"]
}
def _ensure_white_background(
self,
gray_cropped: np.ndarray
) -> Tuple[np.ndarray, Dict]:
"""
ํฐ๋ฐฐ๊ฒฝ ๋ณด์žฅ (OCR์šฉ)
Returns:
tuple: (์ฒ˜๋ฆฌ๋œ ์ด์ง„ ์ด๋ฏธ์ง€, ์ฒ˜๋ฆฌ ์ •๋ณด)
"""
# Step 1: ์ด์ง„ํ™”
_, binary = cv2.threshold(
gray_cropped, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
# Step 2: ํด๋ผ๋ฆฌํ‹ฐ ํŒ๋‹จ
mean_brightness = np.mean(binary)
# Step 3: ํ•„์š”์‹œ ๋ฐ˜์ „
if mean_brightness < self.config["brightness_threshold"]:
binary_final = cv2.bitwise_not(binary)
polarity = "inverted"
else:
binary_final = binary
polarity = "normal"
final_brightness = np.mean(binary_final)
return binary_final, {
"mean_brightness_before": float(mean_brightness),
"mean_brightness_after": float(final_brightness),
"polarity": polarity,
"is_white_bg": final_brightness > self.config["brightness_threshold"]
}
def preprocess_unified(
self,
input_image_path: str,
output_swin_path: str,
output_ocr_path: str,
margin: Optional[int] = None,
use_rubbing: bool = False
) -> Dict:
"""
ํ†ตํ•ฉ ์ „์ฒ˜๋ฆฌ (Swin Gray + OCR ๋™์‹œ ์ƒ์„ฑ)
ํ•œ ๋ฒˆ์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋กœ Swin Gray์™€ OCR์šฉ ์ด๋ฏธ์ง€๋ฅผ ๋ชจ๋‘ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
ํƒ๋ณธ ๋ฐ ํ…์ŠคํŠธ ์˜์—ญ ๊ฒ€์ถœ์€ 1ํšŒ๋งŒ ์ˆ˜ํ–‰๋˜์–ด ํšจ์œจ์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
Args:
input_image_path (str): ์ž…๋ ฅ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ
output_swin_path (str): Swin Gray ์ถœ๋ ฅ ๊ฒฝ๋กœ (JPG)
output_ocr_path (str): OCR ์ถœ๋ ฅ ๊ฒฝ๋กœ (PNG)
margin (int, optional): ํฌ๋กญ ์—ฌ๋ฐฑ (ํ”ฝ์…€)
use_rubbing (bool): ํƒ๋ณธ ๊ฒ€์ถœ ์—ฌ๋ถ€ (๊ธฐ๋ณธ: False)
Returns:
dict: ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ
์„ฑ๊ณต ์‹œ: {
"success": True,
"original_shape": (H, W, C),
"bbox": (x, y, w, h),
"region_type": "text" or "rubbing",
"region_detected": bool,
"swin": {
"output_path": str,
"output_shape": (H, W, 3),
"is_bright_bg": bool,
...
},
"ocr": {
"output_path": str,
"output_shape": (H, W),
"is_white_bg": bool,
...
}
}
์‹คํŒจ ์‹œ: {
"success": False,
"message": str
}
Processing Steps:
1. ์ด๋ฏธ์ง€ ๋กœ๋“œ
2. ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
3. ์˜์—ญ ๊ฒ€์ถœ (ํƒ๋ณธ ๋˜๋Š” ํ…์ŠคํŠธ, 1ํšŒ๋งŒ)
4. ํฌ๋กญ + ์—ฌ๋ฐฑ
5. Swin Gray ์ฒ˜๋ฆฌ (๋ฐ์€๋ฐฐ๊ฒฝ ๋ณด์žฅ)
6. OCR ์ฒ˜๋ฆฌ (์ด์ง„ํ™” + ํฐ๋ฐฐ๊ฒฝ ๋ณด์žฅ)
7. ๋™์‹œ ์ €์žฅ
Output:
- Swin: JPG 3์ฑ„๋„ (๋น„์ด์ง„ํ™” 256๋‹จ๊ณ„)
- OCR: PNG 1์ฑ„๋„ (์ด์ง„ํ™”)
Example:
>>> prep = UnifiedImagePreprocessor()
>>> result = prep.preprocess_unified(
... "input.jpg",
... "swin.jpg",
... "ocr.png"
... )
>>> if result["success"]:
... swin_output = result["swin"]["output_path"]
... ocr_output = result["ocr"]["output_path"]
"""
margin_val = margin or self.config["margin"]
try:
# ====================================================================
# Step 1: ์ด๋ฏธ์ง€ ๋กœ๋“œ
# ====================================================================
img_bgr = cv2.imread(str(input_image_path), cv2.IMREAD_COLOR)
if img_bgr is None:
raise ValueError(f"์ด๋ฏธ์ง€ ๋กœ๋“œ ์‹คํŒจ: {input_image_path}")
original_shape = img_bgr.shape
logger.info(f"[LOAD] ์ด๋ฏธ์ง€ ๋กœ๋“œ: {input_image_path} {original_shape}")
# ====================================================================
# Step 2: ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
# ====================================================================
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
# ====================================================================
# Step 3: ์˜์—ญ ๊ฒ€์ถœ (ํƒ๋ณธ ๋˜๋Š” ํ…์ŠคํŠธ)
# ====================================================================
if use_rubbing:
detected_bbox = self._find_rubbing_bbox(gray)
region_type = "rubbing"
logger.info("[DETECT] ํƒ๋ณธ ์˜์—ญ ๊ฒ€์ถœ ๋ชจ๋“œ")
else:
detected_bbox = None
region_type = "text"
logger.info("[DETECT] ํ…์ŠคํŠธ ์˜์—ญ ๊ฒ€์ถœ ๋ชจ๋“œ")
H_img, W_img = gray.shape
# ====================================================================
# Step 4: ํฌ๋กญ + ์—ฌ๋ฐฑ
# ====================================================================
if detected_bbox is not None:
bbox_final = self._apply_margin(detected_bbox, gray, margin_val)
logger.info(f"[DETECT] {region_type} ์˜์—ญ ๊ฒ€์ถœ: {bbox_final}")
else:
# ํƒ๋ณธ ๋ฏธ๊ฒ€์ถœ ๋˜๋Š” ํ…์ŠคํŠธ ๋ชจ๋“œ -> ํ…์ŠคํŠธ ๊ฒ€์ถœ
if use_rubbing:
bbox_final = (0, 0, W_img, H_img)
logger.warning("[DETECT] ํƒ๋ณธ ๋ฏธ๊ฒ€์ถœ - ์ „์ฒด ์ด๋ฏธ์ง€ ์‚ฌ์šฉ")
else:
bbox_text = self._find_text_bbox(gray)
bbox_final = self._apply_margin(bbox_text, gray, margin_val)
logger.info(f"[DETECT] ํ…์ŠคํŠธ ์˜์—ญ ๊ฒ€์ถœ: {bbox_final}")
x, y, w, h = bbox_final
gray_cropped = gray[y:y+h, x:x+w]
logger.info(f"[CROP] ํฌ๋กญ ์™„๋ฃŒ: {gray_cropped.shape}")
# ====================================================================
# Step 5: Swin Gray ์ฒ˜๋ฆฌ
# ====================================================================
gray_bright, info_swin = self._ensure_bright_background(gray_cropped)
swin_output_3ch = cv2.cvtColor(gray_bright, cv2.COLOR_GRAY2BGR)
# ====================================================================
# Step 6: OCR ์ฒ˜๋ฆฌ
# ====================================================================
binary_final, info_ocr = self._ensure_white_background(gray_cropped)
# ====================================================================
# Step 7: ๋™์‹œ ์ €์žฅ
# ====================================================================
output_swin_path_obj = Path(output_swin_path)
output_swin_path_obj.parent.mkdir(parents=True, exist_ok=True)
swin_success = cv2.imwrite(str(output_swin_path_obj), swin_output_3ch)
output_ocr_path_obj = Path(output_ocr_path)
output_ocr_path_obj.parent.mkdir(parents=True, exist_ok=True)
ocr_success = cv2.imwrite(str(output_ocr_path_obj), binary_final)
if not swin_success or not ocr_success:
raise ValueError("์ด๋ฏธ์ง€ ์ €์žฅ ์‹คํŒจ")
logger.info(f"[SAVE] Swin ์ €์žฅ: {output_swin_path_obj}")
logger.info(f"[SAVE] OCR ์ €์žฅ: {output_ocr_path_obj}")
# ====================================================================
# ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
# ====================================================================
return {
"success": True,
"version": "Unified Swin Gray + OCR (v1.0.0)",
"original_shape": original_shape,
"bbox": bbox_final,
"region_type": region_type,
"region_detected": detected_bbox is not None,
# Swin ๋ถ€๋ถ„
"swin": {
"output_path": str(output_swin_path_obj).replace("\\", "/"),
"output_shape": swin_output_3ch.shape,
"color_type": "Grayscale 3์ฑ„๋„ (B=G=R, ๋น„์ด์ง„ํ™” 256๋‹จ๊ณ„)",
"is_inverted": info_swin["is_inverted"],
"mean_brightness_before": info_swin["mean_brightness_before"],
"mean_brightness_after": info_swin["mean_brightness_after"],
"is_bright_bg": info_swin["is_bright_bg"]
},
# OCR ๋ถ€๋ถ„
"ocr": {
"output_path": str(output_ocr_path_obj).replace("\\", "/"),
"output_shape": binary_final.shape,
"polarity": info_ocr["polarity"],
"mean_brightness_before": info_ocr["mean_brightness_before"],
"mean_brightness_after": info_ocr["mean_brightness_after"],
"is_white_bg": info_ocr["is_white_bg"]
},
"message": "[DONE] ํ†ตํ•ฉ ์ „์ฒ˜๋ฆฌ ์™„๋ฃŒ (Swin + OCR)"
}
except Exception as e:
logger.error(f"[ERROR] ํ†ตํ•ฉ ์ „์ฒ˜๋ฆฌ ์‹คํŒจ: {e}")
return {
"success": False,
"message": str(e)
}
# ================================================================================
# Global Instance & Convenience Functions
# ================================================================================
_global_preprocessor = None
def get_preprocessor(config_path: Optional[str] = None) -> UnifiedImagePreprocessor:
"""์ „์—ญ ์ „์ฒ˜๋ฆฌ๊ธฐ ์ธ์Šคํ„ด์Šค ๋ฐ˜ํ™˜"""
global _global_preprocessor
if _global_preprocessor is None:
_global_preprocessor = UnifiedImagePreprocessor(config_path)
return _global_preprocessor
def preprocess_image_unified(
input_path: str,
output_swin_path: str,
output_ocr_path: str,
margin: Optional[int] = None,
use_rubbing: bool = False
) -> Dict:
"""
ํŽธ์˜ ํ•จ์ˆ˜: ํ†ตํ•ฉ ์ „์ฒ˜๋ฆฌ
Args:
input_path (str): ์ž…๋ ฅ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ
output_swin_path (str): Swin ์ถœ๋ ฅ ๊ฒฝ๋กœ
output_ocr_path (str): OCR ์ถœ๋ ฅ ๊ฒฝ๋กœ
margin (int, optional): ์—ฌ๋ฐฑ
use_rubbing (bool): ํƒ๋ณธ ๋ชจ๋“œ
Returns:
dict: ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ
"""
prep = get_preprocessor()
return prep.preprocess_unified(
input_path,
output_swin_path,
output_ocr_path,
margin,
use_rubbing
)
# ================================================================================
# Usage Example
# ================================================================================
if __name__ == "__main__":
"""
ํ…Œ์ŠคํŠธ ์˜ˆ์‹œ
"""
logger.info("=" * 80)
logger.info("[TEST] Unified Image Preprocessor v1.0.0 - ํ…Œ์ŠคํŠธ ์‹œ์ž‘")
logger.info("=" * 80)
try:
prep = UnifiedImagePreprocessor()
result = prep.preprocess_unified(
"test_input.jpg",
"test_swin.jpg",
"test_ocr.png"
)
if result["success"]:
logger.info("[TEST] ํ†ตํ•ฉ ์ „์ฒ˜๋ฆฌ ์„ฑ๊ณต!")
logger.info(f"[TEST] Swin: {result['swin']['output_path']}")
logger.info(f"[TEST] OCR: {result['ocr']['output_path']}")
logger.info(f"[TEST] Swin ๋ฐ์€๋ฐฐ๊ฒฝ: {'Yes' if result['swin']['is_bright_bg'] else 'No'}")
logger.info(f"[TEST] OCR ํฐ๋ฐฐ๊ฒฝ: {'Yes' if result['ocr']['is_white_bg'] else 'No'}")
else:
logger.error(f"[TEST] ์‹คํŒจ: {result['message']}")
except Exception as e:
logger.error(f"[TEST] ์˜ˆ์™ธ: {e}")
logger.info("=" * 80)