Start here
Step 1 · Start here

Segment an image by colour

In this practical, we will generate our own image, identify blue pixels using a BGR range, create a binary mask, and apply that mask to keep only the blue parts.

You will build
  • image.png — the generated test image
  • blue_mask.png — the binary segmentation mask
  • blue_only.png — the masked result
You will learn
  • How OpenCV stores colour in BGR order
  • What lower and upper colour bounds mean
  • How cv2.inRange() creates a mask
  • How cv2.bitwise_and() applies a mask

The complete workflow

Create image
Shapes + colours
→
Choose blue range
Lower + upper BGR
→
Create mask
255 or 0
→
Apply mask
Keep matching pixels
Gestalt connection: this practical implements the idea of similarity. Pixels with similar colour values are grouped into the same foreground region.
Step 2 · Create the test image

Build a controlled image first

Instead of depending on a photograph, we create a predictable scene: three blue objects, one red object, and a white background.

Program 1 · 1_create_demo_image.pycreates image.png
import cv2
import numpy as np

image = np.full((400,600,3), 255, dtype=np.uint8)

# Three blue objects
cv2.circle(image, (120, 100), 50, (255, 0, 0), -1)
cv2.circle(image, (300, 250), 50, (255, 0, 0), -1)
cv2.rectangle(image, (450, 80), (550, 180), (255, 0, 0), -1)

# One red object
cv2.circle(image, (150, 300), 50, (0, 0, 255), -1)

cv2.imwrite("image.png", image)

What the important lines mean

np.full(..., 255)Creates a 400 × 600 colour image where every channel is 255, so the starting image is white.
(255, 0, 0)OpenCV uses BGR order. High Blue + zero Green + zero Red gives pure blue.
(0, 0, 255)Zero Blue + zero Green + high Red gives pure red.
-1 thicknessFills the circle or rectangle instead of drawing only an outline.

Preview of the generated scene

image.pngThree blue shapes + one red circle on white.

Checkpoint

Before segmentation, identify the exact BGR value of the blue objects: [255, 0, 0].

Step 3 · Define a colour range

What counts as blue?

We do not require every pixel to be exactly one BGR value. Instead, we define a minimum and maximum accepted value for each channel.

Lower blue

[200, 0, 0]

Minimum accepted B, G, R values.

Upper blue

[255, 80, 80]

Maximum accepted B, G, R values.

ChannelAccepted rangeMeaning
Blue200–255Blue must be strong.
Green0–80Allow only a little green.
Red0–80Allow only a little red.

Test the rule yourself

Move the B, G, and R sliders. The page will tell you whether that pixel would pass our blue range.

Blue255
Green0
Red0
Important: this is a range chosen for this experiment, not a universal definition of blue. Real photographs usually require testing and tuning.
Step 4 · Create the segmentation mask

Turn colour similarity into labels

cv2.inRange() checks every pixel against the lower and upper BGR bounds and returns a binary mask.

Program 2 · 2_segmentation_by_color.pycreates blue_mask.png
import cv2
import numpy as np

image = cv2.imread("image.png")

# Define the BGR range for blue
lower_blue = np.array([200, 0, 0])
upper_blue = np.array([255, 80, 80])

# Create binary mask
mask = cv2.inRange(image, lower_blue, upper_blue)

cv2.imwrite("blue_mask.png", mask)

The key line

mask = cv2.inRange(image, lower_blue, upper_blue)

Read one pixel

OpenCV looks at its B, G, and R values.

Check all three channels

The pixel must be inside the allowed range for Blue, Green, and Red.

Write a mask value

If it passes, the mask gets 255 (white). Otherwise it gets 0 (black).

Original image → binary mask

Original
Blue maskWhite = selected; black = rejected
Pixel examples

[255,0,0] → white ✅

[0,0,255] → black ❌

[255,255,255] → black ❌

Why is this segmentation?

Every pixel has been assigned to one of two groups: blue foreground or not-blue background.

Step 5 · Apply the mask

Keep only the selected pixels

The mask can now control which pixels from the original image survive in the result.

Program 3 · 3_keep_only_blue_part.pycreates blue_only.png
import cv2
import numpy as np

image = cv2.imread("image.png")

# Blue range
lower_blue = np.array([200, 0, 0])
upper_blue = np.array([255, 80, 80])

# Create mask
mask = cv2.inRange(image, lower_blue, upper_blue)

# Apply mask to original image
result = cv2.bitwise_and(image, image, mask=mask)

cv2.imwrite("blue_only.png", result)

The key operation

result = cv2.bitwise_and(image, image, mask=mask)
Mask valueWhat happens?Result
255Keep the corresponding original pixel.Blue objects remain blue.
0The result pixel becomes zero.That pixel becomes black.

See the three stages together

1. Original
2. Mask
3. Blue only
Why did the white background become black? The background is not inside the blue range, so its mask value is 0. Applying the mask therefore writes [0,0,0] at those positions.
Step 6 · Review

From colour to segmentation

You have completed a full rule-based colour segmentation pipeline.

Generate image
→
Read BGR pixels
→
Choose range
→
Build mask
→
Apply mask

What does a white pixel in blue_mask.png mean?

Why does the background become black after bitwise_and?

One-sentence summary

Colour segmentation groups pixels by numerical colour similarity, stores the grouping in a binary mask, and uses that mask to isolate the desired region.