识别并跟踪矩形目标
识别视频中符合矩形轮廓的目标区域,并标记中心点位置。
实现思路
- **图像预处理:灰度 + 二值化
- **闭运算消除孔洞
- 二值化处理
- 查找并筛选矩形轮廓
- 解算中心点
- 目标筛选
- 结果绘制
环境
使用 OpenCV 和 python:
图像预处理:灰度 + 二值化
将读取到的帧转换为灰度图,再进行阈值二值化处理。
这里采用 反向二值化(白底黑物体),便于检测黑色矩形:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)
闭运算消除孔洞
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50)))
- 闭运算 : 膨胀后腐蚀,可去除图中小孔洞和断裂,提高矩形轮廓连通性。
闭运算后图像(轮廓更完整)
查找并筛选矩形轮廓
使用 cv2.findContours()
提取所有闭合轮廓,再筛选出“近似矩形”的轮廓:
is_rect, approx = is_approx_rect(cnt)
def is_approx_rect(contour, epsilon_factor=0.02): peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, epsilon_factor * peri, True) return (4 <= len(approx) <= 5 and cv2.isContourConvex(approx)), approx
- 调用
cv2.approxPolyDP()
将轮廓多边形逼近 - 条件:点数为 4~5 且轮廓是凸的
保留下的近似矩形轮廓图像
解算中心点
通过轮廓的几何矩获取中心点坐标:
def calc_center(approx): M = cv2.moments(approx) if M["m00"] == 0: return None return int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])
目标筛选(追踪最近矩形)
- 第一帧:选取面积最大的矩形
- 后续帧:优先选取与上一帧中心点距离最近的矩形
- 距离在 50 像素内的多个候选中,选择面积最大的
距离计算函数如下:
cv2.drawContours(display_frame, [approx], -1, (0, 0, 255), 5)
cv2.circle(display_frame, center, 7, (0, 0, 255), -1)
结果绘制
cv2.drawContours(display_frame, [approx], -1, (0, 0, 255), 5) cv2.circle(display_frame, center, 7, (0, 0, 255), -1)
- 红色画出识别轮廓
- 红点标记中心点位置
最终效果
电赛e题,杂乱环境稳定识别,-哔哩哔哩
完整cv代码
创作不易,点个赞再走哦
import cv2
import numpy as np
def is_approx_rect(contour, epsilon_factor=0.02):
peri = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon_factor * peri, True)
return (4 <= len(approx) <= 5 and cv2.isContourConvex(approx)), approx
def calc_center(approx):
M = cv2.moments(approx)
if M["m00"] == 0:
return None
return int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])
def distance(p1, p2):
return np.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
def main():
cap = cv2.VideoCapture("222.mp4")
if not cap.isOpened():
print("打开视频失败")
return
prev_center = None
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50)))
contours_data = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = contours_data[1] if len(contours_data) == 3 else contours_data[0]
candidates = []
for cnt in contours:
is_rect, approx = is_approx_rect(cnt)
if is_rect:
center = calc_center(approx)
if center:
candidates.append((approx, center, cv2.contourArea(approx)))
if not candidates:
selected = None
elif prev_center is None:
selected = max(candidates, key=lambda x: x[2])
else:
candidates.sort(key=lambda x: distance(x[1], prev_center))
top_n = [candidates[0]]
for c in candidates[1:]:
if distance(c[1], prev_center) - distance(candidates[0][1], prev_center) < 50:
top_n.append(c)
else:
break
selected = max(top_n, key=lambda x: x[2])
display_frame = frame.copy()
contour_img = np.zeros_like(frame)
if selected:
approx, center, _ = selected
cv2.drawContours(display_frame, [approx], -1, (0, 0, 255), 5)
cv2.circle(display_frame, center, 7, (0, 0, 255), -1)
cv2.drawContours(contour_img, [approx], -1, (0, 255, 0), 3)
prev_center = center
else:
prev_center = None
cv2.imshow("原视频", display_frame)
cv2.imshow("二值化", binary)
cv2.imshow("闭运算", closed)
cv2.imshow("轮廓", contour_img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()