0%
OpenCV with Python: Introduction to Image Processing

OpenCV with Python: Introduction to Image Processing

Get started with image processing using OpenCV and Python: color spaces, filters, edges, thresholding, face detection, and webcam stream capture.

I

InSkillCoach

· min

OpenCV with Python: Introduction to Image Processing

OpenCV (Open Source Computer Vision Library) is the go-to library for computer vision. With its Python interface, you can read, transform, and analyze images and videos in just a few lines: filtering, edge detection, face recognition, and real-time webcam stream processing. This guide covers the fundamental operations that every image processing project relies on, with complete, runnable examples.

Installation

OpenCV is installed through the opencv-python package, which includes the main modules:

pip install opencv-python

# If you need the additional modules (contrib)
pip install opencv-contrib-python

Verification:

import cv2

print(cv2.__version__)
# 4.10.0 (or a more recent version)

Note that the module is imported as cv2, regardless of the name of the installed package. OpenCV represents images as NumPy arrays, and NumPy is installed automatically as a dependency.

Reading, Displaying, and Saving an Image

import cv2

# Read an image from disk
image = cv2.imread("photo.jpg")

# imread does NOT raise an exception if the file is not found: it returns None
if image is None:
    raise FileNotFoundError("Impossible de lire photo.jpg")

# An image is a NumPy array: (height, width, channels)
print(image.shape)
# (720, 1280, 3)
print(image.dtype)
# uint8 (values from 0 to 255)

# Display in a window
cv2.imshow("Ma photo", image)
cv2.waitKey(0)            # wait for a key press (0 = indefinitely)
cv2.destroyAllWindows()   # close all windows

# Save (the format is inferred from the extension)
cv2.imwrite("photo_copie.png", image)

The if image is None check is essential: forgetting it is the number one cause of cryptic errors for beginners.

Color Spaces: The BGR Trap

Watch out for the classic trap: OpenCV loads images in BGR (blue, green, red) rather than RGB. If you display an OpenCV image with Matplotlib without converting it, the reds and blues are swapped.

import cv2

image = cv2.imread("photo.jpg")

# BGR to RGB conversion (needed for Matplotlib, Pillow, etc.)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Convert to grayscale (a single channel)
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print(gris.shape)
# (720, 1280): no more color dimension

# Convert to HSV (hue, saturation, value)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

The HSV space is valuable for isolating a color, because hue is independent of brightness:

import numpy as np

# Isolate the blue areas of the image
borne_basse = np.array([100, 100, 50])    # minimum hue, saturation, value
borne_haute = np.array([130, 255, 255])
masque = cv2.inRange(hsv, borne_basse, borne_haute)

# Apply the mask: only the blue areas remain visible
resultat = cv2.bitwise_and(image, image, mask=masque)

Resizing and Cropping

import cv2

image = cv2.imread("photo.jpg")
hauteur, largeur = image.shape[:2]

# Resize to specific dimensions
petite = cv2.resize(image, (640, 360))

