Skip to content

Select and group a colour

CookbookRegions and masks

Select an HSV colour range, close small holes and outline matching colour regions.

Try the recipe ↓

The pipeline

VISUAL WALKTHROUGHRegions and masks
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 / 2Hue channel448 × 320
Hue channel: Grayscale displays the numeric hue code, 0 to 179. Brightness here represents hue, not scene brightness; low-saturation pixels have unreliable hue.

Grayscale displays the numeric hue code, 0 to 179. Brightness here represents hue, not scene brightness; low-saturation pixels have unreliable hue.

STEP 01 / 03

Convert BGR to HSV

Explore the algorithm →
cvtColorCOLOR_BGR2HSV

Separate hue from brightness.

Receives
An 8-bit BGR image.
Passes to the next step
An 8-bit HSV image; hue is encoded on 0..179.

Why this step? HSV gives separate channels for hue, saturation and value. A hue interval is easier to specify than three independent BGR intervals, while a saturation floor excludes gray pixels whose hue is unstable or uninformative.

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

Colour can separate objects that overlap in brightness. Select a colour in a representation that separates hue from intensity, then convert scattered selected pixels into contiguous regions.

  1. 01

    Convert BGR to HSV

    cvtColorCOLOR_BGR2HSV

    HSV gives separate channels for hue, saturation and value. A hue interval is easier to specify than three independent BGR intervals, while a saturation floor excludes gray pixels whose hue is unstable or uninformative.

    Receives
    An 8-bit BGR image.
    Passes on
    An 8-bit HSV image; hue is encoded on 0..179.
  2. 02

    Select the colour interval

    inRange

    Range testing makes the colour definition explicit. This lab selects one contiguous hue interval and a minimum saturation while allowing the full value range. Every passing pixel becomes white; all others become black.

    Receives
    HSV pixels and lower/upper bounds.
    Passes on
    A binary colour-selection mask.
  3. 03

    Clean and group the selection

    morphologyExMORPH_CLOSEconnectedComponentsWithStats

    Closing fills small holes and joins small gaps within the selection. Component statistics then turn selected pixels into area-filtered boxes. They group by connectivity, so adjacent objects of the same colour may merge.

    Receives
    The colour mask.
    Passes on
    Colour-region boxes and component IDs.

Tune and diagnose

Choose the parameters

Use the pixel inspector and HSV guide to choose the hue interval. Raise minimum saturation to reject pale or gray regions. Use a small closing kernel. A red interval spanning the hue wrap needs two inRange masks joined with bitwise_or in your own chain.

Read the result

Inspect the raw mask before cleanup: morphology cannot rescue an incorrect colour range. Illumination, reflections and white balance can shift colours beyond fixed bounds.

Try it with your images

Choose your own image or start with the built-in sample. 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 an HSV colour range, close small holes and outline matching colour regions.

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

HSV hue is 0..179. Red may wrap across zero and need two intervals; this recipe demonstrates one contiguous interval.

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 {
  COLOR_BGR2HSV,
  CV_8UC3,
  MORPH_CLOSE,
  MORPH_ELLIPSE,
  Mat,
  connectedComponentsWithStats,
  cvtColor,
  getStructuringElement,
  inRange,
  morphologyEx
} from '@banou/opencv-wasm'

// The engine is already initialized; image is an 8-bit BGR Mat.
// "using" releases native handles at scope exit; inspect or copy outputs before then.

// 1. Allocate the alternative colour representation and the resulting selection mask.
using hsv = new Mat(), mask = new Mat()
// HSV separates hue from saturation and brightness, making a colour interval easier to choose.
cvtColor(image, hsv, COLOR_BGR2HSV)
// Lower bounds are H=25, S=40, V=0. CV_8UC3 stores three 8-bit channels.
// The fourth scalar entry is unused because this matrix has only three channels.
using low = new Mat(image.rows, image.cols, CV_8UC3, [25, 40, 0, 0])
// Upper bounds are H=95, S=255, V=255; 8-bit HSV hue ranges from 0 to 179.
// Both bound Mats are filled with the same bound triplet at every pixel.
using high = new Mat(image.rows, image.cols, CV_8UC3, [95, 255, 255, 0])
// 2. Select pixels within all three channel intervals, inclusively.
// The saturation floor excludes grayish pixels with poorly defined hue.
inRange(hsv, low, high, mask)
// Choose a small 3x3 ellipse for filling gaps in the selected colour regions.
using kernel = getStructuringElement(MORPH_ELLIPSE, { width: 3, height: 3 })
// Closing fills small holes and connects nearby selected pixels.
morphologyEx(mask, mask, MORPH_CLOSE, kernel)
// 3. Allocate component IDs, five-column region statistics and x,y centroids.
using labels = new Mat(), stats = new Mat(), centres = new Mat()
// Group the cleaned mask into connected colour regions. Label 0 is background;
// the full lab uses each region's area and bounds to filter and draw boxes.
connectedComponentsWithStats(mask, labels, stats, centres)

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