#!/usr/bin/env python3 """Compare TrueHDR RGB channel values, optionally normalizing Windows FP16 scRGB.""" import argparse import hashlib import json import math from pathlib import Path import struct # Linear Rec.709/scRGB -> Rec.2020, D65. scRGB 1.0 represents 80 cd/m2. REC709_TO_2020 = ((0.627403896, 0.329283038, 0.043313066), (0.069097289, 0.919540395, 0.011362316), (0.016391439, 0.088013308, 0.895595253)) def pq_encode(nits): value = (max(0.0, min(10000.0, nits)) / 10000.0) ** (2610 / 16384) return ((3424 / 4096 + (2413 / 128) * value) / (1 + (2392 / 128) * value)) ** (2523 / 32) def pixels(data, fmt): if fmt == 'rgb10a2': for (word,) in struct.iter_unpack('> shift) & 1023 for shift in (0, 10, 20)) else: for r, g, b, _ in struct.iter_unpack(' max_code_error return dict(passed=failed == 0, pixels=count // 3, channels=count, channels_outside_tolerance=failed, max_code_error=maximum, mean_absolute_code_error=errors/count, rms_code_error=(squared/count)**0.5, allowed_max_code_error=max_code_error, reference_format=reference_format, candidate_sha256=hashlib.sha256(candidate).hexdigest(), reference_sha256=hashlib.sha256(reference).hexdigest(), scope='RGB values only; input identity, geometry, parameters, versions, and frame history must also match') def main(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('candidate', type=Path) p.add_argument('reference', type=Path) p.add_argument('--reference-format', choices=('rgb10a2', 'scrgb-f16'), default='rgb10a2') p.add_argument('--max-code-error', type=float, default=0, help='Maximum absolute 10-bit RGB code error; default requires exact RGB10 equality') args = p.parse_args() result = compare(args.candidate.read_bytes(), args.reference.read_bytes(), args.reference_format, args.max_code_error) print(json.dumps(result, indent=2)) return 0 if result['passed'] else 1 if __name__ == '__main__': raise SystemExit(main())