Skip to content

Eval: score.py with tests on synthetic boxes - #9

Merged
06pratyush merged 3 commits into
mainfrom
eval-score
Oct 28, 2026
Merged

06pratyush merged 3 commits into
mainfrom
eval-score

Conversation

@shashwat-deep

@shashwat-deep shashwat-deep commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Second step of the evidence half (docs/plan.md, "Your half", item 3). Stacked on #2: merge #2 first; GitHub then moves this PR onto main.

What is in it

  • eval/score.py scores a detections file (docs/contracts.md §4) against the manifest, as part B of the protocol defines it. Padding and threshold are applied here, so sweeping them needs no new detection run.

    • A mask is a box at or above the threshold, grown by pad, rounded outward to whole pixels and clipped: the pixels the app paints.
    • Coverage is the share of the face's box inside those pixels, computed exactly (the union of masks is cut into cells and the face is clipped to each). A face under the rotation transform is a rotated quadrilateral, which needs no special case.
    • It reports the miss rate (90% rule), the share covered completely, recall at IoU 0.5 (one-to-one, boxes in order of score) and false masks per image (IoU < 0.1 with every face, depictions included). Each comes with a 95% bootstrap interval over images: 10,000 resamples, numpy.random.default_rng(0), percentile interval.
    • Breakdowns: face size (the protocol's five bins), occlusion, truncation, yaw once labelled, and depictions on their own. Each transform and setting in the file is scored separately. --faces writes one row per face for later breakdowns (luma, for example).
    • --operating-point applies the protocol's padding and threshold rules on the tune split.
  • eval/transforms.py: for now only the geometry of rot<degrees>, written to match Pillow's rotate(expand=True) exactly, including its transposes at 90°, 180° and 270°. The image transforms and the darkening noise parameters come with the runner, before the first run.

  • 35 new tests on synthetic boxes, 44 in total. They cover:

    • the 90% line with rounding (a box 89.5 px wide covers 90 px, so the face is protected; 88.6 px covers 89, so it is missed);
    • overlapping masks counted once;
    • matching by score order;
    • false masks next to depictions;
    • a 45° face that is covered completely while its bounding box has IoU 0.48, so the usual recall misses it;
    • the bootstrap;
    • the operating-point rule, which picks threshold 0.31 with pad 0.10 on a made-up case built so that only that answer fits;
    • rotation against Pillow, pixel by pixel, at 8 angles and 3 image shapes.

    A mutation check confirmed that the tests fail when the order by score, the rounding direction or the depiction rule for false masks is broken.

  • Speed: on the real manifest with made-up detections, scoring the 800 report images takes about 1 s, and the operating-point search on the 200 tune images also about 1 s.

For both of us to agree before any run

  1. Contract 0.2 (docs/contracts.md §2): masks are rounded outward to whole pixels, left and top edges with Math.floor, right and bottom with Math.ceil. This is what maskFor in app/src/pipeline/mask.ts (Skeleton 3/4: cover each face with a padded opaque block #6) already does, so the contract now records it, and score.py counts the same pixels. (fillRect with fractional edges would blend the edge pixels with the face beneath them.)
  2. Protocol, part B (clarifications, no values changed):
    • recall at IoU 0.5 matches one to one in order of score, using boxes before padding, clipped to the image;
    • false masks are counted against depictions too;
    • the bootstrap's generator and interval are stated;
    • padding and threshold depend on each other, so the protocol now says how they are set together: for each threshold from 0.05 to 0.99 in steps of 0.01, take the smallest pad meeting the 99% rule, then the lowest threshold whose false masks meet the 0.25 rule with that pad.

How to check

uv pip sync --python .venv/bin/python eval/requirements.txt
.venv/bin/python -m pytest eval/tests

🤖 Generated with Claude Code

score.py scores a detections file against the manifest as part B of the
protocol defines it. Masks are rounded outward to whole pixels, as the app
paints them, and each face's covered share is computed exactly from those
pixels, so faces under the rotation transform need no special case. It reports
the miss rate, the share covered completely, recall at IoU 0.5 and false masks
per image, each with a 95% bootstrap interval over images, broken down by face
size, occlusion, truncation and yaw, and finds the operating point on the tune
split. transforms.py holds the rotation geometry, matched to Pillow.

Contract 0.2 rounds masks outward to whole pixels. The protocol now states how
the recall match works, the bootstrap's generator and how padding and
threshold are set together. 35 tests on synthetic boxes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--operating-point now refuses --split other than tune instead of ignoring it;
the bootstrap builds its resample counts once per group; the dropped zero-face
resamples of a breakdown are stated in the protocol; the file hashes come from
download.sha256_file; the summary names the settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shashwat-deep
shashwat-deep marked this pull request as ready for review October 27, 2026 18:55
Brings in main (#3 to #8) through #2. docs/ai-use.md keeps every row in the
order written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shashwat-deep added a commit that referenced this pull request Oct 27, 2026
Brings in main (#3 to #8) through #2 and #9. docs/ai-use.md keeps every row
in the order written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@06pratyush
06pratyush deleted the branch eval-manifest October 28, 2026 10:25
@06pratyush 06pratyush closed this Oct 28, 2026
@06pratyush 06pratyush reopened this Oct 28, 2026
@06pratyush
06pratyush changed the base branch from eval-manifest to main October 28, 2026 10:26
@06pratyush
06pratyush merged commit 52400c7 into main Oct 28, 2026
3 checks passed
@06pratyush
06pratyush deleted the eval-score branch October 28, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants