机器视觉 | 目标识别跟踪 - 基于特征法
基于特征的方法在计算机视觉和图像处理中占据重要地位,尤其在目标检测与跟踪领域。这种方法的核心在于通过特征提取和特征匹配来实现对目标的识别和定位。以下是对基于特征的方法的详细阐述:
一、特征提取
特征提取是基于特征方法的第一步,其目的是从图像或视频数据中提取出具有代表性、区分度高的特征信息。这些特征可以是手动设计的,也可以是通过算法自动提取的。
1. 手动设计的特征
颜色特征、纹理特征、边缘特征。
2. 自动提取的特征 (SIFT SURF ORB HOG LBP等)
- SIFT(Scale-Invariant Feature Transform)特征:一种尺度不变特征变换算法,用于提取图像的局部特征。SIFT特征具有旋转、尺度缩放、亮度变化等不变性,对于视角变化、仿射变换和噪声也保持一定程度的稳定性。

代码示例: (为提高精度,在sift前叠加了一层颜色阈值识别划分roi区 )
点击查看代码
#sift 在视频中识别匹配
import cv2
import numpy as np
# 加载视频
cap = cv2.VideoCapture(r"C:\Users\ASUS\Desktop\lubang.mp4")
# 加载另一张图片
template_img = cv2.imread(r"C:\Users\ASUS\Desktop\0393E890FB25C71EC713286012CC461E.png", cv2.IMREAD_GRAYSCALE)
template_keypoints, template_descriptors = cv2.SIFT_create().detectAndCompute(template_img, None)
# 创建BFMatcher对象
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)
lower_purple = np.array([120, 50, 50]) # 紫色的下限(可能需要根据实际情况调整)
upper_purple = np.array([180, 255, 255]) # 紫色的上限(同样需要调整)
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, None, fx=0.5, fy=0.5)
hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 根据HSV范围创建掩码
mask = cv2.inRange(hsv_frame, lower_purple, upper_purple)
# 找到掩码中的轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓(边界框)
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
area = cv2.contourArea(contour)
max_area = 0
max_area_idx = 0
# 遍历所有轮廓
for idx, contour in enumerate(contours):
x, y, w, h = cv2.boundingRect(contour)
area = cv2.contourArea(contour)
# 更新最大面积和索引
if area > max_area:
max_area = area
max_area_idx = idx
# 绘制最大面积的边界框
max_contour = contours[max_area_idx]
x, y, w, h = cv2.boundingRect(max_contour)
center_x = x + w // 2
center_y = y + h // 2
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.circle(frame, (center_x, center_y), 20, (0, 180, 185), 3)
# 如果需要,裁剪并显示ROI
max_roi = frame[y:y + h, x:x + w]
gray_roi = cv2.cvtColor(frame[y:y + h, x:x + w], cv2.COLOR_BGR2GRAY)
# 在 ROI 上运行 SURF 或 SIFT(这里以 SIFT 为例)
sift = cv2.SIFT_create()
keypoints_roi, descriptors_roi = sift.detectAndCompute(gray_roi, None)
# 如果检测到关键点且描述符不为空
if descriptors_roi is not None and template_descriptors is not None:
# 匹配描述符
matches = bf.match(descriptors_roi, template_descriptors)
matches = sorted(matches, key=lambda x: x.distance)[:10] # 取前10个最佳匹配
# 创建一个空的关键点列表,用于原始帧
keypoints_frame = []
# 将 ROI 中的关键点坐标转换回原始帧的坐标
for kp in keypoints_roi:
frame_x, frame_y = int(kp.pt[0] + x), int(kp.pt[1] + y)
keypoints_frame.append(cv2.KeyPoint(frame_x, frame_y, kp.size))
# 绘制匹配结果
img_matches = cv2.drawMatches(frame, keypoints_frame, template_img, template_keypoints, matches, None, flags=2)
# 计算所有关键点的坐标均值
if keypoints_frame:
mean_pt = np.mean([kp.pt for kp in keypoints_frame], axis=0).astype(int)
mean_x, mean_y = mean_pt[0], mean_pt[1]
# 在均值位置绘制一个圆
cv2.circle(img_matches, (mean_x, mean_y), 10, (0, 0, 255), 3) # 红色圆,半径为10,线条粗细为3
# 显示结果
cv2.imshow('SIFT KeyPoints, Matches, and Mean Position on Original Frame', img_matches)
# 显示最大 ROI
cv2.imshow('Max ROI', max_roi)
if cv2.waitKey(100) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
——————————————————————
- SURF(Speeded Up Robust Features)特征:SURF是SIFT的改进版本,它在保持SIFT特征优良性能的同时,提高了运算速度。SURF特征同样具有尺度不变性和旋转不变性。
-(受其专利保护,opencv4不能使用)-
——————————————————————
- ORB(Oriented FAST and Rotated BRIEF)特征:ORB是一种结合了FAST特征点检测和BRIEF特征描述子的算法。ORB特征检测具有尺度和旋转不变性,且计算速度快,适用于实时性要求较高的场景。
ORB特征检测主要分为以下两个步骤:
①方向FAST特征点检测: FAST角点检测是一种基于机器学习的快速角点特征检测算法,具有方向的FAST特征点检测是对兴趣点所在圆周上的16个像素点进行判断,若判断后的当前中心像素点为暗或亮,将候定其是否为角点。FAST角点检测计算的时间复杂度小,检测效果突出。FAST角点检测为加速算法实现,通常先对回周上的点集进行排序,排序使得其计算过程大大得到了优化。FAST对多尺度特性的描述是还是通过建立图像金字塔实现的,而对于旋转不变性即方向的特征则引入灰度质心法用于描述特征点的方向。
②BRIEF特征描述: BRIEF描述子主要是通过随机选取兴趣点周围区域的若干点来组成小兴趣区域,将这些小兴趣区域的灰度二值化并解析成二进制码串,将串特征作为该特征点的描述子,BRIEF描述子选取关键点附近的区域并对每一位比较其强度大小,然后根据图像块中两个二进制点来判断当前关键点编码是0还是1.因为BRIEF描述子的所有编码都是二进制数的,这样就节省了计算机存储空间。
引自 图像处理之特征提取
(1)图片与图片匹配实例:
点击查看代码
import numpy as np
import cv2
def orb_feature_matching(img1, img2):
# 初始化ORB检测器
orb = cv2.ORB_create()
# 查找关键点和描述符
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 创建BFMatcher对象,进行汉明距离匹配
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 匹配描述符
matches = bf.match(des1, des2)
# 根据距离排序
matches = sorted(matches, key=lambda x: x.distance)
# 绘制前N个匹配项
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2)
# 显示匹配结果
cv2.imshow('ORB Feature Matches', img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 读取图像
img1 = cv2.imread(r"C:\Users\ASUS\Desktop\v2-d0e1772dfef7d33fc0edeec302565d86_b.jpg", 0) # 确保替换为你的图像路径
img2 = cv2.imread(r"C:\Users\ASUS\Desktop\5D6D877E3DFF0178E00199B70ECDC8B3.png", 0) # 确保替换为你的图像路径
# 调用函数进行ORB特征匹配
if img1 is not None and img2 is not None:
orb_feature_matching(img1, img2)
else:
print("Error: Unable to load images.")
效果图:

(2)视频里图片特征点选取、匹配、框选:(简陋版,未加良好的匹配算法)
点击查看代码
import cv2
import numpy as np
# 加载模板图片
template = cv2.imread(r"C:\Users\ASUS\Desktop\tezheng.png", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
kp_template, des_template = orb.detectAndCompute(template, None)
# 打开视频文件
cap = cv2.VideoCapture(r"C:\Users\ASUS\Desktop\0f6a8abf1b513e7860da9257a8ed6e65.mp4")
while True:
ret, frame = cap.read()
if not ret:
break
# 转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
kp_frame, des_frame = orb.detectAndCompute(gray, None)
if des_template is not None and des_frame is not None:
# 使用BFMatcher进行特征匹配
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des_template, des_frame)
# 根据距离排序匹配项
matches = sorted(matches, key=lambda x: x.distance)
# 选择前N个好的匹配项(这里N可以是根据需要调整的)
good_matches = matches[:10]
if len(good_matches) > 4:
src_pts = np.float32([kp_template[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp_frame[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
H, masked = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
if H is not None:
template_width, template_height = template.shape[1], template.shape[0]
corners = np.float32([[0, 0], [0, template_height - 1],
[template_width - 1, template_height - 1], [template_width - 1, 0]])
corners_transformed = cv2.perspectiveTransform(corners.reshape(1, -1, 2), H)
corners_transformed = np.int32(corners_transformed.reshape(-1, 2))
cv2.polylines(frame, [corners_transformed], isClosed=True, color=(0, 255, 0), thickness=2)
resized_frame = cv2.resize(frame, None, fx=0.5, fy=0.5)
cv2.imshow('Video with Template', resized_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源和关闭窗口
cap.release()
cv2.destroyAllWindows()

HOG、LBP:
SIFT提取的关键点是角点,HOG提取的是边缘特征。
LBP(局部二值模式)是一种描述图像局部纹理的特征算子,具有旋转不变性与灰度不变性等显著优点。

HAAR:
人脸检测最为经典的算法Haar-like特征+Adaboost。
二、特征匹配
特征匹配是基于特征方法的第二步,其目的是通过比较目标特征和背景特征(或另一幅图像中的特征),找出相似或相同的特征点,从而实现目标的检测和跟踪。
特征匹配算法:
常用的特征匹配算法包括暴力匹配(Brute-Force Matcher)、FLANN(Fast Library for Approximate Nearest Neighbors)匹配、交叉检查(Cross Check)等。暴力匹配算法简单直观,但计算量大;FLANN匹配算法则通过构建索引结构来加速匹配过程。
实例:
【1】SIFT+FLANN:

点击查看代码
import cv2
import numpy as np
# 读取图像
img1 = cv2.imread(r"C:\Users\ASUS\Desktop\v2-d0e1772dfef7d33fc0edeec302565d86_b.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread(r"C:\Users\ASUS\Desktop\7934D5695FE014F7B90AA14EB5582BA2.png", cv2.IMREAD_GRAYSCALE)
# 初始化SIFT检测器
sift = cv2.SIFT_create()
# 检测关键点和描述符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 检查描述符是否存在
if des1 is None or des2 is None:
print("SIFT descriptors not found.")
exit()
# 创建FLANN索引
# 对于浮点描述符,使用KDTree
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50) # 或者传递空的字典
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 匹配描述符
matches = flann.knnMatch(des1, des2, k=2)
# 应用比例测试
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
# 如果找到了好的匹配项,则绘制它们
if len(good_matches) > 4: # 假设至少需要4个匹配项来绘制结果
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None,
flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow('Good Matches', img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print("Not enough matches are found - {}/{}".format(len(good_matches), 4))
【2】SIFT+暴力匹配:
点击查看代码
import cv2
import numpy as np
# 读取图像
img1 = cv2.imread(r"C:\Users\ASUS\Desktop\v2-d0e1772dfef7d33fc0edeec302565d86_b.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread(r"C:\Users\ASUS\Desktop\7934D5695FE014F7B90AA14EB5582BA2.png", cv2.IMREAD_GRAYSCALE)
# 初始化SIFT检测器
sift = cv2.SIFT_create()
# 检测关键点和描述符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 检查描述符是否存在
if des1 is None or des2 is None:
print("SIFT descriptors not found.")
exit()
# 创建FLANN索引
# 对于浮点描述符,使用KDTree
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50) # 或者传递空的字典
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 匹配描述符
matches = flann.knnMatch(des1, des2, k=2)
# 应用比例测试
good_matches = []
for m, n in matches:
if m.distance < 0.3 * n.distance: #0.3为比例测试参数
good_matches.append(m)
# 如果找到了好的匹配项,则绘制它们
if len(good_matches) > 4: # 假设至少需要4个匹配项来绘制结果
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None,
flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow('Good Matches', img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print("Not enough matches are found - {}/{}".format(len(good_matches), 4))

【3】SIFT+交叉检查:
点击查看代码
# 初始化SIFT检测器
sift = cv2.SIFT_create()
# 检测关键点和描述符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 创建暴力匹配器,启用交叉检查
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)
# 匹配描述符
matches = bf.match(des1, des2)
匹配策略:
为了提高匹配的准确性和效率,可以采用一些匹配策略,如最近邻匹配(Nearest Neighbor Matching)、比率测试(Ratio Test)等。最近邻匹配是找到与目标特征点最近的特征点作为匹配点;比率测试则是比较最近邻和次近邻的距离比,只有当该比值小于一定阈值时才认为匹配成功。

浙公网安备 33010602011771号