Perceptual (YIQ) Image Diff with Anti-Alias Suppression

visual_regression.image_difference counts raw per-channel max-delta pixels and ssim_compare gives a global structural score. Neither uses a perceptual colour metric, and neither ignores anti-aliased edges — the #1 source of false-positive visual-diff failures across DPI and font-hinting. perceptual_diff compares pixels in YIQ space (the pixelmatch colour metric, far closer to human perception than RGB) and, by default, removes the thin one-pixel edge differences that anti-aliasing produces (a morphological open), so only solid changed regions count.

Runs on an injectable image pair (ndarray / path / PIL), so it is headless-testable on synthetic arrays. OpenCV + NumPy come in via je_open_cv; reuses the shared connected-component helper and RGB loader. Imports no PySide6.

Headless API

from je_auto_control import perceptual_diff, assert_perceptual

result = perceptual_diff("actual.png", "golden.png", threshold=0.1)
print(result.diff_pixels, result.diff_ratio, result.regions)

# Gate a visual-regression test (raises if the ratio is exceeded).
assert_perceptual("actual.png", "golden.png", max_diff_ratio=0.01)

perceptual_diff returns a PerceptualDiffResult (diff_pixels, total_pixels, diff_ratio, and the regions boxes of the changed clusters). threshold (0..1) is the pixelmatch sensitivity. include_aa=True keeps the thin edge differences instead of suppressing them. assert_perceptual raises AutoControlActionException when diff_ratio exceeds max_diff_ratio. Images of different sizes raise ValueError.

Executor command

AC_perceptual_diff (actual / expected / threshold / include_aa / max_diff_ratio{diff_pixels, total_pixels, diff_ratio, regions}; raises when max_diff_ratio is given and exceeded). It is exposed as the MCP tool ac_perceptual_diff and as a Script Builder command under Image.