from PIL import Image import numpy as np from numpy.lib.stride_tricks import sliding_window_view def load_bw(path): # Black=1, White=0 im = Image.open(path).convert("L") bw = (np.array(im) < 128).astype(np.uint8) return bw def finder_template(box_size=2): # 7x7 finder: outer black, inner white (5x5), center black (3x3) F = np.array([ [1,1,1,1,1,1,1], [1,0,0,0,0,0,1], [1,0,1,1,1,0,1], [1,0,1,1,1,0,1], [1,0,1,1,1,0,1], [1,0,0,0,0,0,1], [1,1,1,1,1,1,1], ], dtype=np.uint8) # upscale to pixels return np.kron(F, np.ones((box_size, box_size), dtype=np.uint8)) def count_finders(img_path, box_size=2, max_mismatch=0): bw = load_bw(img_path) T = finder_template(box_size=box_size) h, w = T.shape # slide 14x14 window over the image win = sliding_window_view(bw, (h, w)) # Hamming distance to template per window mismatches = (win ^ T).sum(axis=(-2, -1)) hits = mismatches <= max_mismatch # Optional: suppress overlapping duplicates by non-maximum suppression on exact matches ys, xs = np.where(hits) # Convert to center coordinates centers = [(int(y + h/2), int(x + w/2)) for y, x in zip(ys, xs)] # Greedy dedup within ~half a finder width deduped = [] r = max(2, h//2) for cy, cx in centers: if all((abs(cy - y) > r) or (abs(cx - x) > r) for y, x in deduped): deduped.append((cy, cx)) return len(deduped), deduped if __name__ == "__main__": n, centers = count_finders("./scrambled_test_qr_code.png", box_size=2, max_mismatch=0) print("Finder count:", n, "centers:", centers, "Exactly three?", n == 3)