from typing import Tuple import cv2 import numpy as np from models import FingerQualityResult class Visualizer: @staticmethod def draw_axis(img, center, vec, length, color, thickness=2): x0, y0 = center x1 = int(x0 + length * vec[0]) y1 = int(y0 + length * vec[1]) cv2.arrowedLine(img, (x0, y0), (x1, y1), color, thickness, tipLength=0.2) @staticmethod def draw_debug( img: np.ndarray, contour: np.ndarray, bbox: Tuple[int, int, int, int], angle_deg: float, result: FingerQualityResult ) -> np.ndarray: out = img.copy() x, y, w_box, h_box = bbox cv2.rectangle(out, (x, y), (x + w_box, y + h_box), (0, 255, 0), 2) cv2.drawContours(out, [contour], -1, (255, 0, 0), 2) pts = contour.reshape(-1, 2).astype(np.float64) mean, eigenvectors, _ = cv2.PCACompute2(pts, mean=np.empty(0)) center = (int(mean[0, 0]), int(mean[0, 1])) main_vec = eigenvectors[0] Visualizer.draw_axis(out, center, main_vec, length=80, color=(0, 0, 255), thickness=2) text_lines = [ f"Blur: {result.blur_score:.1f} ({'OK' if result.blur_pass else 'BAD'})", f"Illum: {result.illumination_score:.1f} ({'OK' if result.illumination_pass else 'BAD'})", f"Coverage: {result.coverage_ratio*100:.1f}% ({'OK' if result.coverage_pass else 'BAD'})", f"Angle: {angle_deg:.1f} deg ({'OK' if result.orientation_pass else 'BAD'})", f"Quality: {result.quality_score:.2f} ({'PASS' if result.overall_pass else 'FAIL'})", ] y0 = 25 for line in text_lines: color = (0, 255, 0) if ("OK" in line or "PASS" in line) else (0, 0, 255) cv2.putText(out, line, (10, y0), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, cv2.LINE_AA) y0 += 22 return out