diff --git a/PROJECT.md b/PROJECT.md
index 5d0ffdc..07f412b 100644
--- a/PROJECT.md
+++ b/PROJECT.md
@@ -365,6 +365,19 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
together. Draws stay integer-aligned with **no vertical offset**: a fractional `dy` on a scaled
image re-snaps its pixel rows every frame under nearest-neighbour — that boiling beside the HUD
was the "scoreboard flicker".
+- **THE APPLAUSE HAS TO HAPPEN WHERE THE HUD IS NOT.** A raise is only detectable where there is
+ empty stand above somebody's head — that is how the arms are told apart from the roll's drift —
+ and the sheet only offers that above the top row of each seating block. One of those rows sits at
+ the very top of the picture, which is exactly where the scoreboard is: measured, 31 of 134 raisers
+ were being drawn under the board or the blood panel. Oliver: "in the upper parts of the crowd there
+ seems to be a lot more action going on, but I can barely see it because the hud is covering it."
+ Two rules now hold the line, and BOTH are needed — the second alone would just delete the
+ applause:
+ 1. the arms-up rolls are asked for standers **in the lower two thirds only** (`_crowdroll.py`);
+ 2. `_crowdpass.py` drops any raiser whose box centre falls under the board or the blood panel
+ (`HUD`, in portrait logical px — landscape has a shorter band, so the rule is conservative
+ there rather than over-eager).
+ Result: 92 of 101 raisers below the board's bottom edge, against 103 of 134 before.
- **A raiser is ~8x6 logical px, so a pose that appears and holds is invisible.** What reads as
applause is the CYCLE — arms high, hands together, up, down, at about 3.5 Hz — and how many people
are doing it at once (`crowd.pop`). Oliver on the first per-person cut: "when clapping I barely see
diff --git a/assets/_crowdpass.py b/assets/_crowdpass.py
index a746b25..e6dedc0 100644
--- a/assets/_crowdpass.py
+++ b/assets/_crowdpass.py
@@ -62,6 +62,19 @@ GRADE_REF = '76ea1d0'
# borrows the hard-court sheet and does its own dimming in `COURTS.dim`, so it is fixed there.
COURTS = [('x_crowd', 0.72), ('x_crowd_c', 1.0), ('x_crowd_h', 1.0)]
+# WHAT THE HUD EATS. A raiser drawn under the scoreboard is a draw call nobody sees, and worse: the
+# "how many join in" fraction spends part of every cheer on them. Oliver: "in the upper parts of the
+# crowd there seems to be a lot more action going on, but I can barely see it because the hud is
+# covering it." Measured on the sheet before this rule, 31 of 134 raisers sat behind the board or the
+# blood panel. They are dropped here, which also makes the visible applause denser for free.
+# In LOGICAL px of the PORTRAIT layout, which is the primary target: the board sits at (6,6) 150x42
+# with its game/sets panel under it, the blood panel is up in the right corner, and the stand band is
+# about 195 px tall (render.js: standsBottom - AD_H). Landscape has a shorter band, so the same
+# fractions cover more of the sheet there — this rule is conservative, never over-eager.
+HUD_W, HUD_BAND = 270, 195
+HUD = [(6, 6, 156, 62), # scoreboard + the GAME/SETS panel under it
+ (170, 6, 264, 30)] # the BLOOD panel, clear of the quit chip
+
MIN_AREA, MAX_AREA = 150, 3000 # an arm blob: one pair of raised arms, or one arm
ARM_H, ARM_W = (10, 70), (8, 90) # ...and its shape, in px of the 1408x768 sheet
BAND = 8 # rows the background colour is sampled over
@@ -138,6 +151,17 @@ def merge(boxes):
return boxes
+def visible(boxes, h, w):
+ """Drop the raisers the HUD sits on top of — see HUD above."""
+ out = []
+ for y0, x0, y1, x1, m in boxes:
+ cx, cy = (x0 + x1) / 2 / w * HUD_W, (y0 + y1) / 2 / h * HUD_BAND
+ if any(a <= cx <= c and b <= cy <= d for a, b, c, d in HUD):
+ continue
+ out.append([y0, x0, y1, x1, m])
+ return out
+
+
def grade(src, ref):
"""Per-channel histogram match: the roll's colours redistributed onto a court's own look.
Returns a function, so the pair can be graded with ONE transform."""
@@ -199,7 +223,9 @@ def main(review):
print(f' pose {bit + 1}: {len(arms)} arm blobs, {moved:.1f}% of the sheet '
f'(the roll itself drifted {drift:.1f}%)')
boxes = merge(boxes)
- rows = write_table(boxes, h, w, len(frames))
+ seen = visible(boxes, h, w)
+ print(f' {len(boxes) - len(seen)} of {len(boxes)} raisers dropped: the HUD covers them')
+ rows = write_table(seen, h, w, len(frames))
both = sum(1 for r in rows if r[4] == 3)
print(f'{len(rows)} spectators can move, {both} of them with both poses (a real clap)')
diff --git a/assets/_crowdroll.py b/assets/_crowdroll.py
index fb76375..2d294f1 100644
--- a/assets/_crowdroll.py
+++ b/assets/_crowdroll.py
@@ -83,7 +83,8 @@ MID = (
"standing with both arms raised straight above their heads. Redraw it EXACTLY — the same crowd, "
"the same people in the same seats, the same clothes, hair, hats and colours, the same rows, "
"railing, empty seats and stairways, the same lighting, pixel for pixel — WITH ONE CHANGE: "
- "every spectator who is standing with their arms up has now brought their HANDS TOGETHER IN "
+ "every spectator who is standing with their arms up — all of them in the lower two thirds of "
+ "the picture — has now brought their HANDS TOGETHER IN "
"FRONT OF THEIR FACE at chin height, elbows bent and pointing out to the sides, in the middle of "
"a clap. They are the same standing people in the same places; only their arms have come down "
"and in. Their hands and elbows still rise above the row in front of them. "
@@ -94,9 +95,12 @@ CLAP = (
"This reference image is a stadium grandstand full of seated spectators. Redraw it EXACTLY — the "
"same crowd, the same people in the same seats, the same clothes, hair, hats and colours, the "
"same rows, railing, empty seats and stairways, the same lighting, pixel for pixel — WITH ONE "
- "CHANGE: about forty of the spectators, scattered evenly all over the picture, are now ON THEIR "
- "FEET APPLAUDING with both arms raised above their heads, hands together, clapping. "
- "Each of those forty is the SAME PERSON as in the reference — same face, same hair, same "
+ "CHANGE: about sixty of the spectators are now ON THEIR FEET APPLAUDING with both arms raised "
+ "above their heads, hands together, clapping. "
+ "EVERY ONE OF THEM IS IN THE LOWER TWO THIRDS OF THE PICTURE — the rows nearest the court, "
+ "below the horizontal railing — spread evenly from the left edge to the right edge. NOBODY in "
+ "the top third of the picture stands up; those rows are exactly as they are in the reference. "
+ "Each of those sixty is the SAME PERSON as in the reference — same face, same hair, same "
"clothing — just standing and reaching up, so their head and hands rise above the row in front. "
"EVERYONE ELSE IN THE PICTURE IS COMPLETELY UNCHANGED, identical to the reference. "
"The three grey-green zombie spectators do NOT move and do NOT applaud. " + STYLE
diff --git a/assets/_sheet_crowd3b.png b/assets/_sheet_crowd3b.png
index 5e363f9..08a6318 100644
Binary files a/assets/_sheet_crowd3b.png and b/assets/_sheet_crowd3b.png differ
diff --git a/assets/_sheet_crowd3c.png b/assets/_sheet_crowd3c.png
index 5e5d33a..64848b3 100644
Binary files a/assets/_sheet_crowd3c.png and b/assets/_sheet_crowd3c.png differ
diff --git a/assets/x_crowd.png b/assets/x_crowd.png
index 4525db7..2f95fd7 100644
Binary files a/assets/x_crowd.png and b/assets/x_crowd.png differ
diff --git a/assets/x_crowd_c.png b/assets/x_crowd_c.png
index 26eb549..511926a 100644
Binary files a/assets/x_crowd_c.png and b/assets/x_crowd_c.png differ
diff --git a/assets/x_crowd_h.png b/assets/x_crowd_h.png
index dda0b20..3d01876 100644
Binary files a/assets/x_crowd_h.png and b/assets/x_crowd_h.png differ
diff --git a/index.html b/index.html
index c4a1609..1c3e71f 100644
--- a/index.html
+++ b/index.html
@@ -664,6 +664,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
-
+