Task 4 of 6

Take It Out, Let the Picture Close Up

Deleting the seam is a splice on a CPU: for each row, remove one element and let the rest shuffle down. On a GPU there is no splice. A thread writes its own output cell and nothing else — it cannot push its neighbour along, which is the "no scatter" rule Thinking in Parallel makes a whole module of.

So turn the question round, which is what a gather always is. The output is one column narrower than the input. The thread that owns output cell (x, y) asks: which input pixel belongs here? Everything left of the seam has not moved. Everything from the seam rightwards has slid one column left, so it comes from x + 1. One if, no shuffling, and every row does its own thing at the same time even though every row's seam is at a different column.

The output being narrower than the input is the reason for dynamicOutputcarve.setOutput([w - 1, 72]) before each call, and the same kernel keeps working all the way down.

nobody is pushed aside — every output pixel reaches for its own source — Eight input pixels above seven output pixels. Input three is the seam and is removed. Outputs zero to two read straight down; outputs three to six read diagonally from inputs four to seven, one column further right.
Goal: write carve so its output is plane with the seam pixel removed from every row, and everything to its right pulled one column left.

Requirements

Hint 1 — which input pixel is mine?

Say the seam is at column 3 in this row. Output cells 0, 1, 2 are input cells 0, 1, 2 — nothing moved. Output cell 3 is input cell 4: input cell 3 was the seam and is gone. Output cell 4 is input 5, and so on.

Hint 2 — the whole kernel
const x = this.thread.x;
const y = this.thread.y;
if (x < seam[y]) return plane[y][x];
return plane[y][x + 1];

Note the strict <. With <= the seam pixel survives and its right-hand neighbour is deleted instead — the picture still narrows by one, so the shapes all check out and the wrong pixel is gone.

Same idea elsewhere

Compaction by gather is the standard GPU answer to "remove some elements": a thread computes where its data comes from rather than where it goes, because destinations collide and sources never do. Stream Compaction builds the general version with a prefix sum; here the geometry hands you the offset for free — it is 0 or 1, decided by one comparison. CUDA's thrust::remove_if and WebGPU compaction passes are the same shape underneath.

All tasks in Seam Carving: Content-Aware Resizing

  1. What Can We Afford to Lose?
  2. The Cheapest Path Down: One Launch per Row
  3. Reading the Seam Back Out
  4. Take It Out, Let the Picture Close Up
  5. Payoff: Thirty-Two Seams
  6. What It Does Badly, and the Fix Everybody Ships

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.