Turn raw pixels into a handful of meaningful measurements: gradients, textures, colours and patterns.
Feature extraction: converting a raw image into meaningful measurements.
Edges, corners, colours, textures.
SIFT, SURF, ORB descriptors.
Object parts, semantic concepts (CNNs).
Purpose: reduce dimensionality while keeping the important information.
HOG describes local object appearance with gradient orientations.
Use case: pedestrian detection, object recognition.
Example: 64×128 window, 8×8 cells, 2×2 blocks, 9 bins
8×16 cells, stride 1 cell → (8−1) × (16−1) = 7 × 15 blocks
7 × 15 blocks × 4 cells × 9 bins
= 3780 dimensions
from skimage.feature import hog
from skimage import exposure
import cv2
# load image
img = cv2.imread('person.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# compute HOG features
features, hog_image = hog(gray, orientations=9, pixels_per_cell=(8, 8), # ans:
cells_per_block=(2, 2), visualize=True) # ans:
# features: the HOG descriptor vector
print(features.shape) # (N,) 1-D array # ans:
# hog_image: a visualisation of the gradients
hog_image = exposure.rescale_intensity(hog_image, out_range=(0, 255)) # ans:
hog_image = np.uint8(hog_image) # ans:
hog_image looks likeEach cell draws its 9 bins as lines along the edge direction, brighter for stronger bins.
LBP is a texture descriptor.
Properties: rotation invariant (optionally) · grayscale invariant · fast to compute
Use case: texture classification, face recognition.
from skimage.feature import local_binary_pattern
# compute LBP
radius = 3
n_points = 8 * radius
lbp = local_binary_pattern(gray, n_points, radius, method='uniform') # ans:
# method options:
# ans: 'default': the original LBP
# ans: 'uniform': rotation invariant (most common)
# ans: 'var': variance-based
# create a histogram
hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, n_points + 3), # ans:
range=(0, n_points + 2)) # ans:
hist = hist.astype('float') # ans:
hist /= (hist.sum() + 1e-7) # normalise # ans:
Flat regions give noisy codes; edges and textures give stable patterns. The histogram of these codes is the texture descriptor.
Colour histogram: the distribution of colours in an image.
Use case: image retrieval, colour-based matching.
# compute a histogram for each channel
hist_b = cv2.calcHist([img], [0], None, [256], [0, 256]) # ans:
hist_g = cv2.calcHist([img], [1], None, [256], [0, 256]) # ans:
hist_r = cv2.calcHist([img], [2], None, [256], [0, 256]) # ans:
# parameters:
# ans: [img]: the source image
# ans: [0]: the channel index (0=B, 1=G, 2=R)
# ans: None: no mask
# ans: [256]: the number of bins
# ans: [0, 256]: the range
# normalise
hist_b = cv2.normalize(hist_b, hist_b).flatten() # ans:
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# compute a 2-D histogram (Hue vs Saturation)
hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], # ans:
[0, 180, 0, 256]) # ans:
# ans: channels [0, 1] = Hue and Saturation
# ans: bins [180, 256] = 180 for H, 256 for S
# normalise
cv2.normalize(hist, hist, 0, 255, cv2.NORM_MINMAX) # ans:
Haar cascades: pre-trained classifiers for object detection.
Pre-trained models: face, eyes, smile, full body… Advantage: fast, real-time detection.
# load the pre-trained face detector
face_cascade = cv2.CascadeClassifier( # ans:
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') # ans:
# detect faces
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, # ans:
minNeighbors=5, minSize=(30, 30)) # ans:
# faces: an array of [x, y, w, h]
for (x, y, w, h) in faces: # ans:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # ans:
result: bright = good match. Click the photo to pick a new template (dashed).
Template matching: find a template image inside a larger image.
TM_CCOEFF_NORMED: correlation coefficient (best)TM_SQDIFF_NORMED: squared differenceTM_CCORR_NORMED: cross-correlationLimitation: not scale- or rotation-invariant.
# load the template
template = cv2.imread('template.jpg', 0)
w, h = template.shape[::-1]
# template matching
result = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED) # ans:
# find the location of the best match
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # ans:
# for CCOEFF_NORMED, higher is better
top_left = max_loc # ans:
bottom_right = (top_left[0] + w, top_left[1] + h) # ans:
cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2) # ans:
Answer first, then press → to check.
What is HOG?
Histogram of Oriented Gradients: it describes object appearance.
What does LBP encode?
A texture pattern, by comparing a pixel with its neighbours.
What is a colour histogram?
The distribution of colours in an image.
What are Haar cascades?
Pre-trained classifiers built on Haar-like features.
What is the limitation of template matching?
It is not scale- or rotation-invariant.
What is a typical HOG cell size?
8×8 or 16×16 pixels.
What is the LBP output range?
0–255 (8-bit patterns from 8 neighbours).
Why normalise histograms?
To make them independent of image size: they sum to 1.
What is the "cascade" in Haar cascades?
A series of weak classifiers that reject non-objects quickly.
What is the best template matching method?
TM_CCOEFF_NORMED (normalised correlation coefficient).
# compute HOG features
from skimage.feature import hog
features, hog_img = hog(gray, orientations=9, pixels_per_cell=(8, 8), # ans:
cells_per_block=(2, 2), visualize=True) # ans:
# compute LBP
from skimage.feature import local_binary_pattern
lbp = local_binary_pattern(gray, 24, 3, method='uniform') # ans:
# colour histogram
hist_b = cv2.calcHist([img], [0], None, [256], [0, 256]) # ans:
hist_b = cv2.normalize(hist_b, hist_b).flatten() # ans:
# face detection with a Haar cascade
face_cascade = cv2.CascadeClassifier( # ans:
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') # ans:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(30, 30)) # ans:
for (x, y, w, h) in faces: # ans:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # ans:
# template matching
template = cv2.imread('template.jpg', 0)
w, h = template.shape[::-1]
result = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED) # ans:
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # ans:
top_left = max_loc # ans:
cv2.rectangle(img, top_left, (top_left[0]+w, top_left[1]+h), # ans:
(0, 255, 0), 2) # ans:
# HSV histogram
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], # ans:
[0, 180, 0, 256]) # ans: