import cv2 import numpy as np # Simulate a thin "1" and "2" img_12 = np.zeros((60, 100), dtype=np.uint8) img_12[10:50, 40:45] = 255 # The "1" img_12[10:15, 60:80] = 255 # Top of "2" img_12[15:45, 75:80] = 255 # Right of "2" img_12[45:50, 60:80] = 255 # Bottom of "2" # Simulate a thin "3" and "7" img_37 = np.zeros((60, 100), dtype=np.uint8) img_37[10:15, 30:50] = 255 # Top of "3" img_37[25:30, 30:50] = 255 # Mid of "3" img_37[45:50, 30:50] = 255 # Bot of "3" img_37[10:15, 60:80] = 255 # Top of "7" img_37[15:50, 75:80] = 255 # Right of "7" # Simulate the SAME "12" but shifted by 2 pixels (due to video wobble) img_12_shifted = np.zeros((60, 100), dtype=np.uint8) img_12_shifted[12:52, 42:47] = 255 img_12_shifted[12:17, 62:82] = 255 img_12_shifted[17:47, 77:82] = 255 img_12_shifted[47:52, 62:82] = 255 def compute_iou(s1, s2): intersection = np.logical_and(s1 > 0, s2 > 0) union = np.logical_or(s1 > 0, s2 > 0) return np.count_nonzero(intersection) / max(1, np.count_nonzero(union)) def robust_match(s1, s2): # Dilate by 3x3 to make lines thick enough to overlap even if shifted by 2px kernel = np.ones((5, 5), np.uint8) d1 = cv2.dilate(s1, kernel, iterations=1) d2 = cv2.dilate(s2, kernel, iterations=1) # Try multiple subpixel shifts manually and take the best IoU best_iou = 0 for dy in [-2, 0, 2]: for dx in [-2, 0, 2]: M = np.float32([[1, 0, dx], [0, 1, dy]]) shifted_d2 = cv2.warpAffine(d2, M, (s2.shape[1], s2.shape[0])) iou = compute_iou(d1, shifted_d2) if iou > best_iou: best_iou = iou return best_iou print("IoU (12 vs 37):", robust_match(img_12, img_37)) print("IoU (12 vs 12_shifted):", robust_match(img_12, img_12_shifted)) # Let's see what TM_CCOEFF_NORMED would have done: res = cv2.matchTemplate(img_37, img_12[5:-5, 5:-5], cv2.TM_CCOEFF_NORMED) _, max_val_diff, _, _ = cv2.minMaxLoc(res) res2 = cv2.matchTemplate(img_12_shifted, img_12[5:-5, 5:-5], cv2.TM_CCOEFF_NORMED) _, max_val_same, _, _ = cv2.minMaxLoc(res2) print("\nTM_CCOEFF_NORMED (12 vs 37):", max_val_diff) print("TM_CCOEFF_NORMED (12 vs 12_shifted):", max_val_same)