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

OpenCV人数统计项目重复计数问题求助:目标唯一ID分配方案

基于OpenCV的人数统计重复计数问题解决方案

问题说明

当前基于OpenCV的人数统计项目中,当目标的cx坐标处于line_value±offset区间时,现有计数逻辑会对同一目标重复统计。需要通过给每个目标分配唯一ID,确保同一目标在该区间内仅被计数一次。

核心解决思路

解决重复计数的关键是跟踪每个目标的唯一身份,记录已完成计数的目标状态,避免重复触发计数逻辑:

  • 使用字典存储跟踪的目标,键为目标唯一ID,值包含目标的中心坐标、是否已计数等状态
  • 对每帧检测到的轮廓,通过计算与已有跟踪目标的距离,匹配到对应的目标(距离阈值可根据实际场景调整)
  • 仅当目标首次进入line_value±offset区间且未被计数时,才执行计数操作,并标记该目标为已计数

修改后的完整代码

import cv2
import numpy as np

video = cv2.VideoCapture(r"C:\Users\korha\OneDrive\Belgeler\Human Detection 1.0\videos\cut1.mp4")
fgbg = cv2.bgsegm.createBackgroundSubtractorMOG()
offset = 12
line_value = 230
kernel = np.ones((5,5),np.uint8)
counter = 0
count_left = 0
count_right = 0

# 存储跟踪的目标:key是目标ID,value是{'cx': 中心x坐标, 'cy': 中心y坐标, 'counted': 是否已计数}
tracked_objects = {}
next_object_id = 0
# 目标匹配的距离阈值(可根据实际场景调整)
distance_threshold = 50

def counter_method(objects):
    global counter, count_left, count_right

    for obj_id, data in objects.items():
        cx = data['cx']
        # 检查是否在计数区间内且未被计数
        if (line_value - offset) < cx < (line_value + offset) and not data['counted']:
            # 获取目标之前的位置判断方向
            prev_cx = tracked_objects[obj_id]['prev_cx'] if 'prev_cx' in tracked_objects[obj_id] else cx
            if cx > prev_cx:
                count_right += 1
                counter += 1
            else:
                count_left += 1
                counter += 1
            # 标记为已计数
            tracked_objects[obj_id]['counted'] = True

def line_method(frame):
    cv2.putText(frame, str(count_right), (205,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0), 2)
    cv2.putText(frame, str(count_left), (190,60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0), 2)   
    cv2.putText(frame, 'Count right:', (10,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0), 2)
    cv2.putText(frame, 'Count left:', (10,60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0), 2)

def rect_method(frame, contours):
    global tracked_objects, next_object_id, distance_threshold
    current_objects = {}

    if len(contours) != 0:
        for contour in contours:
            if cv2.contourArea(contour) > 50000:
                x,y,w,h = cv2.boundingRect(contour)
                cv2.rectangle(frame, (x,y), (x+w,y+h), (0,0,255), 3)
                cx = x + w/2       
                cy = y + h/2
                cv2.circle(frame, (int(cx), int(cy)), 1, (0,0,255), 2)

                # 尝试匹配已有跟踪目标
                matched_id = None
                for obj_id, data in tracked_objects.items():
                    # 计算当前中心与已有目标中心的欧氏距离
                    dist = np.sqrt((cx - data['cx'])**2 + (cy - data['cy'])**2)
                    if dist < distance_threshold:
                        matched_id = obj_id
                        break

                if matched_id is not None:
                    # 更新已跟踪目标的位置,保留前一次位置用于方向判断
                    tracked_objects[matched_id]['prev_cx'] = tracked_objects[matched_id]['cx']
                    tracked_objects[matched_id]['cx'] = cx
                    tracked_objects[matched_id]['cy'] = cy
                    current_objects[matched_id] = tracked_objects[matched_id]
                else:
                    # 新增目标
                    tracked_objects[next_object_id] = {
                        'cx': cx,
                        'cy': cy,
                        'counted': False
                    }
                    current_objects[next_object_id] = tracked_objects[next_object_id]
                    next_object_id += 1

    # 移除不再出现的目标(可选,避免内存占用)
    tracked_objects = {k:v for k,v in tracked_objects.items() if k in current_objects}
    return current_objects

def main():
    while True:
        ret, frame = video.read()
        if ret is True:
            fgmask = fgbg.apply(frame)
            fgmask = cv2.erode(fgmask, kernel=kernel, iterations=1)
            fgmask = cv2.dilate(fgmask, kernel=kernel, iterations=1)
            fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
            fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel)

            contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
            start_point = (line_value,50)
            end_point = (line_value,800)
            cv2.line(frame, start_point, end_point, (0,0,255), 2)
            
            # 获取当前帧的跟踪目标
            current_objects = rect_method(frame, contours)
            # 执行计数逻辑
            counter_method(current_objects)
            # 绘制计数文本
            line_method(frame)
            
            cv2.imshow('frame', frame)
            # 按q键退出,原代码waitKey(0)会暂停每帧,改为1按正常速度播放
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
        else:
            break

    video.release()
    cv2.destroyAllWindows()

main()

代码说明

  1. 目标跟踪模块:rect_method中通过计算轮廓中心与已有目标的距离实现目标匹配,为新目标分配唯一ID,更新已有目标的位置
  2. 计数逻辑优化:counter_method仅对首次进入计数区间且未被标记的目标计数,标记后不再重复统计
  3. 方向判断:通过目标的前一次位置与当前位置对比,判断移动方向(左/右)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 07:37:02