report-detect/scripts/optimize_seal_params.py

138 lines
6.7 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Optimize Seal Text Recognition (v15 - Refined Search)
Systematically searches for unwarping parameters to match target text.
"""
import sys
import cv2
import numpy as np
import os
import difflib
import json
import time
# Target text: "威凯检测技术有限公司" using unicode escapes for safety on Windows
TARGET_TEXT = "\u5a01\u51ef\u68c0\u6d4b\u6280\u672f\u6709\u9650\u516c\u53f8"
MIN_SIMILARITY = 0.70
def similarity(s1, s2):
if not s1 or not s2: return 0.0
# Strip any extra whitespace
s1 = s1.strip().replace(" ", "")
s2 = s2.strip().replace(" ", "")
return difflib.SequenceMatcher(None, s1, s2).ratio()
def optimize_params(image_path, output_dir):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Loading {image_path}...", file=sys.stderr)
img = cv2.imread(image_path)
if img is None:
print(f"Error: Could not load {image_path}", file=sys.stderr)
return
h, w = img.shape[:2]
# Pre-processing (Hough circle only)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
saturation = hsv[:,:,1]
mask = cv2.bitwise_or(cv2.inRange(hsv, (0, 40, 40), (10, 255, 255)),
cv2.inRange(hsv, (160, 40, 40), (180, 255, 255)))
red_intensity = cv2.bitwise_and(saturation, saturation, mask=mask)
# Initialize OCR
try:
os.environ["DISABLE_MODEL_SOURCE_CHECK"] = "True"
from paddleocr import PaddleOCR
import logging
logging.getLogger('ppocr').setLevel(logging.ERROR)
ocr_model = PaddleOCR(use_angle_cls=False, lang='ch', show_log=False)
except Exception as e:
# Some versions don't support show_log in constructor
from paddleocr import PaddleOCR
ocr_model = PaddleOCR(use_angle_cls=False, lang='ch')
# Centers
centers = [((w // 2, h // 2), "img_center")]
blur = cv2.medianBlur(red_intensity, 5)
circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1, h / 8,
param1=100, param2=30,
minRadius=int(h / 4), maxRadius=int(h / 1.5))
radius_hough = min(w, h) // 2
if circles is not None:
best_c = circles[0][0]
centers.append(((int(best_c[0]), int(best_c[1])), f"hough_r{int(best_c[2])}"))
radius_hough = int(best_c[2])
# Focussed search
start_angles = np.arange(0, 360, 15)
factors = [1.0, 1.5, 2.0, 2.5, 3.0]
r_scales = [0.95, 1.0, 1.05]
flips = [("none", None), ("vh", -1)]
modes = [("bgr", img)]
best_sim = 0.0
combination_count = 0
total_total = len(centers) * len(start_angles) * len(r_scales) * len(factors) * 2 * len(flips) * len(modes)
print(f"Starting focused search across {total_total} combinations...", file=sys.stderr)
log_path = os.path.join(output_dir, "v5_optimization_log.txt")
debug_candidates_dir = os.path.join(output_dir, "debug_candidates")
if not os.path.exists(debug_candidates_dir): os.makedirs(debug_candidates_dir)
with open(log_path, "w", encoding="utf-8") as f_log:
for m_name, work_img in modes:
for (cx, cy), c_name in centers:
radius_base = radius_hough if "hough" in c_name else min(cx, cy, w-cx, h-cy)
for angle in start_angles:
M_rot = cv2.getRotationMatrix2D((cx, cy), angle, 1.0)
rotated_img = cv2.warpAffine(work_img, M_rot, (w, h), borderValue=(255, 255, 255))
for r_scale in r_scales:
rr = int(radius_base * r_scale)
for factor in factors:
out_w = int(rr * 2 * np.pi * factor)
out_h = rr
unwarped = cv2.warpPolar(rotated_img, (out_w, out_h), (cx, cy), rr, cv2.WARP_POLAR_LINEAR)
strips = [
("outer", unwarped[int(out_h * 0.7):out_h, :]),
("inner", unwarped[int(out_h * 0.4):int(out_h * 0.7), :])
]
for s_name, strip in strips:
for flip_label, flip_code in flips:
final = strip
if flip_code is not None: final = cv2.flip(strip, flip_code)
combination_count += 1
param_str = f"{m_name}_{c_name}_a{angle}_r{r_scale}_f{factor}_{s_name}_{flip_label}"
try:
res = ocr_model.ocr(final)
recognized = ""
if res and len(res) > 0:
if isinstance(res[0], dict) and 'rec_texts' in res[0]:
for text in res[0]['rec_texts']: recognized += str(text)
elif isinstance(res[0], list):
for line in res[0]:
if line and len(line) > 1: recognized += str(line[1][0])
sim = similarity(recognized, TARGET_TEXT)
if recognized:
f_log.write(f"Sim: {sim:.4f} | lenR:{len(recognized)} | {param_str} | {recognized}\n")
f_log.flush()
if sim > 0.1:
print(f" FOUND: {sim:.4f} | R:'{recognized}' | {param_str}", flush=True)
if sim > 0.4 or combination_count % 100 == 0:
cv2.imwrite(os.path.join(debug_candidates_dir, f"{param_str}_s{sim:.2f}.png"), final)
if sim > best_sim:
best_sim = sim
cv2.imwrite(os.path.join(output_dir, f"best_sim_{sim:.2f}.png"), final)
if sim >= MIN_SIMILARITY:
print(f"WINNER: {param_str} -> {recognized}", file=sys.stderr)
return
except: pass
if combination_count % 50 == 0:
print(f"Progress: {combination_count}/{total_total}...", flush=True)
print(f"Done. Best Sim: {best_sim:.2f}", file=sys.stderr)
if __name__ == "__main__":
if len(sys.argv) < 3: sys.exit(1)
optimize_params(sys.argv[1], sys.argv[2])