The Seam Problem
Open any photo editor and try to paste a piece of one photo onto another. You drag a sunlit face onto a moody portrait, drop a tropical bird into a winter landscape, or splice a tumour patch into a medical scan for training data. Almost always the same thing happens: a hard, obvious seam appears along the edge of the pasted region. The patch "sits" on top of the scene instead of belonging to it.
The seam is not a hardware bug or a missing alpha channel. It is a boundary mismatch: the pixel colours at the inside edge of your patch disagree with the pixel colours just outside it. Even a tiny brightness offset of 10 grey levels is instantly visible to the human visual system, because the eye is enormously sensitive to gradients — local differences in brightness and colour.
The key insight: what makes a region look "part of" the surrounding scene is not its absolute colour but the way its colour changes as you walk across the boundary. If you can stitch the gradients smoothly, the eye stops seeing the seam — even when the interior pixels are different.
In 2003, Patrick Pérez, Michel Gangnet, and Andrew Blake turned this insight into a clean variational problem and solved it with Poisson's equation . This section is the calculus story behind that idea — how a 19th-century equation about gravitational potential ended up driving Photoshop's healing brush, computational photography, gradient-domain HDR, and modern image completion.
Intuition: Edit Gradients, Not Pixels
Think of a photograph not as a grid of colours but as a landscape of heights: pixel value = altitude. A bright sky is a high plateau, a dark shadow is a valley, an edge is a cliff. Most of what your eye registers as "the content" of an image is the slopes of this landscape — the gradient .
When you copy-paste, you carry the source patch's altitudes with you. Almost always the patch lands on the target landscape at a different overall height. The cliff at the boundary of the patch is the seam.
The trick: instead of copying altitudes, copy slopes. Take the patch's slope field , drop it onto the target landscape, and then ask the patch's heights to be re-computed so that (a) the slopes match inside, and (b) the heights match the target on the boundary. Calculus will solve for the heights for us.
That is the entire idea of Poisson blending. We give up control over what the patch looks like in absolute brightness, in exchange for total control over how it flows into its neighbourhood. The eye, watching only slopes, never notices the seam.
An analogy that helps: imagine you are restoring a damaged painting. A square inch is missing in the middle of a fresco. You can't just paste in a square from another fresco — colours won't match. But if you have a sketch of the brushwork pattern that would have been in that square, you can ask a careful painter to redraw the missing inch with that brushwork, blending the pigments smoothly into the surrounding wall. Poisson blending is that careful painter, mathematised.
From Calculus to Pixels: The Discrete Laplacian
Continuous calculus gives us the Laplacian . An image is a grid, not a smooth function, so we replace each partial derivative with a finite difference. The standard approximation at pixel is:
Add the same thing in the direction and you get the famous 5-point stencil:
Visually, that is just: (sum of four neighbours) minus four times the centre. Flat regions give . Bumps and dips give nonzero values; the sign tells you whether the pixel is a local maximum or minimum, the magnitude tells you how sharp it is.
| Region in the image | Sign of Δf | Meaning |
|---|---|---|
| Smooth gradient (sky) | ≈ 0 | Slopes are constant, no curvature |
| Bright spot on dark background | negative | Centre higher than neighbours |
| Dark spot on bright background | positive | Centre lower than neighbours |
| Edge (cliff) | large magnitude | Strong second derivative across the edge |
The 5-point Laplacian is the bridge between calculus and image processing. Everything in Poisson blending will be expressed as a relationship between two of these stencils — one on the blended image we are solving for, one on the source we want to import.
Deriving Poisson's Equation for Blending
Let's state the problem cleanly. We have:
- A target image , defined everywhere except inside the patch region.
- A source image defined on (the region we want to fill).
- We want to find a function on whose gradient looks like the source's gradient inside , but whose values match on the boundary.
Mathematically, define the guidance vector field and minimise:
In English: among all functions that agree with the target on the boundary, pick the one whose slopes are closest, in least-squares sense, to the source's slopes. This is a textbook calculus-of-variations problem. The Euler-Lagrange equation falls out by setting the first variation to zero — and the result is exactly Poisson's equation:
Where the Euler-Lagrange step comes from: a variation with changes the objective by . Integrate by parts (Green's identity) — the boundary term vanishes because there — and you are left with for every . The only way that is true is pointwise.
Discretise with the 5-point Laplacian on both sides and we get the linear system we will actually solve. For every pixel inside :
Whenever a neighbour lies outside , we know its value: it is , the target. So that term moves to the right-hand side and becomes a fixed contribution. The remaining unknowns are exactly the pixels inside , and we have one linear equation per unknown — the system is sparse, symmetric, and positive-definite. Perfect for iterative solvers.
Why Dirichlet Boundary Conditions Make Seams Vanish
The Dirichlet boundary condition is doing more than "pin the edge." It is the entire reason the seam disappears.
Suppose the source patch is uniformly 30 grey levels brighter than the target at the boundary. The naive paste produces a hard 30-level jump along the boundary — instantly visible. Poisson blending responds by adding a harmonic correction across the entire interior of : a smooth function with inside, whose values on the boundary cancel the mismatch exactly.
Mathematically, write inside . Substituting into gives — so is harmonic. And the boundary condition becomes on . So is the unique harmonic function that smoothly repairs the boundary disagreement over the whole patch. It is, in a precise sense, the "best feathering" you could hope for — better than any handcrafted blur kernel because the eye sees no second derivative jumps.
Why this matters for art and ML: the harmonic correction is what humans perceive as "the patch belongs." Modern neural style-transfer and diffusion-based inpainting systems can be viewed as learned versions of exactly this correction — they regress a more powerful, non-linear inpainting field, but the boundary-matching principle is the same.
Interactive: Watch a 1D Seam Disappear
In one dimension the math is identical but the picture is simpler. The target is a smooth curve; the "patch" is a sub-interval we want to replace by a source curve. The naive paste creates a vertical jump at each endpoint (the seam). The Poisson blend solves on with and . The result keeps the source's wiggle but ramps up or down to match the target on both sides.
Drag the Source DC offset slider to mis-align the patch vertically. The red curve (naive paste) develops bigger and bigger seams. The blue curve (Poisson blend) keeps the same internal shape but shifts/tilts to absorb the offset — and the seams vanish.
Notice what the blue curve does as you slide the offset: it does not simply translate the source up or down. It applies a smooth, almost linear, correction across the interval — exactly the 1D version of the harmonic function . (In 1D, harmonic = linear, because .) The interior wiggle is preserved because its second derivative, not its absolute height, is what the equation cares about.
Worked Example: A 4×4 Patch by Hand
Before we trust a solver, let's do one fully by hand. A tiny patch is small enough to write every equation explicitly. Open the box below and try the arithmetic on paper — it makes the matrix structure of Poisson blending tangible.
Click to expand: solve a 4×4 Poisson blend by hand
Take a tiny scene. The target is a 4×4 image:
The four 's are the patch : the inner block, with the 12 boundary cells around it acting as .
The source on the same grid is a much brighter object:
The naive paste copies the four central values straight into the target — producing a hideous bright square next to the dim border (12, 14, 18, 20).
Step 1 — compute div(v) at each interior pixel
For the patch pixel at position (using row, column with 0-indexing) the 5-point Laplacian of is:
By symmetry the other three give:
| Pixel (r, c) | g value | Sum of 4 g-neighbours | Δg = 4·g − sum |
|---|---|---|---|
| (1,1) | 200 | 101+201+101+201 = 604 | 196 |
| (1,2) | 201 | 200+104+102+201 = 607 | 197 |
| (2,1) | 201 | 102+202+201+104 = 609 | 195 |
| (2,2) | 202 | 201+105+201+105 = 612 | 196 |
Step 2 — write the linear system
Let be the four unknown blended pixels in the same order (top-left, top-right, bottom-left, bottom-right of the inner block). Each one's discrete Poisson equation pulls in its known target neighbours from :
Step 3 — solve the 4×4 linear system
The system is small enough to solve by adding and subtracting rows. Add the first and fourth equations, and the second and third equations:
Let and . Then and . Adding gives … let's just solve cleanly. From the first: . Substitute: , so , giving and .
Now subtract equation 1 from equation 4: , so . With we get , . Equation 2 minus equation 3 gives , so . With we get , .
Step 4 — interpret
The blended patch is approximately:
Compare three things side by side at pixel : target said , naive paste said , Poisson blend says . The bright source has been pulled down toward the dim target — but critically, the differences between the four blended pixels match the differences in the source . Same internal texture; rescaled absolute level. That is Poisson blending in four numbers.
Sanity check via Jacobi. A single Jacobi sweep starting from the target values in the interior takes about 25 iterations to reach those numbers to within . The next interactive (and the Python code below) runs exactly this loop on full images.
Interactive: 2D Seamless Cloning Playground
Two synthetic 96×72 images. The target is a soft blue-to-green scene with a sun-bright spot; the source is a colourful spiral object. Click anywhere on the target to drop the patch region . Switch between naive paste, full Poisson blend, and mixed gradients (next section) to compare.
Click anywhere on the target image to place the patch region Ω. The Poisson solver runs on every change — give it a moment for large iterations counts.
Two things to play with: (a) drop the patch on a bright part of the scene versus a dark part — notice how Poisson blending pulls the patch's brightness toward whatever ∂Ω demands, without you ever recolouring it; (b) crank iterations from 50 up to 1500 — at low iterations the seam is still slightly visible because Jacobi has not yet propagated the boundary correction all the way to the centre of .
Plain Python: A Tiny Jacobi Solver
Twelve lines of NumPy is all it takes. We build the guidance field once, then run Jacobi sweeps that, at every pixel inside , replace its value by the average of its four neighbours plus . Pixels outside are never touched — that is the boundary condition in code.
On a typical laptop this runs a 200×200 patch with 2000 iterations in about two seconds — already usable for prototyping. For real-time editing or 4K images you want a GPU.
PyTorch on the GPU
The Jacobi iteration is embarrassingly parallel: every pixel's update depends only on the previous iteration's neighbours, so we can update them all at once. PyTorch makes this trivial — replace NumPy slices with the same kind of slicing on tensors, pad with for clean borders, and run on .
Two production improvements you should be aware of, even though we keep them out of the teaching code: (1) replacing Jacobi with red-black Gauss-Seidel or conjugate gradient roughly halves the iteration count; (2) for rectangular patches a closed-form solver via the discrete sine transform exists and runs in — essentially a single FFT pass.
Mixing Gradients: Keep the Strongest Edges
Pure Poisson blending always uses the source's gradients . That is exactly right when the patch should replace what is under it. But sometimes you want to overlay — paste graffiti onto a wall and keep the wall's brick texture visible through the thin parts of the letters, or paste a face into a low-contrast background and keep some background detail.
The fix is one line of code. Instead of always taking , at each pixel pick — per direction — whichever of or has the larger magnitude:
The Poisson equation, the boundary condition, and the solver are unchanged — only the right-hand-side guidance field differs. In the 2D interactive above, switch the mode to mixed and drag the patch over the bright sun in the target: the sun's strong gradient survives through the patch because it "won" the pixel-by-pixel competition.
Variants you can build with the same machinery: texture flattening (kill weak gradients, keep strong edges → cartoon look); local illumination change (compress the log of the magnitude of → HDR tone-mapping); seamless tiling (force the gradient to be periodic on the boundary). They are all one-line changes to .
Where This Shows Up in the Real World
Poisson blending is not a niche technique. The same equation, with the same Jacobi-or-better solver underneath, powers a surprising slice of modern visual computing.
| Domain | What is solved | What changes |
|---|---|---|
| Photoshop healing brush / seamless clone | Δf = Δg on the brushed region | Boundary takes original pixels — invisible mends. |
| Computational photography (HDR, flash/no-flash) | Δf = div(compressed gradients) | Compresses dynamic range while preserving local contrast. |
| Panorama stitching | Δf = Δg with mixed gradients across seams | Removes exposure jumps between adjacent photos. |
| Texture / inpainting | Δf = 0 inside Ω (Laplace equation) | Pure feathering; smoothly fills small holes. |
| Depth completion (lidar) | Δd = guidance derived from RGB edges | Sparse lidar depth is densified using image gradients. |
| Image-to-image translation pre-/post-processing | Poisson blending on the generator output | Pastes generated content into a real photo seamlessly. |
| Medical imaging (data augmentation) | Poisson blending of synthetic lesions | Trains tumour detectors with realistic synthetic positives. |
Once you have a Poisson solver you also essentially have a Laplace solver (just feed it ) — and Laplace turns up in physics simulation (heat, electrostatics), fluid dynamics (pressure projection in incompressible flow), and graph signal processing. The image-blending application is just the most photogenic example.
Summary
- The human eye reads images through gradients, not absolute pixel values. Match gradients on the inside, match values on the boundary, and seams vanish.
- That requirement is exactly the variational problem with a Dirichlet boundary condition. Its Euler-Lagrange equation is — Poisson's equation.
- Discretising with the 5-point Laplacian turns the PDE into a sparse symmetric positive-definite linear system. Jacobi sweeps solve it; on GPU the same code runs in milliseconds.
- Swapping the guidance field reuses the same solver for mixed gradients, texture flattening, HDR tone-mapping, panorama stitching, depth completion, and more.
- Poisson blending was the first vivid example in modern image processing of an idea now everywhere in ML: specify what the answer should look like locally, and let an optimiser fill in the global picture.