# Resize by a factor (here: half) while preserving the aspect ratio
moitie = cv2.resize(image, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

# Cropping: an image is a NumPy array, so we slice it
# Syntax: image[y1:y2, x1:x2]
recadree = image[100:400, 200:600]

cv2.imwrite("recadree.jpg", recadree)

When shrinking an image, cv2.INTER_AREA interpolation gives the best result; when enlarging it, prefer cv2.INTER_CUBIC or cv2.INTER_LINEAR.

Drawing on an Image

Drawing functions modify the image in place. Colors are in BGR:

import cv2

image = cv2.imread("photo.jpg")

# Rectangle: opposite corners, BGR color, thickness (-1 = filled)
cv2.rectangle(image, (50, 50), (250, 200), (0, 255, 0), 2)

# Circle: center, radius
cv2.circle(image, (400, 300), 60, (255, 0, 0), 3)

# Line
cv2.line(image, (0, 0), (640, 360), (0, 0, 255), 2)

# Text
cv2.putText(image, "Detection", (50, 40),
            cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2)

cv2.imwrite("annotee.jpg", image)

Blurs and Filters

Gaussian blur is the basic building block of preprocessing: it reduces noise before edge detection or thresholding.

import cv2

image = cv2.imread("photo.jpg")

# Gaussian blur: the (15, 15) kernel must have odd dimensions
flou = cv2.GaussianBlur(image, (15, 15), 0)

# Median blur: very effective against "salt-and-pepper" noise
median = cv2.medianBlur(image, 5)

# Bilateral filter: smooths flat areas while preserving edges
bilateral = cv2.bilateralFilter(image, 9, 75, 75)

The larger the kernel, the stronger the blur. A (5, 5) kernel is generally enough for preprocessing.

Edge Detection: Canny and findContours

The Canny algorithm detects edges (areas of sharp intensity change):

import cv2

image = cv2.imread("photo.jpg")
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Reduce noise before detection
gris_flou = cv2.GaussianBlur(gris, (5, 5), 0)

# Canny: the two thresholds control sensitivity
# Below 50: rejected; above 150: definite edge
contours_canny = cv2.Canny(gris_flou, 50, 150)
cv2.imwrite("canny.jpg", contours_canny)

findContours goes further: it extracts contours as lists of points, which you can use to measure, count, or box objects:

# findContours expects a binary image (result of Canny or thresholding)
contours, _ = cv2.findContours(contours_canny,
                               cv2.RETR_EXTERNAL,      # external contours only
                               cv2.CHAIN_APPROX_SIMPLE)  # point compression

print(f"Nombre de contours trouvés : {len(contours)}")

# Draw a box around each sufficiently large contour
for contour in contours:
    aire = cv2.contourArea(contour)
    if aire > 500:  # ignore noise
        x, y, w, h = cv2.boundingRect(contour)
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imwrite("objets_detectes.jpg", image)

Thresholding

Thresholding turns a grayscale image into a binary (black and white) image, a prerequisite step for many analyses:

import cv2

gris = cv2.imread("document.jpg", cv2.IMREAD_GRAYSCALE)

# Simple thresholding: any pixel > 127 becomes 255, otherwise 0
_, binaire = cv2.threshold(gris, 127, 255, cv2.THRESH_BINARY)

# Otsu's thresholding: the optimal threshold is computed automatically
_, otsu = cv2.threshold(gris, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# Adaptive thresholding: threshold computed locally, ideal for a
# document photographed under uneven lighting
adaptatif = cv2.adaptiveThreshold(gris, 255,
                                  cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                  cv2.THRESH_BINARY, 11, 2)

Face Detection with Haar Cascades

OpenCV ships with pre-trained classifiers for detecting faces, eyes, or smiles. Here is a complete face detector:

import cv2

# Load the pre-trained classifier bundled with OpenCV
cascade_visages = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

image = cv2.imread("groupe.jpg")
if image is None:
    raise FileNotFoundError("Impossible de lire groupe.jpg")

# Detection works on a grayscale image
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# detectMultiScale returns one rectangle (x, y, width, height) per face
visages = cascade_visages.detectMultiScale(
    gris,
    scaleFactor=1.1,    # image reduction at each pass (10%)
    minNeighbors=5,     # higher = fewer false positives
    minSize=(40, 40),   # minimum face size in pixels
)

print(f"{len(visages)} visage(s) détecté(s)")
# 3 visage(s) détecté(s)

# Draw a box around each face
for (x, y, w, h) in visages:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imwrite("visages_detectes.jpg", image)
cv2.imshow("Visages", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Adjust minNeighbors if detection produces too many false positives (increase it) or misses faces (decrease it). Haar cascades are fast but sensitive to orientation: they mostly detect front-facing faces.

Reading a Video Stream or the Webcam

A video is just a sequence of images: you read the frames one by one in a loop. The following example detects faces live from the webcam:

import cv2

cascade_visages = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

# 0 = first webcam; replace with "video.mp4" for a file
capture = cv2.VideoCapture(0)
if not capture.isOpened():
    raise RuntimeError("Impossible d'ouvrir la webcam")

while True:
    # ret is False if reading fails (end of file, webcam unplugged)
    ret, frame = capture.read()
    if not ret:
        break

    gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    visages = cascade_visages.detectMultiScale(gris, 1.1, 5, minSize=(40, 40))

    for (x, y, w, h) in visages:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv2.imshow("Webcam - appuyez sur Q pour quitter", frame)

    # waitKey(1): wait 1 ms; quit if the Q key is pressed
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Release the webcam and close the windows
capture.release()
cv2.destroyAllWindows()

Going Further: The DNN Module

Haar cascades date back to 2001. For more accurate and more robust detection, OpenCV includes a deep neural network module (cv2.dnn) that can load pre-trained models in ONNX, TensorFlow, or Caffe formats:

import cv2

# Example: load a detection model in ONNX format
# net = cv2.dnn.readNetFromONNX("modele_detection.onnx")

Natural next steps after this guide include DNN-based face detection (the YuNet model built into OpenCV via cv2.FaceDetectorYN), object detection with YOLO, and pose estimation. The principle remains the same as in this guide: read the image, preprocess it, analyze it, and draw the result.

Best Practices

  • Always check the return value of imread and capture.read(): OpenCV returns None or False instead of raising an exception.
  • Never forget the BGR/RGB conversion when exchanging images with Matplotlib, Pillow, or a deep learning model.
  • Preprocess before analyzing: grayscale followed by Gaussian blur almost always improves Canny and thresholding.
  • Release resources: call capture.release() and cv2.destroyAllWindows() at the end of video processing.
  • Work on copies (image.copy()) when drawing, so the original stays intact.
  • Filter by area (cv2.contourArea) after findContours to eliminate noise.

Conclusion

You now have the foundations of image processing with OpenCV: loading and saving, color spaces (and the BGR trap), resizing, filters, edge detection with Canny and findContours, thresholding, face detection with Haar cascades, and real-time webcam stream processing. These operations cover the vast majority of the needs of a first computer vision project.

To go deeper, check out the official OpenCV documentation and its Python tutorials, then explore the DNN module to move on to deep learning-based detection.

InSkillCoach

About InSkillCoach

Training and technology expert

Coach specializing in advanced technologies and AI, backed by GNeurone Inc.

Certifications:

  • AWS Certified Solutions Architect – Professional
  • Google Cloud certifications
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
643
308

Comments

Comments are powered by GitHub Discussions

Sign in with GitHub to join the discussion

Link copied!