Edge-Shape (Chamfer) Template Matching
Intensity correlation (visual_match) is dragged down when the same control is rendered
with a different fill, gradient, theme or anti-aliasing, and ORB feature matching
(feature_match) needs corner texture that flat-design glyphs — a hamburger menu, a plain
chevron — simply do not have. edge_match locates a template by its edge shape instead:
it runs Canny on both images, builds a distance transform of the scene edges, and slides the
template’s edges over it, scoring each position by the mean distance from a template edge to
the nearest scene edge (Chamfer matching). A perfect alignment costs ~0 regardless of how the
shape is filled or shaded.
It reuses visual_match’s gray loaders / resize / NMS / Match and edge_lines’s Canny
default, so no matching or geometry code is duplicated. The haystack is injectable
(ndarray / path / PIL); the search is unit-testable on synthetic arrays. Imports no
PySide6.
Headless API
from je_auto_control import edge_match, edge_match_all, chamfer_distance
# find a flat icon regardless of its fill colour / theme
hit = edge_match("chevron.png", min_score=0.7)
if hit:
click(*hit.center)
for m in edge_match_all("divider_handle.png", min_score=0.8):
print(m.center, m.score)
print(chamfer_distance("logo_outline.png")) # 0 = edges coincide
edge_match returns the best Match (score = 1 / (1 + mean edge distance), so 1.0 is
a perfect outline match) over the requested scales, or None. edge_match_all returns
every match at or above min_score with overlaps removed by NMS. chamfer_distance returns
the mean edge-to-edge distance at the best alignment (0 = the outlines coincide).
Executor commands
AC_edge_match (template / min_score / scales / region →
{found, match}) and AC_edge_match_all (adds max_results / nms_iou →
{count, matches}). They are exposed as the MCP tools ac_edge_match /
ac_edge_match_all (read-only) and as the Script Builder commands Match Template (edge
shape) / Match Template All (edge shape) under Image.