Snapshot: fog implementation + fidelity tooling baseline (pre bilinear-clamp fix)
Per-vertex GS fog end-to-end (gs_stub emit incl. persp_emit5, gs_prim_list_feeder XYZ2->XYZF2 on PRIM.FGE, gs_make_sh3_scheduler_fixture.py F/FGE packing), new fog TBs, fidelity attribution tooling. Functional baseline before removing the dead bilinear lerp8 clamps (Codex: 161-node comb loop -> -0.042ns setup fail). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Preview the exact Ch438 source-space 3x3 binomial scanout."""
|
||||
import math
|
||||
import sys
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def binom3(a, b, c):
|
||||
return tuple((x + 2 * y + z + 2) // 4 for x, y, z in zip(a, b, c))
|
||||
|
||||
|
||||
def score(a, b):
|
||||
ae = 0
|
||||
se = 0
|
||||
n = a.width * a.height * 3
|
||||
for pa, pb in zip(a.getdata(), b.getdata()):
|
||||
for ca, cb in zip(pa, pb):
|
||||
d = int(ca) - int(cb)
|
||||
ae += abs(d)
|
||||
se += d * d
|
||||
return ae / n, math.sqrt(se / n)
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(argv) != 4:
|
||||
raise SystemExit(
|
||||
"usage: preview_scanout_ch438.py <fb.png> <reference.png> <out.png>"
|
||||
)
|
||||
|
||||
src = Image.open(argv[1]).convert("RGB")
|
||||
if src.size != (640, 480):
|
||||
raise SystemExit(f"expected 640x480 framebuffer, got {src.size}")
|
||||
ref = Image.open(argv[2]).convert("RGB").resize(
|
||||
(640, 480), Image.Resampling.BILINEAR
|
||||
)
|
||||
|
||||
# RTL rounds after each separable pass, so mirror that order exactly.
|
||||
horizontal = Image.new("RGB", src.size)
|
||||
hp = horizontal.load()
|
||||
for y in range(480):
|
||||
for x in range(512):
|
||||
xm1 = max(0, x - 1)
|
||||
xp1 = min(511, x + 1)
|
||||
hp[x, y] = binom3(src.getpixel((xm1, y)),
|
||||
src.getpixel((x, y)),
|
||||
src.getpixel((xp1, y)))
|
||||
|
||||
filtered = Image.new("RGB", src.size)
|
||||
fp = filtered.load()
|
||||
for y in range(32, 480):
|
||||
ym1 = max(32, y - 1)
|
||||
yp1 = min(479, y + 1)
|
||||
for x in range(512):
|
||||
fp[x, y] = binom3(horizontal.getpixel((x, ym1)),
|
||||
horizontal.getpixel((x, y)),
|
||||
horizontal.getpixel((x, yp1)))
|
||||
|
||||
out = Image.new("RGB", (640, 480))
|
||||
px = out.load()
|
||||
for y in range(480):
|
||||
sy = 32 + (y * 14) // 15
|
||||
for x in range(640):
|
||||
sx = (x * 4) // 5
|
||||
px[x, y] = filtered.getpixel((sx, sy))
|
||||
|
||||
out.save(argv[3])
|
||||
mae, rmse = score(out, ref)
|
||||
print(f"binomial3x3 MAE={mae:.4f} RMSE={rmse:.4f}")
|
||||
print(f"wrote {argv[3]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
Reference in New Issue
Block a user