summaryrefslogtreecommitdiff
path: root/scripts/check-p40-night-boat-contrast.py
blob: c6375a9e7ee172b05fa22fb2fada7703379a9d3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env python3
"""Fail if the p40-night frame's Wu Peng boat is not dark enough against its glow.

The `LV_P40_SHOT=1` night hook centers tile (234,50) in a 1280x720 viewport; the
two-tile boat occupies tiles (234,50)-(235,50), so its screen box and the ring of
lantern glow around it are pure functions of those constants.  The regression
budget is: mean luma of the boat box must be at least 40 grey below the mean luma
of a 24px ring around it.

Usage:
    python3 scripts/check-p40-night-boat-contrast.py [p40-night.png]

Defaults to docs/evidence/p40-night.png.  Exit 0 clean, 1 below budget, 2 on
usage/read error.
"""

import sys
from pathlib import Path

import numpy as np
from PIL import Image

MIN_CONTRAST = 40.0
TILE = 32
VIEW_W, VIEW_H = 1280, 720
BOAT_TX, BOAT_TY = 234, 50
RING = 24


def measure(path):
    rgba = np.asarray(Image.open(path).convert("RGB"), dtype=np.float64)
    luma = 0.299 * rgba[:, :, 0] + 0.587 * rgba[:, :, 1] + 0.114 * rgba[:, :, 2]
    cam_x = BOAT_TX * TILE + TILE // 2 - VIEW_W // 2
    cam_y = BOAT_TY * TILE + TILE // 2 - VIEW_H // 2
    x0 = BOAT_TX * TILE - cam_x
    y0 = BOAT_TY * TILE - cam_y
    x1 = x0 + 2 * TILE
    y1 = y0 + TILE
    boat = float(luma[y0:y1, x0:x1].mean())
    patch = luma[y0 - RING:y1 + RING, x0 - RING:x1 + RING].copy()
    patch[RING:RING + TILE, RING:RING + 2 * TILE] = np.nan
    ring = float(np.nanmean(patch))
    return boat, ring


def main(argv):
    target = Path(argv[1]) if len(argv) > 1 else Path("docs/evidence/p40-night.png")
    if not target.is_file():
        sys.stderr.write(f"missing frame: {target}\n")
        return 2
    boat, ring = measure(target)
    contrast = ring - boat
    status = "OK" if contrast >= MIN_CONTRAST else "FAIL"
    print(f"p40-night boat={boat:.1f} ring={ring:.1f} contrast={contrast:.1f} (>= {MIN_CONTRAST:.0f}) {status}")
    return 0 if contrast >= MIN_CONTRAST else 1


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))