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

PyQt5+OpenCV控制摄像头时SIGSEGV信号11错误求助

问题分析与解决方案

问题本质

MacOS下出现的Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)是摄像头资源释放不彻底或线程间资源竞争导致的段错误。MacOS的AVFoundation摄像头框架对资源管理的严谨性远高于Windows的MSMF,所以Windows仅出现警告但程序可继续,而MacOS直接触发崩溃。单独测试OpenCV无问题,说明是PyQt5线程与OpenCV资源交互的逻辑漏洞。

核心修复方案

所有摄像头相关操作(创建、读取、释放)必须完全限制在子线程内部,主线程仅通过信号槽发送控制指令,禁止直接操作子线程内的VideoCapture对象,同时确保线程优雅退出、资源完全释放。

修正后的代码示例

摄像头线程类

from PyQt5.QtCore import QThread, pyqtSignal
import cv2

class CameraThread(QThread):
    # 发送读取到的帧给主线程
    frame_ready = pyqtSignal(object)

    def __init__(self):
        super().__init__()
        self._running = False
        self._cap = None

    def start_capture(self):
        """主线程调用此方法启动摄像头"""
        self._running = True
        self.start()

    def stop_capture(self):
        """主线程调用此方法停止摄像头,确保线程优雅退出"""
        self._running = False
        self.wait()  # 等待线程完全执行完释放逻辑

    def run(self):
        """子线程执行体:所有摄像头操作都在这里完成"""
        # 子线程内创建VideoCapture,强制指定MacOS后端
        self._cap = cv2.VideoCapture(0, cv2.CAP_AVFOUNDATION)
        while self._running:
            ret, frame = self._cap.read()
            if ret:
                self.frame_ready.emit(frame)
        
        # 退出循环后必须释放资源
        if self._cap is not None:
            self._cap.release()
            self._cap = None

主窗口类

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel
from PyQt5.QtGui import QImage, QPixmap
import sys
import cv2

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.camera_thread = CameraThread()
        self.camera_thread.frame_ready.connect(self.update_display)

    def init_ui(self):
        self.setWindowTitle("Camera Test")
        self.btn_on = QPushButton("ON")
        self.btn_off = QPushButton("OFF")
        self.frame_label = QLabel()
        
        layout = QVBoxLayout()
        layout.addWidget(self.btn_on)
        layout.addWidget(self.btn_off)
        layout.addWidget(self.frame_label)
        self.setLayout(layout)

        self.btn_on.clicked.connect(self.camera_thread.start_capture)
        self.btn_off.clicked.connect(self.camera_thread.stop_capture)

    def update_display(self, frame):
        """将OpenCV帧转为QImage显示"""
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_frame.shape
        bytes_per_line = ch * w
        q_img = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
        self.frame_label.setPixmap(QPixmap.fromImage(q_img))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

关键注意事项

  1. 资源隔离:VideoCapture的创建、读取、释放必须全在子线程的run()方法内,主线程绝不直接操作该对象。
  2. 优雅退出:停止摄像头时必须调用wait()等待线程完全结束,确保VideoCapture被正确释放后再进行下一次启动。
  3. 指定后端:MacOS下强制指定cv2.CAP_AVFOUNDATION后端,避免OpenCV自动选择的后端存在兼容性问题。
  4. 避免快速操作:不要频繁快速点击ON/OFF,wait()会确保线程完成资源释放后再响应下一次启动。

为什么之前的代码会崩溃

如果你的原始代码存在以下任意一种情况,都会触发MacOS的段错误:

  • 在主线程创建VideoCapture,子线程读取,导致跨线程资源访问冲突;
  • 停止摄像头时直接终止线程(比如调用terminate()),未执行release()释放摄像头资源;
  • 多次启动线程但未彻底释放上一次的VideoCapture对象,导致资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:23:20