Autocrop finds the subject in a background-removed image - #50
Merged
Conversation
Autocrop on a background-removed image could hand back exactly the crop that was already there, looking like it had failed to see the picture at all. Two causes, both in the content scan: - Any pixel with alpha >= 16 counted as content. A matting model leaves a wash of near-zero alpha where it was unsure — invisible against the checkerboard, but enough to stretch the bounding box to the edges of whatever was scanned. The box then filled its region, so `square` sized itself to the region and returned it unchanged. Alpha now decides a cut-out on its own (colour sampling reads transparent pixels as black and drops a dark subject), with the bar at 64. - One speck of dust or one stray pixel pinned the box to the border, because bounds came from a raw min/max over every marked pixel. Marked pixels are now grouped into blobs — run-based connected components, a few ms on a 40MP source — and a blob is ignored when it is tiny on both axes AND holds under 0.5% of the content found. A hairline rule or a wire has real extent, so it is never at risk; if everything looks like a speck, everything is kept. Background colour, when it is still needed, is the median of the scanned border rather than the average of four corners, so a subject touching an edge no longer poisons the sample. Autocrop also no longer silently returns the crop it was given: if trimming inside the crop finds nothing (or nothing to trim), it re-scans the whole image, and when there is genuinely no border to trim it leaves the crop alone and says so in the panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DLjvwdNig18447roDzKQLs
Trimming inside the current crop meant a crop drawn through the subject could hide part of it from the search: Autocrop would clamp its answer to that crop and leave the subject's far edge outside. Scanning is now always full-frame, and the result replaces whatever crop was there. `computeContentBounds` loses its region parameter with the last caller that passed one. In place of the old "did the box fill the region it was scanned in" test, the finished rectangle is compared with the crop the user already has (or the whole image, when they have none) — so `1:1` on a full-bleed photo still gives a centred square, while `max` on the same photo correctly reports that there is no border to trim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DLjvwdNig18447roDzKQLs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Autocrop on a cut-out could hand back exactly the crop that was already there, looking like it had failed to see the picture at all. Reproduced in a headless browser, matching the reported rectangle byte for byte: a 4480×2520 cut-out with a 2128×2128 crop at x=1064, y=0 came back unchanged.
Why
Two faults in the content scan, both triggered by background removal:
1:1sized the square to the region and returned it. In the repro, alpha-20 haze turned the true subject box{1542, 681, 1786×1431}into{201, 60, 3899×2302}.Scanning was also confined to the current crop, so a crop drawn through the subject could hide part of it from the search.
What changed
computeContentBoundsloses its region parameter along with the last caller that passed one.Verified
1:1{1542, 504, 1786×1786}— whole subjectMax{1542, 681, 1786×1431}— exact subject1:1Max/Keep ratioSeven opaque-image regressions still pass — logo on white, photo on a black card, subject touching an edge, scan with a dust speck, flat colour, thin tall feature, black subject on transparency.
npm run typecheckandnpm run buildare clean.Generated by Claude Code