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

多线程实现双路摄像头CRST目标跟踪异常问题咨询

双路USB摄像头CSRT多线程目标跟踪故障解决方案

问题复现

基于多线程实现两台USB外接摄像头的CSRT(原描述中CRST为笔误)实时目标跟踪,单路运行逻辑完全正常,摄像头ID规则为:0=笔记本内置摄像头,1=1号外接摄像头,2=2号外接摄像头。双路运行时出现两类异常:

  • 内置摄像头+单外接摄像头组合:两路均弹出ROI框选窗口,但仅一路可正常进入跟踪流程,另一路选完ROI后无响应
  • 双外接摄像头组合(核心场景):显示窗口两路画面快速交替闪烁,抛出如下线程异常:
Exception in thread Thread-8:
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\threading.py", line 973, in _bootstrap_inner
    self.run()
  File "C:\ProgramData\Anaconda3\lib\threading.py", line 910, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\user\AppData\Local\Temp/ipykernel_18232/2959108515.py", line 59, in object_tracking_1
cv2.error: OpenCV(3.4.11) C:\Users\appveyor\AppData\Local\Temp\1\pip-req-build-neg5amx3\opencv\modules\core\src\dxt.cpp:3335: error: (-215:Assertion failed) type == CV_32FC1 || type == CV_32FC2 || type == CV_64FC1 || type == CV_64FC2 in function 'cv::dft'

公开资料提及该异常可能和USB总线冲突相关,但单路运行无任何问题。

故障根因

  • 代码逻辑错误:两个跟踪函数使用了完全相同的显示窗口名Tracking,且函数内所有逻辑被包裹在if __name__ == '__main__'判断块中,在ipykernel子线程环境下该判断不成立时,会直接导致逻辑不执行;子线程同时调用cv2.selectROI、cv2.waitKey等非线程安全的UI接口,出现资源抢占
  • OpenCV版本缺陷:当前使用的3.4.11版本CSRT跟踪器存在全局资源并发冲突bug,多线程同时调用DFT计算时,会拿到被其他线程污染的帧数据,触发类型断言报错
  • 硬件带宽冲突:两台高分辨率摄像头同时接入同一个USB根集线器(比如共用同一个扩展坞、同侧USB接口)时,会出现USB带宽不足,导致帧读取数据损坏,进一步触发跟踪器报错

修复步骤

  1. 代码逻辑修正
    • 给两路摄像头分配独立的ROI窗口、显示窗口名称,避免同窗口资源抢占
    • 删除子线程函数内冗余的if __name__ == '__main__'判断,避免子线程逻辑被意外屏蔽
    • 不要在子线程内调用cv2.waitKey,该接口必须在主线程执行,否则会出现UI事件响应异常
  2. 版本兼容修复
    • 升级OpenCV到4.5及以上版本,官方已修复旧版CSRT跟踪器的多线程并发bug
    • 若暂时无法升级,给跟踪器更新逻辑加线程互斥锁,保证同一时间只有一个线程调用跟踪器计算接口
  3. 硬件带宽优化
    • 将两台外接摄像头分别接入不同的USB根集线器(比如笔记本左右侧各插一个,不要共用同一个扩展坞)
    • 代码中手动设置摄像头分辨率、帧率为较低值(比如640x480@15fps),降低USB带宽占用

最小修改可运行代码

import cv2
import sys
import threading

(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
# 线程锁,旧版OpenCV需要加锁避免跟踪器资源冲突
tracker_lock = threading.Lock()
exit_flag = threading.Event()

def create_tracker():
    tracker_type = "CSRT"
    if int(minor_ver) < 3:
        tracker = cv2.Tracker_create(tracker_type)
    else:
        tracker = cv2.TrackerCSRT_create()
    return tracker, tracker_type

def object_tracking(cam_id, window_name):
    # 初始化摄像头
    video = cv2.VideoCapture(cam_id)
    # 降低分辨率和帧率减少带宽占用
    video.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    video.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
    video.set(cv2.CAP_PROP_FPS, 15)

    if not video.isOpened():
        print(f"Could not open camera {cam_id}")
        return

    ok, frame = video.read()
    if not ok:
        print(f"Cannot read camera {cam_id}")
        return

    # 独立ROI窗口
    bbox = cv2.selectROI(f"Select ROI for {window_name}", frame, False)
    cv2.destroyWindow(f"Select ROI for {window_name}")

    tracker, tracker_type = create_tracker()
    tracker.init(frame, bbox)

    while True:
        ok, frame = video.read()
        if not ok:
            break

        timer = cv2.getTickCount()
        # 加锁保证跟踪器调用线程安全,新版OpenCV可去掉锁
        with tracker_lock:
            ok, bbox = tracker.update(frame)
        fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer)

        if ok:
            p1 = (int(bbox[0]), int(bbox[1]))
            p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
            cv2.rectangle(frame, p1, p2, (255,0,0), 2, 1)
            center_x = round((bbox[0]+bbox[2])/2, 2)
            center_y = round((bbox[1]+bbox[3])/2, 2)
            cv2.putText(frame, f"x Coordinate : {center_x}", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)
            cv2.putText(frame, f"y Coordinate : {center_y}", (100,105), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)
        else:
            cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)

        cv2.putText(frame, f"{tracker_type} Tracker Cam{cam_id}", (100,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50),2)
        cv2.putText(frame, f"FPS : {int(fps)}", (100,50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50), 2)
        # 独立显示窗口
        cv2.imshow(window_name, frame)

        # 退出标志由主线程waitKey控制
        if exit_flag.is_set():
            break
    
    video.release()

if __name__ == '__main__':
    t1 = threading.Thread(target=object_tracking, args=(1, "Tracking_Cam1"))
    t2 = threading.Thread(target=object_tracking, args=(2, "Tracking_Cam2"))

    t1.start()
    t2.start()

    # 主线程统一处理UI按键事件
    while True:
        k = cv2.waitKey(1) & 0xff
        if k == 27:
            exit_flag.set()
            break

    t1.join()
    t2.join()
    cv2.destroyAllWindows()

注:如果要运行内置+外接的组合,只需要把其中一个线程的cam_id改成0即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 23:18:25