Task 5 of 5
Everything so far, pointed at a real question. brightScene hides an
8×8 patch; patch is that patch. rotatedPatch is the same eight
by eight values turned a quarter turn — identical mean, identical spread, identical
histogram, and nowhere in the scene. Find the one; refuse the other.
The kernel is finished (it is task 4's, unchanged). What is left is the part that catches people twice: reading an answer off a score map.
Take the maximum. NCC is a similarity, so its best is its largest. SSD was a distance, so its best was its smallest. The convention is inverted between the two measures, and reaching for the wrong one does not give you a slightly worse answer — it gives you the map's most emphatically wrong position.
The coordinates are the window's top-left corner. Cell (x, y) scored
the window that starts at (x, y) and runs 8 pixels right and down. That corner is the
answer. The centre is (x + 4, y + 4) if that is what you want — just be sure
you know which one you are reporting, because the score map is 89 wide where the scene is
96, and quietly mixing the two coordinate systems is how a detector ends up drawing boxes
in the wrong place.
And then the honest part. A search over 7,921 positions always returns a winner — the best score is a best score whether or not anything is there. What turns matching into detection is a threshold: a line below which "the best I found" means "nothing". Here the patch that is present scores 1.000 and the one that is absent tops out near 0.47, so 0.9 separates them with room to spare. That number is not universal — it depends on the noise, on the template, and on how much deformation you are willing to forgive — and calibrating it against data whose answers you already know is most of the work in building a real detector.
patch is, report that
rotatedPatch is not there, and let THRESHOLD be what decides.brightScene with the same kernelbestMatch: scan for the largest score and return the window's top-left cornerconsole.log the position found for patch, and the best score each template managedconsole.log whether its best score clears THRESHOLD — one true, one falseStart from -Infinity and keep the larger:
let best = -Infinity;
let bx = 0;
let by = 0;
for (let y = 0; y < map.length; y++) {
for (let x = 0; x < map[y].length; x++) {
if (map[y][x] > best) {
best = map[y][x];
bx = x;
by = y;
}
}
}
bx and by are already the corner — no offset to add.
prepare() hands the kernel what task 4 built, so each report is
three lines:
const map = await ncc(brightScene, t.centered, t.norm);
const hit = bestMatch(map);
console.log(label, hit.x, hit.y, hit.score,
hit.score >= THRESHOLD);This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.