基于OpenCV实现随瓶子旋转的跟踪线条及替代算法咨询
如何在OpenCV中实现随瓶子旋转的对齐线条并自动触发保存?
问题背景
你想要实现的功能是:
- 在图像中绘制固定的红色垂直边界线(x=500和x=800),并初始化绿色线条
- 当瓶子旋转时,绿色线条始终处于瓶子中间并跟随旋转
- 当绿色线条与红色边界线对齐时,自动保存图像或退出程序
你之前尝试用模板匹配跟踪,但效果不佳,现有代码如下:
import cv2 from time import sleep import numpy as np vid = cv2.VideoCapture(0) sleep(2) line_show = False save_reference = False template_compare_method = cv2.TM_SQDIFF_NORMED i = 0 while True: check, frame = vid.read() print(check) frame1 = cv2.line(frame, (500, 0), (500, 720), (255, 0, 0), 7) frame1 = cv2.line(frame1, (800, 0), (800, 720), (255, 0, 0), 7) if line_show: h, w = frame1.shape[:2] if not save_reference: reference = frame1[200:500, 780:790] cv2.imwrite("../../images/white_image.jpg", reference) save_reference = True if save_reference: reference_image = cv2.imread('../../images/white_image.jpg') result = cv2.matchTemplate(reference_image, frame1, template_compare_method) mn, _, mnLoc, _ = cv2.minMaxLoc(result) MPx, MPy = mnLoc trows, tcols = reference_image.shape[:2] frame1 = cv2.rectangle(frame1, (MPx, MPy), (MPx+tcols, MPy+trows), (0, 0, 255), 2) cv2.imshow("image", frame1) key = cv2.waitKey(1) if key == ord('l'): line_show = True if key == ord('k'): cv2.imwrite("../../images/saved_image_"+str(i)+".jpg", frame1) i = i + 1 if key == ord('s'): cv2.imwrite("../../images/saved_image.jpg", frame) vid.release() print("Image saved") break elif key == ord('q'): vid.release() cv2.destroyAllWindows() break
为什么模板匹配效果差?
模板匹配的核心是寻找和模板完全一致的像素区域,但瓶子旋转时,你截取的固定模板(frame1[200:500, 780:790])无法适应瓶子的形变,导致匹配度急剧下降,自然跟踪失效。这种方法仅适合刚性物体的平移跟踪,完全不适合带旋转形变的场景。
更合适的解决方案:轮廓检测+最小外接矩形
针对瓶子的旋转跟踪,最直观且鲁棒的方法是通过轮廓检测提取瓶子的形状,再计算其最小外接矩形的旋转角度,以此绘制动态的绿色中间线。具体步骤如下:
- 预处理图像:转灰度、模糊、阈值分割,提取瓶子的前景区域
- 提取瓶子轮廓:筛选出符合瓶子尺寸/形状的轮廓
- 计算旋转角度:通过最小外接矩形获取瓶子的旋转角度
- 绘制动态绿色线条:根据瓶子中心和旋转角度绘制中间线
- 对齐判断:当绿色线条角度接近垂直(与红色边界线平行)时,触发保存/退出
改进后的代码示例
import cv2 import numpy as np from time import sleep def get_bottle_rotation_angle(frame, lower_color, upper_color): # 预处理:转HSV、颜色阈值分割、模糊去噪(根据瓶子颜色调整阈值) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, lower_color, upper_color) mask = cv2.medianBlur(mask, 5) # 查找轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return None, None # 筛选最大轮廓(假设瓶子是画面中最大的物体) max_contour = max(contours, key=cv2.contourArea) area = cv2.contourArea(max_contour) # 过滤小轮廓,避免噪声干扰 if area < 10000: return None, None # 计算最小外接矩形,获取中心、尺寸、旋转角度 rect = cv2.minAreaRect(max_contour) center, (width, height), angle = rect # 调整角度范围,统一为0~90度,方便判断垂直对齐 if width < height: angle = 90 + angle return center, angle # 初始化视频捕获 vid = cv2.VideoCapture(0) sleep(2) line_show = False # 颜色阈值:根据你的瓶子+背景颜色调整,这里示例是透明瓶子在白色背景下的参数 # 如果是彩色瓶子,替换为对应颜色的HSV范围即可 lower_color = np.array([0, 0, 200]) upper_color = np.array([180, 30, 255]) align_threshold = 2 # 角度差小于2度时认为对齐 while True: check, frame = vid.read() if not check: break # 绘制固定红色边界线 frame1 = cv2.line(frame, (500, 0), (500, 720), (255, 0, 0), 7) frame1 = cv2.line(frame1, (800, 0), (800, 720), (255, 0, 0), 7) if line_show: center, angle = get_bottle_rotation_angle(frame1, lower_color, upper_color) if center is not None: cx, cy = int(center[0]), int(center[1]) # 计算绿色线条的端点:穿过瓶子中心,长度固定为200 length = 200 rad = np.deg2rad(angle) x1 = int(cx - length * np.sin(rad)) y1 = int(cy + length * np.cos(rad)) x2 = int(cx + length * np.sin(rad)) y2 = int(cy - length * np.cos(rad)) # 绘制绿色中间线 frame1 = cv2.line(frame1, (x1, y1), (x2, y2), (0, 255, 0), 5) # 判断是否对齐:红色线是垂直的,角度接近0或180度时触发保存 if abs(angle) < align_threshold or abs(angle - 180) < align_threshold: cv2.imwrite("../../images/aligned_image.jpg", frame1) print("对齐成功,图像已保存") vid.release() cv2.destroyAllWindows() break cv2.imshow("image", frame1) key = cv2.waitKey(1) if key == ord('l'): line_show = True elif key == ord('q'): vid.release() cv2.destroyAllWindows() break
代码说明
- 颜色阈值调整:你需要根据实际瓶子和背景的颜色,修改
lower_color和upper_color的HSV范围。如果是透明瓶子,也可以改用灰度阈值(转灰度后做二值化)。 - 轮廓筛选:通过面积过滤小轮廓,避免噪声干扰;若场景复杂,还可以通过外接矩形的长宽比进一步筛选瓶子轮廓。
- 角度调整:
cv2.minAreaRect返回的角度范围是-900,我们将其调整为090度,方便判断垂直对齐状态。 - 对齐判断:当角度与0度(垂直)的差值小于
align_threshold时,认为线条对齐,自动保存图像并退出。
其他备选方案
如果你的场景背景复杂,或者瓶子颜色不固定,还可以考虑:
- 霍夫变换检测直线:检测瓶子的两条侧边直线,计算中间线的角度,适合圆柱形瓶子。
- 特征点跟踪(ORB/SIFT):提取瓶子上的特征点,跟踪特征点运动并计算旋转矩阵得到角度,适合非刚性或背景复杂的场景,但实现相对复杂。
内容的提问来源于stack exchange,提问作者Jaswant
相关产品推荐
相关产品推荐

