fix(photos): preserve tapered tips and dark live edge (#73 #74)

Lower the axial body threshold while retaining fringe rejection, minimize length-only tip inset, and make sheet sensitivity curves testable and conservative at zero.
This commit is contained in:
2026-09-16 15:18:06 +00:00
parent 037a4b870a
commit 03d3fcf460
5 changed files with 281 additions and 36 deletions
+36
View File
@@ -0,0 +1,36 @@
/**
* Sheet-vs-slab keep predicates used by the mask worker.
* Extracted so unit tests can cover sensitivity curves without the worker.
*/
/** Keep slab pixels on a green sheet. Higher s widens the green-sheet match. */
export function greenKeep(r: number, g: number, b: number, s: number): boolean {
const t = Math.max(0, Math.min(100, s)) / 100;
// s=0 removes only strongly green sheet; increasing sensitivity widens the match.
const isSheet = g - Math.max(r, b) > 45 - 35 * t;
return !isSheet;
}
/**
* Keep slab pixels on a black sheet.
* At s=0 only near-black sheet (very low lum) is removed so dark live-edge /
* bark (lum ~25–50) stays. The match widens monotonically toward s=100 (#74).
*/
export function blackKeep(r: number, g: number, b: number, s: number): boolean {
const t = Math.max(0, Math.min(100, s)) / 100;
const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
// Old floor 40 ate dark wood at minimum sensitivity. Floor 10 isolates near-black.
const isSheet = lum < 10 + 140 * t;
return !isSheet;
}
export function autoPickSheet(
samples: { r: number; g: number; b: number }[],
): 'green' | 'black' {
let gr = 0, gl = 0;
for (const p of samples) {
if (p.g - Math.max(p.r, p.b) > 20) gr++;
if (0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b < 70) gl++;
}
return gl >= gr ? 'black' : 'green';
}