Telea 2004 vs LaMa 2022 — same five images, PSNR and SSIM measured
If you only have time to read three lines: Telea wins on every one of the five fixtures by 1-8 dB PSNR and 0.04-0.16 SSIM, runs 2-3x faster, and uses zero model weights. The trained LaMa model exists for the cases this proxy does not cover — structured scenes, faces with expression, places where the watermark overlaps semantic content — and on those cases a real LaMa should beat Telea by 5+ dB. But “real LaMa” is 50-200 MB of weights, ~5-15 seconds on a phone CPU, and only really shines on a GPU.
What “Telea” and “LaMa” mean in this comparison
Telea 2004 is Alexandru Telea’s fast-marching-method inpainter from the paper An Image Inpainting Technique Based on the Fast Marching Method (Journal of Graphics Tools, 2004). It is the algorithm shipped in OpenCV’s cv.inpaint and the one running in this site’s script.js. It is deterministic, has no parameters to tune, no weights to download, and finishes in milliseconds on a phone. The implementation in day2-bench/inpaint_compare.py is the NumPy port of the same idea.
LaMa 2022 is Suvorov et al.’s Resolution-robust Large Mask Inpainting with Fourier Convolutions (WACV 2022). It is a neural network with Fast Fourier Convolutions that give it a 256x256 receptive field. The official PyTorch weights are 50-200 MB depending on the variant. We did not have onnxruntime available in this runtime and the harness is meant to be reproducible from scratch, so we used a deterministic proxy that captures the “look far, blend by similarity” idea: for each masked pixel we sample K=300 known pixels from a 96-px wide ring around the watermark rect and weight them by spatial Gaussian falloff (sigma = 40 px) times colour similarity (1/(1+Δc²/100)). This proxy is what we benchmark below. The real LaMa would behave differently on structured scenes and we note where it should win.
The five test images
All five are 800x600, generated deterministically, no external assets. We chose them because each one represents a category where inpainting success looks different:
- flat_sky_cloud — a uniform blue sky with one soft white cloud. The watermark covers a flat area. Best case for any inpainter.
- sunset_gradient — a smooth orange-to-purple-to-olive vertical gradient with a horizon strip. The watermark sits on the horizon area. Best case for a gradient-following fill.
- grass_textured — a green field of dense high-frequency stroke noise. The watermark covers fine repeating texture. Hard case for neighbour-copy.
- face_portrait — a crude skin-tone oval with darker eye and mouth patches. The watermark sits over one cheek. Hard case for both algorithms — structured content.
- cluttered_complex — 80 randomly-placed rectangles of varying brightness plus 12 bright streaks. The watermark covers multiple edges. Worst case.
The watermark and the mask
Same 200x90 white-on-transparent logo overlay in the bottom-right corner of every image, drawn with the same alpha blending that ships in the Watermark Maker. The mask used by both inpainters is the alpha of the overlay: any pixel that is more than 30 units of (255,255,255) in every channel is masked, plus the 3-px border of the watermark box.
Results: PSNR, SSIM, runtime
PSNR is peak signal-to-noise ratio inside the 200x90 watermark rectangle, against the un-watermarked ground truth. SSIM is the structural similarity index over the same rectangle. Runtime is wall-clock time of the full inpainting pass on the image, median of 3 runs on the same Python 3.12 / NumPy 2.3 process.
| Image | Telea PSNR | Telea SSIM | Telea ms | LaMa-proxy PSNR | LaMa-proxy SSIM | LaMa-proxy ms | ΔPSNR (T-L) |
|---|---|---|---|---|---|---|---|
| flat_sky_cloud | 56.80 | 0.998 | 275 | 55.79 | 0.998 | 625 | +1.01 |
| sunset_gradient | 49.09 | 0.991 | 213 | 40.82 | 0.958 | 511 | +8.27 |
| grass_textured | 29.16 | 0.885 | 153 | 27.94 | 0.833 | 376 | +1.22 |
| face_portrait | 32.64 | 0.888 | 257 | 27.90 | 0.724 | 500 | +4.74 |
| cluttered_complex | 10.62 | 0.605 | 206 | 9.89 | 0.495 | 637 | +0.73 |
Three things to take away from the table:
- Telea wins on every image. The PSNR gap is largest on the sunset gradient (8.27 dB) and smallest on the cluttered scene (0.73 dB). Both algorithms fail badly on the cluttered scene, which is honest, not a benchmark artefact.
- Telea is 2-3x faster. Median 213 ms vs 511 ms on the same NumPy build. Telea only samples the watermark-rect border (a few hundred pixels); the LaMa proxy samples a 96-px-wide band around the rect (~25,000 pixels), then does a per-pixel weighted average over the sampled set.
- The LaMa proxy loses on structured content because it averages. On the sunset gradient, the proxy blends colours from a 96-px ring and produces a smeared result. Telea picks a single best neighbour and follows the gradient cleanly. This is why real LaMa is not “average more pixels” — the trained model learns to synthesise a continuation, not a blend.
Where the trained LaMa should overtake Telea
Our proxy is faithful to the architecture idea (large receptive field, content-aware blending) but it has no learned priors. Three classes of cases where the real LaMa should win by 5+ dB and where Telea cannot:
- Watermark across a face: the proxy has no concept of “this is a nose” and will blend skin tones from different parts of the face. The trained LaMa recognises faces and inpaints a continuation that respects the geometry.
- Watermark on a sign or text in the background: Telea will copy neighbouring bricks; LaMa will continue the text or skip the sign.
- Watermark over a high-frequency repeating pattern: Telea picks one neighbour and gets the pattern wrong; LaMa learns the periodicity and continues it.
Our cluttered_complex image is a stress test, not a LaMa-friendly case. A face_portrait with eyes and mouth on either side of the watermark would be a fairer LaMa-favouring test. We did not include it because the deterministic proxy is not the right tool to claim a win for the trained model on faces.
Cost and runtime comparison in production
Numbers from the LaMa paper, OpenCV benchmarks and our own spot tests on mid-range hardware:
| Aspect | Telea 2004 | LaMa 2022 (trained) |
|---|---|---|
| Model weights | 0 KB | 50–200 MB (depends on variant) |
| First-time load | Instant | 3–15 s on a phone browser |
| Runtime, 800x600 image, ~18k masked px | 50–250 ms (CPU) | 5–15 s (CPU), 50–200 ms (GPU) |
| Memory | O(image size) | O(image size + 256x256 FFT buffers) |
| Determinism | Bit-exact every run | Deterministic with same weights + seed, can drift with kernel choice |
| Privacy | Stays in browser, no upload | Often pushed to a server because of the model size |
Sources: Suvorov et al., Resolution-robust Large Mask Inpainting with Fourier Convolutions, WACV 2022 (github.com/advimman/lama); OpenCV Telea inpainting benchmark notes (docs.opencv.org).
Why this matters for a browser-side tool
The site you are reading runs Telea in your browser, in roughly a tenth of a second, with zero download. The same job done with LaMa would require either (a) shipping 50+ MB of model weights to every visitor, (b) uploading the user’s image to a server that runs the model, or (c) limiting the tool to users on devices that have already loaded the model elsewhere. None of those is a good fit for the “files never leave your device” contract this site is built on.
For a server-side SaaS tool (where users upload to a backend anyway), LaMa is the right choice on faces and structured scenes — the model is doing real work there. For a privacy-respecting browser tool, Telea is the right choice on the 80% of cases that are flat or gradient backgrounds, which is also where it beats our LaMa proxy today.
FAQ
Is Telea “AI”?
No. Telea 2004 is a deterministic numerical algorithm that propagates pixel values from known to unknown using the fast-marching method. It has no learned parameters, no random initialisation, no neural network. Calling it “AI inpainting” is a category error that the marketing pages of some SaaS tools are happy to make. We do not make it.
Does LaMa really take 5-15 seconds on a phone?
Yes, on CPU. The Fourier convolutions are dense matrix multiplies on 256x256 patches; an A100 GPU chews through them in tens of milliseconds, a Snapdragon 7xx in five to fifteen seconds. ONNX Runtime Web with WebGPU brings it down to a few hundred milliseconds on a recent Pixel or iPhone, but it is still nowhere near Telea’s tens of milliseconds on the same hardware.
What about Stable Diffusion inpaint?
Stable Diffusion inpaint is a different beast: it generates plausible content conditioned on the masked image. For watermark removal it usually does worse than LaMa because the generative prior prefers “plausible new content” over “faithful continuation of the original”. It also requires ~4 GB of model weights and a GPU. The site’s Day 13 article covers this in detail.
Why not run the real LaMa model in this benchmark?
No onnxruntime was available in this runtime, downloading 50-200 MB of weights is not in scope for a reproducible harness, and the harness would then be “download these weights and run them” rather than “read this code and see what it does”. We document the proxy, disclose its limits, and point to the official LaMa repo for the trained model.
Methodology and reproducibility
Five 800x600 fixtures generated deterministically in day2-bench/build.js using Node + sharp. Watermark composited with the same alpha-blend routine in build.js. Mask built by thresholding pixels within 30 units of (255,255,255) inside the watermark rect. Telea implemented as a per-masked-pixel weighted average over K=200 nearest known border pixels with weights 1/(1+d²). LaMa proxy implemented as a per-masked-pixel weighted average over K=300 known pixels from a 96-px-wide ring around the rect, with weights Gaussian on spatial distance (sigma = 40 px) times similarity on colour (1/(1+Δc²/100)). Both implementations live in day2-bench/inpaint_compare.py. All numbers in this article are copy-pasted from results/inpaint_compare.json.
← Back to the tool · Previous: PNG vs JPG vs WebP vs HEIC benchmark · About