#!/usr/bin/env python3 """Does the running game draw the full-res `8AX` or the 640x360 `ptbase` upscaled? Both carry the SAME artwork at two resolutions, so no pixel comparison of the backgrounds can separate them. What CAN: the detail 8AX has and an upscale does not. Compute `8AX - upscale(ptbase)` -- the 8AX-only detail -- and ask whether the live capture contains it. Both candidates are first mapped into the capture's tone domain with the measured gamma (docs/re/structures/ui-render-tone-curve.md); without that, the capture's residual is dominated by the tone difference and the test is blind. CONTROLS are the point: the same correlation with the 8AX residual shifted 7 px and flipped. Those preserve the spatial correlation structure while destroying the alignment, so they are what "no signal" looks like here. """ import sys import numpy as np from PIL import Image CASES = [ ("main menu", 1.491, "a715f485_8AX_1280x720.png", "a715f485_ptbase.t32_640x360.png", "docs/re/captures/title-builds/live-main-menu.png"), ("title", 1.338, "a60fcb85_8AX_1280x720.png", "a60fcb85_ptbase2.t32_640x360.png", "docs/re/captures/title-builds/live-title-press-a.png"), ] TEX = sys.argv[1] if len(sys.argv) > 1 else "/tmp/tex" Y0, Y1, X0, X1 = 40, 300, 20, 600 # background, away from menu text def main(): for label, gamma, ax_png, pb_png, cap_png in CASES: ax = np.asarray(Image.open(f"{TEX}/{ax_png}").convert("L")).astype(float) pb = np.asarray(Image.open(f"{TEX}/{pb_png}").convert("L") .resize((1280, 720), Image.BILINEAR)).astype(float) cap = np.asarray(Image.open(cap_png).convert("L")).astype(float) h, w = cap.shape ax, pb = ax[:h, :w], pb[:h, :w] g = lambda a: 255 * np.power(np.clip(a, 0, 255) / 255.0, gamma) axg, pbg = g(ax), g(pb) E = (axg - pbg)[Y0:Y1, X0:X1] # detail only 8AX has C = (cap - pbg)[Y0:Y1, X0:X1] # how the capture departs from the upscale cc = lambda a, b: float(np.corrcoef(a.ravel(), b.ravel())[0, 1]) ceiling = E.std() / C.std() r = cc(C, E) print(f"{label}: corr {r:+.4f} controls {cc(C[:, :-7], E[:, 7:]):+.4f} (shift) " f"{cc(C, E[::-1]):+.4f} (flip) ceiling {ceiling:.3f} -> {100*r/ceiling:.0f}% of it") if __name__ == "__main__": main()