fix(fe): downscale mask flood-fill to a working resolution
The border flood-fill on a full-res 2256x4000 (9M px) photo was too slow in JS and the async mask pass timed out into the catch branch (overlay stayed null, 'Mask settled' never rendered). Downscale to <=1200px for the fill, then nearest-neighbor upsample the slab bitset back to full size so minAreaRect/slabBounds stay in source-pixel coordinates.
This commit is contained in:
@@ -117,7 +117,39 @@ function walkMask(decoded: DecodedPhoto, params: MaskParams): Uint8Array {
|
|||||||
const { width, height, rgba } = decoded;
|
const { width, height, rgba } = decoded;
|
||||||
const tone = sheetToneFor(params.sheet, decoded);
|
const tone = sheetToneFor(params.sheet, decoded);
|
||||||
const tolerance = 20 + (params.sensitivity / 100) * 80; // 20..100 distance
|
const tolerance = 20 + (params.sensitivity / 100) * 80; // 20..100 distance
|
||||||
return removeBackground(width, height, rgba, { tolerance, sheetTone: tone });
|
// Downscale to a working resolution so the flood-fill stays fast on large
|
||||||
|
// photos; the bitset is remapped back to full size for downstream geometry.
|
||||||
|
const MAX = 1200;
|
||||||
|
const scale = Math.min(1, MAX / Math.max(width, height));
|
||||||
|
if (scale >= 0.999) {
|
||||||
|
return removeBackground(width, height, rgba, { tolerance, sheetTone: tone });
|
||||||
|
}
|
||||||
|
const sw = Math.round(width * scale);
|
||||||
|
const sh = Math.round(height * scale);
|
||||||
|
const small = new Uint8ClampedArray(sw * sh * 4);
|
||||||
|
for (let y = 0; y < sh; y++) {
|
||||||
|
for (let x = 0; x < sw; x++) {
|
||||||
|
const sx = Math.min(width - 1, Math.round((x / sw) * width));
|
||||||
|
const sy = Math.min(height - 1, Math.round((y / sh) * height));
|
||||||
|
const si = (sy * width + sx) * 4;
|
||||||
|
const di = (y * sw + x) * 4;
|
||||||
|
small[di] = rgba[si];
|
||||||
|
small[di + 1] = rgba[si + 1];
|
||||||
|
small[di + 2] = rgba[si + 2];
|
||||||
|
small[di + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const smallBitset = removeBackground(sw, sh, small, { tolerance, sheetTone: tone });
|
||||||
|
// Remap the small bitset back to full resolution (nearest-neighbor upsample).
|
||||||
|
const full = new Uint8Array(width * height);
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
const tx = Math.min(sw - 1, Math.round((x / width) * sw));
|
||||||
|
const ty = Math.min(sh - 1, Math.round((y / height) * sh));
|
||||||
|
full[y * width + x] = smallBitset[ty * sw + tx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return full;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sheet tone to anchor the flood fill: sampled border, biased by the choice. */
|
/** Sheet tone to anchor the flood fill: sampled border, biased by the choice. */
|
||||||
|
|||||||
Reference in New Issue
Block a user