Session 2: hybrid VQ codec, two quality profiles, three corrections
Answers session 1's critical-path question. Flat 4x4 VQ at k=256 was prototyped and REJECTED by eye: Dirk's face disintegrates and ink outlines break into 4-pixel stair-steps. The 256-colour palettised frame is excellent, so the palette was never the problem -- block VQ was. Replaced it with a Cinepak-style hybrid: each 4x4 block is SKIP, one 4x4 codeword, four 2x2 codewords, or RAW literal pixels, chosen per block by rate-distortion. The RAW escape makes lam=0 pixel-exact (measured 0.00 dB loss), so the quality knob spans lossless to heavily-compressed in one bitstream. Per the user's decision, ships TWO quality profiles from that one codec, one decoder and one bitstream -- only the rate knob differs: sasi 45 KB/s lam=300 34.8 dB stock 10MHz ACE/EXPERT scsi 75 KB/s lam=100 35.9 dB Super/XVI or CZ-6BS1 Three corrections to earlier numbers: 1. Session 1's "183 KB/s at 12fps" was a bad extrapolation. Halving the framerate does not halve the bitrate -- decimation roughly doubles the per-frame delta. Re-measured directly: 340 KB/s for session 1's own RLE, 247 KB/s for changed-spans+deflate. The lossless floor is 319 MB. 2. A FOURTH false-good result, same family as the three in FINDINGS 4: k=1024 codebooks appeared to buy +2.4 dB free, because the rate model charged 1 byte for a 10-bit index. Charging the true cost reverses the verdict -- k=256 wins at every matched bitrate, and by 5 dB at the low end where the SASI profile lives. k=256 ships. 3. Stream inventory: the ~3-5MB clips are 1.2-1.7s, not ~60s, and some 60s streams are menus, not content. Any survey must classify before averaging. Also cleared both candidate sources for the game-logic layer: the SNES project is MIT and DirkSimple is zlib, so the arcade scene graph can be imported and the two transcriptions diffed against each other. Encoder is working end-to-end: extract.py -> vq/vq_hybrid/ratectl -> encode.py, emitting a big-endian DLX1 container the 68000 can parse with plain moves. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract decimated frames from a Blu-ray .m2ts into 256x192 PNGs.
|
||||
|
||||
Source is 1920x1080 (16:9). The arcade original is 4:3, so we CENTER-CROP to
|
||||
1440x1080 by default -- see docs/STATUS.md open question on framing.
|
||||
"""
|
||||
import subprocess, sys, os, shutil
|
||||
|
||||
STREAM_DIR = "/media/reala-misaki/BDROM/BDMV/STREAM"
|
||||
W, H = 256, 192
|
||||
|
||||
def duration(path):
|
||||
out = subprocess.check_output(["ffprobe","-v","error","-show_entries",
|
||||
"format=duration","-of","csv=p=0",path], text=True)
|
||||
return float(out.strip())
|
||||
|
||||
def extract(stream, outdir, fps=12, mode="crop", start=None, dur=None):
|
||||
src = f"{STREAM_DIR}/{stream}.m2ts"
|
||||
total = duration(src)
|
||||
if start is None: start = 0.0
|
||||
if dur is None: dur = total - start
|
||||
shutil.rmtree(outdir, ignore_errors=True); os.makedirs(outdir)
|
||||
if mode == "crop": # 4:3 centre crop, arcade framing
|
||||
vf = f"crop=1440:1080:240:0,hqdn3d=4:3:0:0,scale={W}:{H}:flags=lanczos"
|
||||
elif mode == "squash": # full 16:9 squeezed into 4:3
|
||||
vf = f"hqdn3d=4:3:0:0,scale={W}:{H}:flags=lanczos"
|
||||
elif mode == "wide": # 16:9 preserved, letterboxed later
|
||||
vf = f"hqdn3d=4:3:0:0,scale={W}:144:flags=lanczos"
|
||||
else: raise ValueError(mode)
|
||||
vf = f"fps={fps}," + vf
|
||||
subprocess.check_call(["ffmpeg","-v","error","-ss",str(start),"-t",str(dur),
|
||||
"-i",src,"-vf",vf,"-vsync","0",f"{outdir}/f%04d.png","-y"])
|
||||
n = len(os.listdir(outdir))
|
||||
print(f"{stream}: dur={total:.2f}s -> {n} frames @{fps}fps ({mode})")
|
||||
return n
|
||||
|
||||
if __name__ == "__main__":
|
||||
stream, outdir = sys.argv[1], sys.argv[2]
|
||||
fps = int(sys.argv[3]) if len(sys.argv) > 3 else 12
|
||||
mode = sys.argv[4] if len(sys.argv) > 4 else "crop"
|
||||
extract(stream, outdir, fps, mode)
|
||||
Reference in New Issue
Block a user