import { describe, expect, it } from "vitest"; import { encodeMaskPng } from "./media-emulator.js"; import type { LumaFrame } from "./media-privacy-pixels.js"; import { controlAgreement, decodePngLuma, maskBounds, residualVisibility, temporalStability, } from "./media-privacy-pixels.js"; /** A 64x64 checkerboard of 4 px squares: detail everywhere. */ function checker(width = 64, height = 64, shift = 0): LumaFrame { const luma = new Uint8Array(width * height); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { luma[y * width + x] = (Math.floor((x + shift) / 4) + Math.floor(y / 4)) % 2 ? 220 : 30; } } return { width, height, luma }; } function fill( frame: LumaFrame, box: { x: number; y: number; w: number; h: number }, value: number, ) { const out = { ...frame, luma: frame.luma.slice() }; for ( let y = Math.floor(box.y * frame.height); y < Math.ceil((box.y + box.h) * frame.height); y++ ) { for ( let x = Math.floor(box.x * frame.width); x < Math.ceil((box.x + box.w) * frame.width); x++ ) { out.luma[y * frame.width + x] = value; } } return out; } const region = { x: 0.25, y: 0.25, w: 0.5, h: 0.5 }; describe("PNG masks", () => { it("decodes the masks the segmenter writes", () => { const mask = decodePngLuma(encodeMaskPng(40, 20, { x: 0.5, y: 0.25, w: 0.25, h: 0.5 })); expect(mask).toMatchObject({ width: 40, height: 20 }); expect(maskBounds(mask)).toEqual({ x: 0.5, y: 0.25, w: 0.25, h: 0.5 }); expect(maskBounds(decodePngLuma(encodeMaskPng(8, 8, { x: 0, y: 0, w: 0, h: 0 })))).toBeNull(); expect(() => decodePngLuma(Buffer.from("nope"))).toThrow("not a PNG"); }); }); describe("residual visibility", () => { it("is 1 for untouched detail and 0 for a filled or flat region", () => { const source = checker(); expect(residualVisibility({ source, treated: source, box: region })).toBe(1); expect(residualVisibility({ source, treated: fill(source, region, 128), box: region })).toBe(0); const flat = fill(source, region, 90); expect( residualVisibility({ source: flat, treated: flat, box: { x: 0.3, y: 0.3, w: 0.4, h: 0.4 } }), ).toBe(0); }); it("counts only the part a crop still shows and only mask pixels", () => { const source = checker(); // The treated frame is the right half of the source, untouched. const half: LumaFrame = { width: 32, height: 64, luma: new Uint8Array(32 * 64) }; for (let y = 0; y < 64; y++) half.luma.set(source.luma.subarray(y * 64 + 32, y * 64 + 64), y * 32); const left = { x: 0, y: 0.25, w: 0.4, h: 0.5 }; expect(residualVisibility({ source, treated: half, box: left, offset: { x: 32, y: 0 } })).toBe( 0, ); expect( residualVisibility({ source, treated: half, box: region, offset: { x: 32, y: 0 } }), ).toBe(1); const mask = decodePngLuma(encodeMaskPng(64, 64, { x: 0.5, y: 0.25, w: 0.25, h: 0.5 })); const treated = fill(source, { x: 0.25, y: 0.25, w: 0.25, h: 0.5 }, 128); expect(residualVisibility({ source, treated, box: region, mask })).toBe(1); }); it("detects frames that are not the same moment", () => { const source = checker(); const treated = fill(source, region, 128); expect(controlAgreement({ source, treated, boxes: [region] })).toBe(1); expect(controlAgreement({ source, treated: checker(64, 64, 2), boxes: [region] })).toBeLessThan( 0.8, ); }); }); describe("temporal stability", () => { it("drops when the treated region flickers more than the frame", () => { const a = checker(); expect(temporalStability([a, a, a], region)).toBe(1); const flicker = [fill(a, region, 0), fill(a, region, 255), fill(a, region, 0)]; expect(temporalStability(flicker, region)).toBe(0); expect(temporalStability([a], region)).toBe(1); }); });