fix(mask): keep only largest connected slab component
Sheet gaps after black/green removal left stray chunks in the bitset. After keep predicates, zero all but the largest 4-connected component and recompute slabPx before bbox / slabBounds.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { keepLargestComponent } from './largestComponent.ts';
|
||||
|
||||
test('keepLargestComponent: two blobs → only larger remains', () => {
|
||||
// 10×6 grid: large 4×3 block (12 px) at left, small 2×2 (4 px) at right
|
||||
const W = 10, H = 6;
|
||||
const bitset = new Uint8Array(W * H);
|
||||
for (let y = 1; y <= 3; y++)
|
||||
for (let x = 1; x <= 4; x++) bitset[y * W + x] = 1;
|
||||
for (let y = 2; y <= 3; y++)
|
||||
for (let x = 7; x <= 8; x++) bitset[y * W + x] = 1;
|
||||
|
||||
const before = bitset.reduce((a, c) => a + c, 0);
|
||||
assert.equal(before, 16);
|
||||
|
||||
const slabPx = keepLargestComponent(bitset, W, H);
|
||||
assert.equal(slabPx, 12, 'slabPx is pixel count of largest blob');
|
||||
|
||||
// Large blob intact
|
||||
for (let y = 1; y <= 3; y++)
|
||||
for (let x = 1; x <= 4; x++)
|
||||
assert.equal(bitset[y * W + x], 1, `large (${x},${y}) kept`);
|
||||
|
||||
// Small blob zeroed
|
||||
for (let y = 2; y <= 3; y++)
|
||||
for (let x = 7; x <= 8; x++)
|
||||
assert.equal(bitset[y * W + x], 0, `small (${x},${y}) removed`);
|
||||
|
||||
assert.equal(bitset.reduce((a, c) => a + c, 0), 12);
|
||||
});
|
||||
|
||||
test('keepLargestComponent: empty bitset → 0', () => {
|
||||
const bitset = new Uint8Array(4 * 4);
|
||||
assert.equal(keepLargestComponent(bitset, 4, 4), 0);
|
||||
});
|
||||
|
||||
test('keepLargestComponent: single blob unchanged', () => {
|
||||
const W = 5, H = 5;
|
||||
const bitset = new Uint8Array(W * H);
|
||||
for (let y = 1; y <= 3; y++)
|
||||
for (let x = 1; x <= 3; x++) bitset[y * W + x] = 1;
|
||||
const slabPx = keepLargestComponent(bitset, W, H);
|
||||
assert.equal(slabPx, 9);
|
||||
assert.equal(bitset.reduce((a, c) => a + c, 0), 9);
|
||||
});
|
||||
|
||||
test('keepLargestComponent: 4-connected — diagonal touch is two components', () => {
|
||||
// Two pixels only touching diagonally; larger is the two-pixel L on the left
|
||||
// of a separate single pixel that is diagonal to nothing of equal size...
|
||||
// Layout: three pixels as an L (size 3) and one diagonal fleck (size 1)
|
||||
// . # .
|
||||
// # # .
|
||||
// . . #
|
||||
const W = 3, H = 3;
|
||||
const bitset = new Uint8Array(W * H);
|
||||
bitset[0 * W + 1] = 1;
|
||||
bitset[1 * W + 0] = 1;
|
||||
bitset[1 * W + 1] = 1;
|
||||
bitset[2 * W + 2] = 1; // diagonal-only to the L
|
||||
|
||||
const slabPx = keepLargestComponent(bitset, W, H);
|
||||
assert.equal(slabPx, 3);
|
||||
assert.equal(bitset[2 * W + 2], 0, 'diagonal fleck removed under 4-conn');
|
||||
assert.equal(bitset[0 * W + 1], 1);
|
||||
assert.equal(bitset[1 * W + 0], 1);
|
||||
assert.equal(bitset[1 * W + 1], 1);
|
||||
});
|
||||
Reference in New Issue
Block a user