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

OpenCV-Python中碰撞矩形合并问题修复求助

问题描述

我正在修复一个bug:视频中两个矩形框碰撞后,在碰撞持续的后续帧中会合并为一个大矩形,而非保持原本的两个独立矩形。

碰撞前效果:
碰撞前的两个独立矩形

碰撞后效果:
碰撞后合并的大矩形

我希望即使在物体碰撞时,也能保留两个独立的矩形框。

核心循环代码片段:

while True:
    ret, frame = vid.read()
    if frame is None:
        break

    # TRESH_OTSU 在处理极暗物体时存在问题
    bright_frame = cv2.addWeighted(
        frame, 4.3, np.zeros(frame.shape, frame.dtype), 0, 20
    )

    # 创建掩码以排除跟踪时的黑色背景
    gray = cv2.cvtColor(bright_frame, cv2.COLOR_BGR2GRAY)
    _, mask = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # 获取运动物体
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    detections = []
    for obj in contours:
        area = cv2.contourArea(obj)
        if area > 50:  # 过滤噪声
            perimeter = cv2.arcLength(obj, True)
            approx = cv2.approxPolyDP(obj, 0.02 * perimeter, True)
            object_type = "rectangle" if len(approx) == 4 else "circle"
            x, y, w, h = cv2.boundingRect(obj)
            detections.append(
                DetectedObject(
                    x, y, w, h, get_obj_color(frame, x, y, w, h), object_type
                )
            )

    rects = tracker.update(detections)
    for rect in rects:
        x, y, w, h, id, _, _, time = rect
        cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 255), 5)
        # cv2.putText(
        #     frame, str(id), (x, y - 15), cv2.FONT_HERSHEY_PLAIN, 5, (255, 255, 255)
        # )
        print(rect)
    cv2.imshow("frame", frame)

    key = cv2.waitKey(10)

    # 按下ESC键终止循环
    if key == 27:
        break
问题根源与修复方案

核心原因

当前代码使用cv2.RETR_EXTERNAL模式提取轮廓,该模式只保留最外层轮廓。当两个矩形碰撞重叠时,它们的像素区域会被合并成一个连通域,导致findContours将其识别为单个轮廓,后续检测和跟踪自然只会输出一个大矩形。

修复步骤

1. 调整轮廓提取模式

将cv2.RETR_EXTERNAL替换为cv2.RETR_CCOMP(提取所有层次轮廓),并通过轮廓层级信息筛选独立物体:

# 替换原轮廓提取代码
contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
detections = []
for i, obj in enumerate(contours):
    area = cv2.contourArea(obj)
    if area > 50:
        # 只处理没有父轮廓的独立物体(层级数组第3位为-1表示无父轮廓)
        if hierarchy[0][i][3] == -1:
            perimeter = cv2.arcLength(obj, True)
            approx = cv2.approxPolyDP(obj, 0.02 * perimeter, True)
            object_type = "rectangle" if len(approx) == 4 else "circle"
            x, y, w, h = cv2.boundingRect(obj)
            detections.append(
                DetectedObject(
                    x, y, w, h, get_obj_color(frame, x, y, w, h), object_type
                )
            )

2. 基于颜色分割优化检测(可选)

如果两个物体颜色差异明显,可以用颜色掩码替代灰度阈值,从根源避免重叠时的轮廓合并:

# 替换原灰度阈值与轮廓提取步骤
hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 根据实际物体颜色调整范围(示例为红、蓝两色)
color_ranges = [
    ((0, 100, 100), (10, 255, 255)),
    ((110, 100, 100), (130, 255, 255))
]
detections = []
for lower, upper in color_ranges:
    mask = cv2.inRange(hsv_frame, lower, upper)
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for obj in contours:
        area = cv2.contourArea(obj)
        if area > 50:
            perimeter = cv2.arcLength(obj, True)
            approx = cv2.approxPolyDP(obj, 0.02 * perimeter, True)
            object_type = "rectangle" if len(approx) == 4 else "circle"
            x, y, w, h = cv2.boundingRect(obj)
            detections.append(DetectedObject(x, y, w, h, get_obj_color(frame, x, y, w, h), object_type))

3. 跟踪器适配优化

如果使用的跟踪器(如Sort、DeepSORT)依赖稳定的检测特征,可以给DetectedObject增加颜色、长宽比等特征,让跟踪器通过多维度匹配关联帧间物体,避免碰撞时ID混淆。

内容的提问来源于stack exchange,提问作者banan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 13:58:34