Spaces:
Running
Running
File size: 5,065 Bytes
6f3fe10 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | """Diagnose per-row rejection in mask_only edge detection.
Traces rejection reasons per ROI row by monkey-patching the edge extractor.
"""
import sys
from pathlib import Path
import cv2
import numpy as np
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from measure_finger import measure_finger, load_image
from src.edge_refinement import extract_ring_zone_roi
from src.edge_refinement_constants import MIN_FINGER_WIDTH_CM, MAX_FINGER_WIDTH_CM
def diag(image_path: str, finger: str) -> None:
snapshot = {}
orig = extract_ring_zone_roi
def spy(*args, **kwargs):
roi_data = orig(*args, **kwargs)
snapshot["roi_data"] = roi_data
return roi_data
import src.edge_refinement as er
er.extract_ring_zone_roi = spy
image = load_image(image_path)
result = measure_finger(
image=image,
finger_index=finger,
card_method="sam",
edge_method="mask",
)
print(f"fail_reason: {result.get('fail_reason')}")
print(f"finger_outer_diameter_cm: {result.get('finger_outer_diameter_cm')}")
roi = snapshot.get("roi_data")
if roi is None:
print("ERROR: ROI not captured")
return
roi_mask = roi["roi_mask"]
axis_center = roi["axis_center_in_roi"]
axis_direction = roi["axis_direction_in_roi"]
h, w = roi_mask.shape[:2]
print(f"\nROI: {w}x{h} axis_center={axis_center} axis_direction={axis_direction}")
px_per_cm = result.get("scale_px_per_cm", 0) or 0
min_w = MIN_FINGER_WIDTH_CM * px_per_cm
max_w = MAX_FINGER_WIDTH_CM * px_per_cm
print(f"scale={px_per_cm:.2f} px/cm width range: [{min_w:.1f}, {max_w:.1f}] px\n")
reject_counts = {"no_mask": 0, "axis_off_mask": 0, "bleed_L": 0, "bleed_R": 0,
"bleed_both": 0, "width_oor": 0, "valid": 0}
sample_widths = []
mask_bool = roi_mask > 0
max_col = w - 1
for row in range(h):
row_mask = mask_bool[row, :]
if abs(axis_direction[1]) < 1e-6:
axis_x = axis_center[0]
else:
t = (row - axis_center[1]) / axis_direction[1]
axis_x = axis_center[0] + t * axis_direction[0]
axis_col = max(0, min(max_col, int(round(axis_x))))
if not np.any(row_mask):
reject_counts["no_mask"] += 1
continue
if not row_mask[axis_col]:
reject_counts["axis_off_mask"] += 1
continue
left_b = axis_col
while left_b > 0 and row_mask[left_b - 1]:
left_b -= 1
right_b = axis_col
while right_b < max_col and row_mask[right_b + 1]:
right_b += 1
bleed_L = left_b == 0
bleed_R = right_b == max_col
if bleed_L and bleed_R:
reject_counts["bleed_both"] += 1
continue
if bleed_L:
reject_counts["bleed_L"] += 1
continue
if bleed_R:
reject_counts["bleed_R"] += 1
continue
width = right_b - left_b
if not (min_w <= width <= max_w):
reject_counts["width_oor"] += 1
continue
reject_counts["valid"] += 1
sample_widths.append(width)
total = sum(reject_counts.values())
print(f"Totals over {total} rows:")
for k, v in reject_counts.items():
pct = 100.0 * v / total if total else 0
print(f" {k:<16}: {v:>5} ({pct:5.1f}%)")
if sample_widths:
print(f"\nValid widths: n={len(sample_widths)}, min={min(sample_widths)}, "
f"max={max(sample_widths)}, median={sorted(sample_widths)[len(sample_widths)//2]}")
# Dump row profile as CSV
print("\nrow,n_mask_runs,run_sizes,axis_col,axis_on_mask,left_b,right_b")
for row in range(0, h, max(1, h // 40)):
row_mask = mask_bool[row, :]
# Count runs
runs = []
i = 0
while i < w:
if row_mask[i]:
start = i
while i < w and row_mask[i]:
i += 1
runs.append((start, i - start))
else:
i += 1
if abs(axis_direction[1]) < 1e-6:
axis_x = axis_center[0]
else:
t = (row - axis_center[1]) / axis_direction[1]
axis_x = axis_center[0] + t * axis_direction[0]
axis_col = max(0, min(max_col, int(round(axis_x))))
axis_on = row_mask[axis_col] if axis_col < w else False
if axis_on:
left_b = axis_col
while left_b > 0 and row_mask[left_b - 1]:
left_b -= 1
right_b = axis_col
while right_b < max_col and row_mask[right_b + 1]:
right_b += 1
else:
left_b = right_b = -1
run_str = ";".join(f"{s}:{l}" for s, l in runs)
print(f"{row},{len(runs)},{run_str},{axis_col},{axis_on},{left_b},{right_b}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("usage: python script/diag_mask_rows.py <image> <finger>")
sys.exit(1)
diag(sys.argv[1], sys.argv[2])
|