Skip to content

Keep a subject sharp

CookbookPhotography

Select the subject, separate it with GrabCut, then blend it over a blurred version of the image.

Try the recipe ↓

The pipeline

VISUAL WALKTHROUGHPhotography
Follow the images, then read what passes to the next algorithm.
STARTING IMAGEOriginal sample448 × 320
The unchanged source image. All steps use this same example.

The unchanged source image. All steps use this same example.

STEP 01 · 1 / 2Initial region448 × 320
Initial region: Inside the orange rectangle is possible foreground. Outside supplies known background to initialize GrabCut.

Inside the orange rectangle is possible foreground. Outside supplies known background to initialize GrabCut.

STEP 01 / 03

Separate the subject

Explore the algorithm →
grabCut

GrabCut creates a foreground mask from the rectangle.

Receives
The image and a rectangle around the subject.
Passes to the next step
A binary subject mask.

Why this step? GrabCut estimates a subject mask from foreground/background colour models and the rectangle constraint. Retaining definite and probable foreground yields a binary selection that controls where sharp pixels survive.

Actual OpenCV 5.0.0 results on the illustrated sample, using the lab’s default algorithm settings. Masks, overlays and normalized fields are labelled previews; the data contracts above describe what the algorithms really exchange. Try this chain with your images ↓

Why this chain works

Separating where an effect applies from how the effect is computed makes many image edits composable. Compute a subject mask, create a blurred alternative image, then use the mask to choose between the two.

  1. 01

    Separate the subject

    grabCut

    GrabCut estimates a subject mask from foreground/background colour models and the rectangle constraint. Retaining definite and probable foreground yields a binary selection that controls where sharp pixels survive.

    Receives
    The image and a rectangle around the subject.
    Passes on
    A binary subject mask.
  2. 02

    Blur the surroundings

    GaussianBlur

    Gaussian blur creates a smooth alternative layer. Computing the blur over the whole image gives each background pixel a complete neighbourhood, but it also means subject colours can spread into that layer near boundaries.

    Receives
    The full colour image.
    Passes on
    A blurred colour layer with the same dimensions as the original.
  3. 03

    Blend with the mask

    GaussianBlurAlpha-weighted compositing

    A small blur of the mask creates a gradual transition. For each channel the lab computes alpha × original + (1 - alpha) × blurred. The mask, not the blur algorithm, determines which regions stay sharp.

    Receives
    The original, blurred layer and subject mask.
    Passes on
    A composite with the selected foreground sharp and its surroundings blurred.

Tune and diagnose

Choose the parameters

Tune the rectangle and GrabCut mask before the blur strength. Sigma sets the spatial blur scale in processed pixels. Strong blur makes segmentation mistakes more visible, especially around thin structures.

Read the result

Use the mask and blurred-layer stages to distinguish segmentation errors from blur behaviour. Subject-colour spill and mask mistakes can produce halos; this simple composite is not a depth-aware lens simulation.

Try it with your images

Choose your own image or start with the built-in sample. Use “Draw a region” and drag a rectangle on the input, or enter its coordinates. Run the recipe, then use the stage buttons to inspect intermediate results without rerunning it.

YOUR IMAGE · REAL OPENCV

Experiment at pixel level

Open full lab ↗

Select the subject, separate it with GrabCut, then blend it over a blurred version of the image.

The engine loads on your first run. Your images stay in this browser.

Input448 × 320
OutputWaiting for a result

Scroll over either image to zoom at the pointer. Use the scrollbars to pan both views over the same relative area. Zoom is relative to the input; pixel coordinates belong to each image. Warps can change scene correspondence.

Pixel inspector RGBA · native values · matched scale · 9 × 9 output pixels
Hover to inspect. Click to pin a pixel.
Input
Select a pixel

Output
Select a pixel

Sample models and licenses

Assumptions and limits

Imperfect masks can blur subject edges or retain background patches. This is mask-based blur, not an optical depth-of-field simulation.

TypeScript core chain

Initialize the shared engine once with await initOpenCV(), then use these named imports. In a bundled browser app, pass the WASM URL as shown in the quickstart. image is an 8-bit BGR Mat from that same engine; paired recipes receive an equally sized nextImage. Region recipes use an in-bounds pixel rect. Read outputs before the using scope ends. See matrix ownership.

import {
  GC_FGD,
  GC_INIT_WITH_RECT,
  GC_PR_FGD,
  GaussianBlur,
  Mat,
  grabCut
} from '@banou/opencv-wasm'

// The engine is already initialized; image is an 8-bit BGR Mat.
// rect is an in-bounds { x, y, width, height } rectangle in processed pixels.
// "using" releases native handles at scope exit; inspect or copy outputs before then.

// 1. Allocate GrabCut labels and its background/foreground colour-model buffers.
// Those model Mats hold learned statistics, not preview images.
using mask = new Mat(), background = new Mat(), foreground = new Mat()
// Initialize from rect: outside is background, inside is possible foreground.
// Run three refinement iterations. Keep the entire subject inside the rectangle.
grabCut(image, mask, rect, background, foreground, 3, GC_INIT_WITH_RECT)
// 2. Reuse the label Mat as a binary mask for compositing.
for (let i = 0; i < mask.data.length; i++) {
  // Read this pixel's four-state GrabCut label before replacing it.
  const label = mask.data[i]
  // White (255) retains definite/probable foreground; black (0) means background.
  mask.data[i] = label === GC_FGD || label === GC_PR_FGD ? 255 : 0
}
// 3. Allocate a replacement layer with blurred surroundings.
using output = new Mat()
// Use sigma 8 pixels. A zero kernel size lets OpenCV choose it from sigma.
// Blur the whole image first so neighbourhoods extend beyond the subject boundary.
GaussianBlur(image, output, { width: 0, height: 0 }, 8)
// Restore original sharp pixels wherever the foreground mask is nonzero.
// This is a hard selection; the full lab uses a softened alpha-weighted transition.
image.copyTo(output, mask)

The snippets isolate the core operations. The complete runnable recipes also include validation, filtering, overlays, intermediate previews and resource cleanup.