|
|
|
|
|
|
|
|
""" |
|
|
================================================================================ |
|
|
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.basicConfig( |
|
|
level=logging.INFO, |
|
|
format='%(asctime)s - [%(levelname)s] %(message)s' |
|
|
) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, |
|
|
} |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
_, dark_mask = cv2.threshold(gray_image, 127, 255, cv2.THRESH_BINARY_INV) |
|
|
|
|
|
|
|
|
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"] |
|
|
) |
|
|
|
|
|
|
|
|
contours, _ = cv2.findContours(dark_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
|
|
|
|
|
if not contours: |
|
|
return None |
|
|
|
|
|
|
|
|
largest = max(contours, key=cv2.contourArea) |
|
|
area = cv2.contourArea(largest) |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
_, binary = cv2.threshold( |
|
|
gray_image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU |
|
|
) |
|
|
|
|
|
|
|
|
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"] |
|
|
) |
|
|
|
|
|
|
|
|
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
|
|
|
|
|
|
|
|
min_area = max( |
|
|
self.config["text_min_area"], |
|
|
int((H_img * W_img) * self.config["text_area_ratio"]) |
|
|
) |
|
|
|
|
|
|
|
|
valid_contours = [ |
|
|
cnt for cnt in contours |
|
|
if cv2.contourArea(cv2.boundingRect(cnt)) >= min_area |
|
|
] |
|
|
|
|
|
|
|
|
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: (์ฒ๋ฆฌ๋ ์ด์ง ์ด๋ฏธ์ง, ์ฒ๋ฆฌ ์ ๋ณด) |
|
|
""" |
|
|
|
|
|
_, binary = cv2.threshold( |
|
|
gray_cropped, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU |
|
|
) |
|
|
|
|
|
|
|
|
mean_brightness = np.mean(binary) |
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gray_bright, info_swin = self._ensure_bright_background(gray_cropped) |
|
|
swin_output_3ch = cv2.cvtColor(gray_bright, cv2.COLOR_GRAY2BGR) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
binary_final, info_ocr = self._ensure_white_background(gray_cropped) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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": { |
|
|
"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": { |
|
|
"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_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 |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|