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

使用OpenCV多线程调用摄像头出现cv2.error异常求助

多线程OpenCV摄像头预览报错排查与解决

问题代码

import threading
import cv2

class CamThread(threading.Thread):

    def __init__(self, previewname, camid):
        threading.Thread.__init__(self)
        self.previewname = previewname
        self.camid = camid

    def run(self):
        print("Starting " + self.previewname)
        previewcam(self.previewname, self.camid)

# Function to preview the camera.
def previewcam(previewname, camid):
    cv2.namedWindow(previewname)
    cam = cv2.VideoCapture(camid)
    if cam.isOpened():
        rval, frame = cam.read()
    else:
        rval = False

    while rval:
        cv2.imshow(previewname, frame)
        rval, frame = cam.read()
        key = cv2.waitKey(20)
        if key == 27:  # Press ESC to exit/close each window.
            break
    cv2.destroyWindow(previewname)

运行报错信息

self.run()
  File "swann.py", line 17, in run
    previewcam(self.previewname, self.camid)
  File "swann.py", line 21, in previewcam
    cv2.namedWindow(previewname)
cv2.error: Unknown C++ exception from OpenCV code
Assertion failed: (NSViewIsCurrentlyBuildingLayerTreeForDisplay() != currentlyBuildingLayerTree), function NSViewSetCurrentlyBuildingLayerTreeForDisplay, file NSView.m, line 13477.
zsh: illegal hardware instruction

问题原因

这是macOS平台特有的OpenCV限制:OpenCV的窗口管理API(cv2.namedWindow、cv2.imshow、cv2.waitKey等)必须在主线程中执行,子线程调用这些UI相关操作会触发系统层的断言错误,导致程序崩溃。

修复方案

方案1:主线程处理UI,子线程仅读取摄像头帧

将UI操作全部放在主线程,子线程负责读取摄像头帧并通过线程安全的队列传递给主线程显示:

import threading
import cv2
from queue import Queue

class CamThread(threading.Thread):
    def __init__(self, camid, frame_queue):
        threading.Thread.__init__(self)
        self.camid = camid
        self.frame_queue = frame_queue
        self.running = True

    def run(self):
        cam = cv2.VideoCapture(self.camid)
        if not cam.isOpened():
            self.running = False
            return
        
        while self.running:
            rval, frame = cam.read()
            if rval:
                # 队列满时丢弃旧帧,避免内存占用过高
                if self.frame_queue.full():
                    self.frame_queue.get()
                self.frame_queue.put(frame)
            else:
                break
        cam.release()

def main():
    previewname = "Camera Preview"
    camid = 0
    frame_queue = Queue(maxsize=5)

    # 启动子线程读取帧
    cam_thread = CamThread(camid, frame_queue)
    cam_thread.start()

    # 主线程处理UI
    cv2.namedWindow(previewname)
    while True:
        if not frame_queue.empty():
            frame = frame_queue.get()
            cv2.imshow(previewname, frame)
        
        key = cv2.waitKey(20)
        if key == 27:  # ESC退出
            cam_thread.running = False
            break
    
    cv2.destroyWindow(previewname)
    cam_thread.join()

if __name__ == "__main__":
    main()

方案2:禁用OpenCV内部多线程

如果仅需单摄像头预览,可在代码开头添加一行禁用OpenCV内部线程,避免线程冲突:

import threading
import cv2

# 禁用OpenCV内部多线程
cv2.setNumThreads(0)

class CamThread(threading.Thread):
    # 原类代码不变...

注意事项

  • macOS上所有OpenCV窗口操作必须在主线程执行,这是系统UI框架的强制要求
  • 多线程传递帧时,使用线程安全的Queue类避免数据竞争
  • 退出程序时,要确保子线程正确停止并释放摄像头资源(调用cam.release())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:31:03