ONE palette gate replaces four feature-picked ones

Oliver, on the brute's fall frames: "now the fall zombie has a different skin
tone.. man this is all so tiresome." Both halves were right. The skin IS twenty
points lighter across that sheet, and the tiresome part is mine: I had been
adding one hand-picked gate per drift I discovered — redness, then hue, then
`changed`, then no-grids — which is a loop with no end, because naming features
is not the same as comparing pictures.

`kit` names four: shirt redness, shorts brightness, shorts hue, near-white
fraction. Every character breaks a different one. The brute's shirt has no red
in it at all, so its reference redness was 0 and blood scored 70-100 against a
tolerance meant for a garment. A body face down shows a fraction of the shirt it
shows standing, so two of the four move with POSE. And nothing in that list ever
measured SKIN, which is exactly how a brute's flesh drifted across a whole sheet
with six gates green.

The palette gate asks the only question that generalises: are the big colour
areas of this frame the same colours as the anchor's? Top SIX by area — the
garments and the skin. Proportions ignored, so a pose change is invisible; blood
is never in the top six, so a fresh wound cannot look like drift.

CALIBRATED BEFORE IT WAS WIRED IN, which is the discipline I skipped all day.
Frames known to be right score 0.0-8.5; the drifts we could actually see score
12.0-15.9. The threshold is 10, in the gap. Three formulations were measured and
the two that overlapped were thrown away.

And every sheet of a variant is now anchored to that variant's FIRST sheet, not
to the stage above it. Chaining let a legal hop compound into a drift nobody
authorised — the shorts walking -9 to -19 down the wound ladder was that, and so
was this.

Its first verdict on the existing brute: br_play is the character, and every
other sheet sits 15 to 48 away from it. It would have caught all of this on the
first roll.

Also in: the GRID guard from the same session — the valley-cut rescue would slice
a 2x2 sheet into four frames of a torso above someone else's boots, turning a
loud failure into a silent one. Asked per cut PIECE, because a body is vertically
continuous and a piece with a gap in it caught two of them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-12 13:45:41 +02:00
1 parent df454c5d92
commit 0e92c72457
33 files changed
+312 -19

No files matched your search

+34 -1
View File
@@ -92,6 +92,34 @@ def valley_cuts(ink_cols, x0, x1, n):
out.append(lo + int(np.argmin(ink_cols[lo:hi])))
return out
# DO THE FIGURES SHARE A GROUND LINE? That is the invariant every prompt in this project asks for,
# and it is the one that tells a ROW from a GRID. An empty-band test does not: in the sheet that
# caught this, a 2x2 of a brute falling, the bottom-right figure was tall enough to overlap the top
# row and there was no clean horizontal gap anywhere.
#
# It matters because the valley rescue below will cut ANY sheet into the number of columns it was
# asked for — and on a grid each column is a torso above someone else's boots. Four such frames
# installed silently and looked, in the log, exactly like a success. The rescue turned a loud failure
# into a quiet one, which is the worst trade available in this file; this is the check that stops it.
# IS THIS ONE FIGURE, OR TWO STACKED? Asked of each CUT PIECE, not of the sheet. A body is
# vertically continuous — head to feet there is no height at which the piece is empty — so a
# full-width empty band inside a piece's own extent means the cut caught someone's torso above
# someone else's boots.
#
# Asking it of the whole sheet does not work, and both weaker versions were tried: an empty-band test
# on the sheet fails when a grid's bottom-right figure is tall enough to overlap the top row, and a
# shared-ground-line test fails because after a bad cut EVERY column reaches the bottom row's feet.
# The piece is the only place the question has a clean answer.
def split_piece(crop):
rows = (crop[..., 3] > 0).any(1)
ys = np.where(rows)[0]
if not len(ys): return False
run = best = 0
for y in range(ys.min(), ys.max() + 1):
run = 0 if rows[y] else run + 1
if run > best: best = run
return best >= MIN_GAP * 2
def frames(a, cuts, want=0):
ink = (a[..., 3] > 0)
cols = ink.any(0)
@@ -122,12 +150,17 @@ def main(sheet, outs, target_h, cuts, anchor, defringe=2):
a = key(Image.open(sheet), defringe)
b = block_size(a)
crops = []
for x0, x1 in frames(a, cuts, len(outs)):
spans = frames(a, cuts, len(outs))
for x0, x1 in spans:
sub = a[:, x0:x1]
ys = np.where((sub[..., 3] > 0).any(1))[0]; xs = np.where((sub[..., 3] > 0).any(0))[0]
crops.append(sub[ys.min():ys.max()+1, xs.min():xs.max()+1])
if len(crops) != len(outs):
sys.exit(f'ERROR: found {len(crops)} frames, expected {len(outs)} ({sheet})')
bad = [i for i, c in enumerate(crops) if split_piece(c)]
if bad:
sys.exit(f'ERROR: frame(s) {bad} contain two stacked bodies — the sheet is a GRID, not one '
f'row ({sheet})')
# palette = the colours the generator actually used, on the largest frame
big = max(crops, key=lambda c: (c[..., 3] > 0).sum())
px = big[big[..., 3] > 200][:, :3]