Keypoint + descriptor + matcher
- Detect interesting points (corners, blobs).
- Compute a descriptor per keypoint (sometimes using a canonical orientation and scale).
- Match descriptors with a distance (L2 for float vectors, Hamming for binary).
- Filter false matches (ratio test, geometric checks with homography or fundamental matrix).
| Method | Descriptor | Speed | Notes |
|---|---|---|---|
| Shi-Tomasi / goodFeaturesToTrack | None (points only) | Fast | Great for optical flow and tracking |
| FAST | Optional (e.g. ORB) | Very fast | Corner score on circle of pixels |
| ORB | Binary (BRIEF-like) | Fast | Free; rotation-aware |
| SIFT | 128-D float | Slower | Strong invariance; check build/license |
Shi-Tomasi corners — goodFeaturesToTrack
Returns up to maxCorners 2D points with high corner response—no descriptor. Often used with cv2.calcOpticalFlowPyrLK for video tracking.
import cv2
import numpy as np
gray = cv2.imread("room.jpg", cv2.IMREAD_GRAYSCALE)
pts = cv2.goodFeaturesToTrack(
gray, maxCorners=200, qualityLevel=0.01, minDistance=10, blockSize=7)
if pts is not None:
for p in pts:
x, y = p.ravel()
cv2.circle(gray, (int(x), int(y)), 3, 255, -1)
Tighter quality, larger separation
pts2 = cv2.goodFeaturesToTrack(
gray, maxCorners=80, qualityLevel=0.05, minDistance=20, blockSize=9)
FAST keypoints
cv2.FastFeatureDetector_create (or constructor) flags corners by comparing intensity on a Bresenham circle. Very fast; combine with ORB or BRIEF for descriptors.
import cv2
gray = cv2.imread("scene.jpg", cv2.IMREAD_GRAYSCALE)
fast = cv2.FastFeatureDetector_create(threshold=40, nonmaxSuppression=True)
kps = fast.detect(gray, None)
vis = cv2.drawKeypoints(gray, kps, None, color=(255, 0, 0),
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
ORB — detect, compute, draw
ORB builds oriented FAST keypoints and binary descriptors. Default is 256 bits; Hamming distance applies. Free to use in typical OpenCV builds.
import cv2
img = cv2.imread("book.jpg", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create(nfeatures=500, scaleFactor=1.2, nlevels=8)
kp, des = orb.detectAndCompute(img, None)
img_bgr = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
img_kp = cv2.drawKeypoints(img_bgr, kp, None, color=(0, 255, 0),
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
des is None if no keypoints; shape (N, 32) for 256-bit ORB (8 bits per byte × 32).
Brute-force matching
BFMatcher with NORM_HAMMING for ORB. Use knnMatch and Lowe’s ratio test to reject ambiguous pairs.
import cv2
im1 = cv2.imread("obj1.jpg", cv2.IMREAD_GRAYSCALE)
im2 = cv2.imread("obj2.jpg", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create(800)
k1, d1 = orb.detectAndCompute(im1, None)
k2, d2 = orb.detectAndCompute(im2, None)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False)
pairs = bf.knnMatch(d1, d2, k=2)
good = []
for m, n in pairs:
if m.distance < 0.75 * n.distance:
good.append(m)
vis = cv2.drawMatches(im1, k1, im2, k2, good[:50], None, flags=2)
flags=2 is NOT_DRAW_SINGLE_POINTS (hide unmatched keypoints). On newer OpenCV you can write cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS.
Cross-check (mutual nearest neighbor)
bf_strict = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf_strict.match(d1, d2)
matches = sorted(matches, key=lambda m: m.distance)
Practical tips
- Work in grayscale for classical detectors unless you use a color-aware variant.
- Resize very large images before matching—scale affects keypoint count and runtime.
- For panorama stitching, follow matching with RANSAC homography to kill outliers (covered in geometry topics).
- SIFT may live in
opencv-contrib-pythonand has patent/history considerations; ORB is the default free baseline.
Takeaways
goodFeaturesToTrack→ points only; pair with optical flow.- ORB gives keypoints + binary descriptors + fast Hamming match.
- Use ratio test or crossCheck before trusting correspondences.
Quick FAQ
minDistance at detection, or enforce geometric consistency (homography/fundamental matrix inliers).