3c2f8871a8
Pure client modules for TECH-SPEC sheet detect, chroma-key / black threshold with border flood-fill, four knobs (live re-run), and mask confirm hooks. Synthetic fixtures. U2Net never invoked. TDD: red evidence in test-results/tv1-tv2-red.txt, then minimum impl. Results: TV-1 | pass | test-results/tv1-tv2-green.txt TV-2 | pass | test-results/tv1-tv2-green.txt
83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
confirmMask,
|
|
createMaskSession,
|
|
defaultKnobs,
|
|
removeSheetBackground,
|
|
reRunMask,
|
|
requestRetake,
|
|
} from "../lib/pipeline/mask.js";
|
|
import {
|
|
blackSheetFixture,
|
|
greenSheetFixture,
|
|
maskAt,
|
|
} from "./fixtures/synthetic.js";
|
|
|
|
describe("TV-1 green-sheet chroma-key", () => {
|
|
it("keeps interior green-ish patch that does not touch the border", () => {
|
|
const { image, interiorGreen, woodSample, borderSample } = greenSheetFixture();
|
|
const knobs = { ...defaultKnobs(), sheet: "green" as const };
|
|
const result = removeSheetBackground(image, knobs);
|
|
|
|
expect(result.u2netInvoked).toBe(false);
|
|
expect(result.sheetUsed).toBe("green");
|
|
expect(maskAt(result.mask, result.width, borderSample.x, borderSample.y)).toBe(0);
|
|
expect(maskAt(result.mask, result.width, woodSample.x, woodSample.y)).toBeGreaterThan(0);
|
|
expect(
|
|
maskAt(result.mask, result.width, interiorGreen.x, interiorGreen.y),
|
|
).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("re-runs live when knobs change and still skips U2Net", () => {
|
|
const { image } = greenSheetFixture();
|
|
const first = removeSheetBackground(image, { ...defaultKnobs(), sheet: "green" });
|
|
const second = reRunMask(image, {
|
|
...defaultKnobs(),
|
|
sheet: "green",
|
|
sensitivity: 0.7,
|
|
edgeOffset: 1,
|
|
feather: 1,
|
|
});
|
|
expect(first.u2netInvoked).toBe(false);
|
|
expect(second.u2netInvoked).toBe(false);
|
|
expect(second.knobs.sensitivity).toBe(0.7);
|
|
});
|
|
});
|
|
|
|
describe("TV-2 black-sheet threshold + border flood-fill", () => {
|
|
it("keeps dark interior wood that does not touch the border", () => {
|
|
const { image, darkInterior, woodSample, borderSample } = blackSheetFixture();
|
|
const knobs = { ...defaultKnobs(), sheet: "black" as const };
|
|
const result = removeSheetBackground(image, knobs);
|
|
|
|
expect(result.u2netInvoked).toBe(false);
|
|
expect(result.sheetUsed).toBe("black");
|
|
expect(maskAt(result.mask, result.width, borderSample.x, borderSample.y)).toBe(0);
|
|
expect(maskAt(result.mask, result.width, woodSample.x, woodSample.y)).toBeGreaterThan(0);
|
|
expect(
|
|
maskAt(result.mask, result.width, darkInterior.x, darkInterior.y),
|
|
).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("flood-fill removes only border-connected dark pixels", () => {
|
|
const { image, darkInterior, borderSample } = blackSheetFixture();
|
|
const result = removeSheetBackground(image, { ...defaultKnobs(), sheet: "black" });
|
|
// Border sheet gone; isolated dark island kept.
|
|
expect(maskAt(result.mask, result.width, borderSample.x, borderSample.y)).toBe(0);
|
|
expect(
|
|
maskAt(result.mask, result.width, darkInterior.x, darkInterior.y),
|
|
).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe("mask confirm hooks (pure)", () => {
|
|
it("starts pending, confirm and retake transition without DOM", () => {
|
|
const { image } = greenSheetFixture();
|
|
const result = removeSheetBackground(image, { ...defaultKnobs(), sheet: "green" });
|
|
const session = createMaskSession(result);
|
|
expect(session.state).toBe("pending");
|
|
expect(confirmMask(session).state).toBe("confirmed");
|
|
expect(requestRetake(session).state).toBe("retake");
|
|
});
|
|
});
|