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

如何将Basler GigE相机的图像数组传入PyQt5实现视频流展示?

问题分析与修复方案

核心问题点

  • BaslerVideo类抓取逻辑错误:原_videoGrab方法内的while camera.IsGrabbing()是无限循环,无终止条件,永远不会返回图像,导致线程调用时完全阻塞,无法向GUI发送信号;且每次调用都会重新初始化相机,造成资源重复占用。
  • App类构造函数拼写错误:__inti__应为__init__,原代码中构造函数未执行,界面元素完全未初始化。
  • 方法定义位置错误:update_image和convert_cv_qt被定义在App类构造函数内部,属于局部函数,信号无法正确连接到这些方法。
  • VideoThread类设计冗余:继承了BaslerVideo却又在run方法中重新创建实例,导致相机重复初始化,逻辑混乱。

修复后的完整代码

from PyQt5 import QtGui
from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QVBoxLayout
from PyQt5.QtGui import QPixmap
import sys
import cv2
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread
import numpy as np
from pypylon import pylon

class BaslerVideo():
    def __init__(self):
        # 相机初始化仅执行一次
        self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
        self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
        self.converter = pylon.ImageFormatConverter()
        self.converter.OutputPixelFormat = pylon.PixelType_BGR8packed
        self.converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned

    def grab_frame(self):
        # 单次抓取一帧图像
        if self.camera.IsGrabbing():
            grabResult = self.camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
            if grabResult.GrabSucceeded():
                image = self.converter.Convert(grabResult)
                img = image.GetArray()
                grabResult.Release()
                return img
        return None

class VideoThread(QThread):
    change_pixmap_signal = pyqtSignal(np.ndarray)

    def __init__(self):
        super().__init__()
        self.cap = BaslerVideo()
        self._is_running = True

    def run(self):
        while self._is_running:
            cv_img = self.cap.grab_frame()
            if cv_img is not None:
                self.change_pixmap_signal.emit(cv_img)
            # 添加短暂延时,降低CPU占用
            self.msleep(10)

    def stop(self):
        self._is_running = False
        self.wait()

class App(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("GigE Cam Test")
        self.display_width = 640
        self.display_height = 480
        # 创建图像显示标签
        self.image_label = QLabel(self)
        self.image_label.resize(self.display_width, self.display_height)
        # 创建文本标签
        self.textLabel = QLabel("GigE Cam")
        
        # 布局设置
        vbox = QVBoxLayout()
        vbox.addWidget(self.image_label)
        vbox.addWidget(self.textLabel)
        self.setLayout(vbox)
        
        # 初始化并启动视频线程
        self.thread = VideoThread()
        self.thread.change_pixmap_signal.connect(self.update_image)
        self.thread.start()

    @pyqtSlot(np.ndarray)
    def update_image(self, cv_img):
        """更新图像标签内容"""
        qt_img = self.convert_cv_qt(cv_img)
        self.image_label.setPixmap(qt_img)
        
    def convert_cv_qt(self, cv_img):
        """将numpy数组转换为QPixmap格式"""
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        p = convert_to_Qt_format.scaled(self.display_width, self.display_height, Qt.KeepAspectRatio)
        return QPixmap.fromImage(p)

    def closeEvent(self, event):
        # 关闭窗口时停止线程,释放相机资源
        self.thread.stop()
        event.accept()

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

修复说明

  1. 重构BaslerVideo类:将相机初始化移至__init__方法,避免重复初始化;将原无限循环抓取改为单次抓取一帧的grab_frame方法,保证能正常返回图像。
  2. 修正构造函数拼写:将__inti__改为正确的__init__,确保界面初始化逻辑正常执行。
  3. 调整方法位置:将update_image和convert_cv_qt移至App类顶层,确保信号能正确连接触发。
  4. 优化线程逻辑:移除冗余继承,在线程初始化时创建相机实例;添加线程停止机制,避免程序退出时资源泄漏;增加短延时降低CPU占用。
  5. 添加资源清理:通过窗口关闭事件停止线程,释放相机资源。

内容的提问来源于stack exchange,提问作者Daniel Ricardo Vega

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 03:35:23