tools: reject degenerate captures in screen_id instead of classifying them
Every statistic in screen_id is an AREA FRACTION, so a capture that is not a game frame still produces clean numbers. Measured 2026-08-26: a guard shot came back 10x710 -- a sliver -- and classified as `menu` with green=0.0000, white=0.0157. The guard passed, the fixed key sequence went out anyway, and the run loaded a TUTORIAL instead of the save's Stage 02. This is the second time this failure has been paid for. bin/screenshot's own header records the first (2026-08-18): a second window of class "xenia_canary" meant grabs came back as slivers and "a whole session's screen ids were noise". That fix hardened the CAPTURE side only, so the same failure still reached the oracles by any other path. Reject it at the point the answer is consumed too: features() now returns None below 640x360 and classify() reports `none`. Verified: the 10x710 sliver -> `none`; readyroom, flight and the briefing capture all still classify as before.
This commit is contained in:
@@ -35,7 +35,34 @@ import sys
|
||||
W, H = 320, 180
|
||||
|
||||
|
||||
def frame_size(path):
|
||||
"""(w, h) of the capture, or None if it cannot be read."""
|
||||
out = subprocess.run(["identify", "-format", "%w %h", path],
|
||||
capture_output=True, text=True).stdout.split()
|
||||
try:
|
||||
return int(out[0]), int(out[1])
|
||||
except (IndexError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
# A capture that is not a plausible game frame must be REJECTED, not classified.
|
||||
# MEASURED 2026-08-26: a guard shot came back 10x710 -- a sliver -- and every
|
||||
# statistic below is an area fraction, so the sliver classified cleanly as
|
||||
# `menu`. The guard passed, the fixed key sequence was issued anyway, and the run
|
||||
# loaded a TUTORIAL instead of the save's Stage 02.
|
||||
#
|
||||
# This is the SECOND time this has been paid for. `bin/screenshot`'s own header
|
||||
# records the first (2026-08-18): a second window of class "xenia_canary" meant
|
||||
# grabs came back as slivers and "a whole session's screen ids were noise". That
|
||||
# fix hardened the CAPTURE side only, so the same failure still reaches the
|
||||
# oracles by any other path. Reject it here too, where the answer is consumed.
|
||||
MIN_W, MIN_H = 640, 360
|
||||
|
||||
|
||||
def features(path):
|
||||
size = frame_size(path)
|
||||
if size is None or size[0] < MIN_W or size[1] < MIN_H:
|
||||
return None
|
||||
raw = subprocess.run(
|
||||
["convert", path, "-alpha", "off", "-resize", f"{W}x{H}!", "-depth", "8",
|
||||
"rgb:-"], capture_output=True).stdout
|
||||
@@ -57,7 +84,7 @@ def features(path):
|
||||
|
||||
def classify(f):
|
||||
if f is None:
|
||||
return "none"
|
||||
return "none" # unreadable OR not a plausible frame -- never a screen
|
||||
# Flight first: the HUD paints far more green than any menu.
|
||||
# (READY ROOM is checked before the menu rule because it is also dark-ish
|
||||
# and strongly blue, and would otherwise be swallowed by it.)
|
||||
|
||||
Reference in New Issue
Block a user