使用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
相关产品推荐
相关产品推荐

