You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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])无法适应瓶子的形变,导致匹配度急剧下降,自然跟踪失效。这种方法仅适合刚性物体的平移跟踪,完全不适合带旋转形变的场景。

更合适的解决方案:轮廓检测+最小外接矩形

针对瓶子的旋转跟踪,最直观且鲁棒的方法是通过轮廓检测提取瓶子的形状,再计算其最小外接矩形的旋转角度,以此绘制动态的绿色中间线。具体步骤如下:

  1. 预处理图像:转灰度、模糊、阈值分割,提取瓶子的前景区域
  2. 提取瓶子轮廓:筛选出符合瓶子尺寸/形状的轮廓
  3. 计算旋转角度:通过最小外接矩形获取瓶子的旋转角度
  4. 绘制动态绿色线条:根据瓶子中心和旋转角度绘制中间线
  5. 对齐判断:当绿色线条角度接近垂直(与红色边界线平行)时,触发保存/退出

改进后的代码示例

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 14:17:56