Task 6 of 6
Drag the last task's scrubber slowly and watch the face. The eyes creep together, the head goes oval, and by the end it is a different person: every row of it wide enough to matter loses exactly seven columns, so its widest row comes back thirty columns instead of thirty-seven — and not the same seven in every row (twenty-eight different columns of the face are cut somewhere), so its features stop lining up vertically. That skew is the straight-line artefact seam carving is famous for, showing up here as a warp. Seam carving has no idea what a face is. It knows that skin is smooth and that smooth is cheap, so once the sky is used up the face is the next best bargain in the picture.
The two poles, meanwhile, come through perfectly straight — every seam in this run happened to pass entirely to one side of them. That is luck, not a property, and it is the uncomfortable part: which structures survive depends on where the cheap material happens to be, and you cannot read it off the picture beforehand.
This is why nothing ships it unattended. Every product that offers content-aware resize offers a brush next to it, and the brush paints a mask: a region whose energy gets a large constant added, so every seam routes around it. Twenty is plenty here — the dearest seam this picture has anywhere in the run prices at under nineteen, and the ones actually taken top out at fourteen, so a single protected pixel already prices a seam out of the neighbourhood.
One trap comes free with the idea, and it is the reason the mask is carried in
planes with everything else: the mask lives in image space. Carve the
picture without carving the mask and the protection slides off the thing it was protecting,
one column at a time.
maskedEnergy, then measure the result — count
the protected pixels that survived, and plot the protected run's costs against the
unprotected ones.maskedEnergy returns the Sobel magnitude plus this.constants.penalty * mask[y][x]planes[4]) after the run and console.log it — it should equal what you started withplot({ ... }) with unmaskedCosts and your own costsThe Sobel part is untouched; the mask is one more term on the end:
return Math.sqrt(gx * gx + gy * gy)
+ this.constants.penalty * mask[y][x];
Because the mask is 0 outside the protected region, this costs the rest of the picture exactly nothing.
planes[4] is the mask after the same 32 carves as the picture, so
it is a plain 2D array one column narrower per removal:
let left = 0;
for (let y = 0; y < 72; y++) {
for (let x = 0; x < planes[4][y].length; x++) left += planes[4][y][x];
}
If that number has dropped, a seam went through the face.
plot takes an object of named series and draws them on the same
axes:
plot({ 'no mask': unmaskedCosts, 'face protected': costs },
{ title: 'what protection costs', xLabel: 'removal' });This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.