大恒相机Python版Qt界面(一)

更新:2022/11/18

        代码已完成,详情请参考:大恒相机Python版Qt界面(二)_△ф的博客-CSDN博客        


        首先,很抱歉之前写的Qt、C++的UI界面鸽了。。。这次的大恒相机界面python版一定坚持写完。

        在大恒相机给的Python示例中找不到完整界面的示例代码,且示例很不完善。由于示例中未使用到多线程的概念(threading),因此也无法直接从示例中修改代码,做一个有效界面。因此,我仿照大恒相机linux系统下的C代码示例,写了这个大恒相机UI界面。由于功能众多,长期更新中。

        本节内容:实现大恒相机采图与UI中显示功能。

        目前功能有:1、更新相机列表;2、打开/关闭相机;3、开始/停止采集;4、界面中显示采集图片及放大缩小功能;

        注:1、大恒相机的开发文档其实很详细,因此环境搭建等问题这里不再叙述。

                2、PyQt用法不再叙述。

1、项目结构:

大恒相机Python版Qt界面(一)_第1张图片

        其中,gxipy为大恒提供的库文件,UI中存放了UI界面,DahengCamera.py是自己写的相机控制类(相机采集使用了回调函数方式),mian.py为主界面函数。

2、UI界面:

        UI界面如下:

大恒相机Python版Qt界面(一)_第2张图片

        这里有个注意事项:在本UI界面中,相机的实时显示图像选择了QGraphicsView控件而非QLabel控制,主要原因是因为前者非常容易实现图像显示的放大、缩小功能。

 3、大恒相机控制函数代码如下:

import gxipy as gx
import time
import threading

rawImageUpdateList = []
rawImageUpdate = None
num = 0


def capture_callback(raw_image):
    if raw_image.get_status() == gx.GxFrameStatusList.INCOMPLETE:
        print("incomplete frame")
    else:
        global rawImageUpdateList, num, rawImageUpdate
        rawImageUpdate = raw_image.get_numpy_array()
        if len(rawImageUpdateList) == 0:
            rawImageUpdateList.append(rawImageUpdate)
        else:
            rawImageUpdateList.pop()
            rawImageUpdateList.append(rawImageUpdate)
        num += 1


class DahengCamera:
    def __init__(self):
        self.cam = None             #   相机对象
        self.dev_num = None
        self.dev_info_list = None
        self.device_manager = gx.DeviceManager()
        self.AcquisitionThread = None
        self.AcquisitionThreadNeedBeStop = False
        self.IsCameraOpened = False
        self.IsCameraStartAcq = False

    def UpdateCameraList(self):
        self.dev_num, self.dev_info_list = self.device_manager.update_device_list()
        if self.dev_num == 0:
            return False, '0'
        else:
            CameraNameList = []
            for info in self.dev_info_list:
                name = info['model_name']
                CameraNameList.append(name)
            return True, CameraNameList

    def OpenCamera(self, Index):
        if self.dev_num == 0:
            return False
        elif self.IsCameraOpened:
            return True
        else:
            self.cam = self.device_manager.open_device_by_index(Index)

        self.AcquisitionThread = threading.Thread(target=self.AcquisitionThreadFunc_CallBack, args=(), daemon=True)
        self.AcquisitionThread.start()
        self.IsCameraOpened = True

        return True

    def AcquisitionThreadFunc_CallBack(self):
        self.cam.data_stream[0].register_capture_callback(capture_callback)

        while not self.AcquisitionThreadNeedBeStop:
            time.sleep(1)

    def CloseCamera(self, Index):
        if not self.IsCameraOpened:
            return

        self.AcquisitionThreadNeedBeStop = True
        self.StopAcquisition()
        time.sleep(1)
        self.cam.data_stream[0].unregister_capture_callback()
        self.cam.close_device()

        self.IsCameraOpened = False

    def StartAcquisition(self):
        if self.IsCameraOpened and not self.IsCameraStartAcq:
            self.cam.stream_on()
            self.IsCameraStartAcq = True
        else:
            return

    def StopAcquisition(self):
        if self.IsCameraOpened and self.IsCameraStartAcq:
            self.cam.stream_off()
            self.IsCameraStartAcq = False
        else:
            return

        相机控制类的主要思路为:当每次调用OpenCamera时,会开启一个新的线程,在新线程中,进行相机回调函数的注册。随后,该线程将一直负责进行回调函数的运行工作,直到CloseCamera函数被调用。通过开辟一个新线程,可以有效的将界面显示跟相机回调分开,避免了画面的卡顿以及图像丢帧等问题。

        针对图像丢帧问题,有一点非常重要,即:不要在回调函数中进行任何复杂的数值计算或图像存储等工作。回调函数的主要工作机制是每当相机采集到一张图像,回调函数便会自动运行一次。而回调函数的整个运行时间取决于回调函数中进行的操作数量。若相机采集帧率非常高,而一次回调函数的处理时间跟不上采集帧率,很显然就会出现丢帧情况。因此,若要进行图像存储会其他处理操作,建议申请一个list变量,将采集到的图像保存在list中,随后在主线程中进行相应操作。(该部分内容后续随缘更新)

4、主界面代码如下:

import sys

from PyQt5 import QtWidgets, QtSerialPort, QtCore
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from UI.MainWindow import Ui_MainWindow
import cv2
import DahengCamera


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.Camera = DahengCamera.DahengCamera()
        self.TimerForShowImageInGraphicsView = QTimer()
        self.ImageWidthInGraphicsView = 500
        self.scene = QGraphicsScene()

        self.SlotInit()
        self.UpdateUI()

    """ 初始化槽信号函数"""
    def SlotInit(self):
        self.ui.pushButton_UpdateCameraList.clicked.connect(self.PB_UpdateCameraList_clicked)
        self.ui.pushButton_OpenCamera.clicked.connect(self.PB_OpenCamera_clicked)
        self.ui.pushButton_CloseCamera.clicked.connect(self.PB_CloseCamera_clicked)
        self.ui.pushButton_StartAcq.clicked.connect(self.PB_StartAcq_clicked)
        self.ui.pushButton_StopAcq.clicked.connect(self.PB_StopAcq_clicked)
        self.ui.pushButton_ZoomIn.clicked.connect(self.PB_ZoomIn_clicked)
        self.ui.pushButton_ZoomOut.clicked.connect(self.PB_ZoomOut_clicked)
        self.TimerForShowImageInGraphicsView.timeout.connect(self.SlotForShowImageInGraphicsView)

    """ 更新UI界面"""
    def UpdateUI(self):
        self.ui.pushButton_OpenCamera.setDisabled(self.Camera.IsCameraOpened)
        self.ui.pushButton_CloseCamera.setDisabled(not self.Camera.IsCameraOpened)
        self.ui.pushButton_StartAcq.setDisabled(not self.Camera.IsCameraOpened or self.Camera.IsCameraStartAcq)
        self.ui.pushButton_StopAcq.setDisabled(not self.Camera.IsCameraStartAcq)

    """ 点击UpdateCameraList"""
    def PB_UpdateCameraList_clicked(self):
        status, CameraNameList = self.Camera.UpdateCameraList()
        if status:
            for CameraName in CameraNameList:
                self.ui.comboBox_CameraList.addItem(CameraName)

    """ 点击OpenCamera"""
    def PB_OpenCamera_clicked(self):
        if self.ui.comboBox_CameraList.count() == 0:
            return
        self.Camera.OpenCamera(int(self.ui.comboBox_CameraList.currentIndex()) + 1)
        self.UpdateUI()

    """ 点击CloseCamera"""
    def PB_CloseCamera_clicked(self):
        self.Camera.CloseCamera(int(self.ui.comboBox_CameraList.currentIndex()) + 1)
        if self.TimerForShowImageInGraphicsView.isActive():
            self.TimerForShowImageInGraphicsView.stop()
        self.UpdateUI()

    """ 点击StartAcq"""
    def PB_StartAcq_clicked(self):
        self.Camera.StartAcquisition()
        self.TimerForShowImageInGraphicsView.start(33)
        self.UpdateUI()

    """ 点击StopAcq"""
    def PB_StopAcq_clicked(self):
        self.Camera.StopAcquisition()
        self.TimerForShowImageInGraphicsView.stop()
        self.UpdateUI()

    """ 点击ZoomIn"""
    def PB_ZoomIn_clicked(self):
        self.ImageWidthInGraphicsView += 100

    """ 点击ZoomOut"""
    def PB_ZoomOut_clicked(self):
        if self.ImageWidthInGraphicsView >= 200:
            self.ImageWidthInGraphicsView -= 100

    """ 图像显示回调函数"""
    def SlotForShowImageInGraphicsView(self):
        if DahengCamera.rawImageUpdate is None:
            return
        else:
            ImageShow = DahengCamera.rawImageUpdateList[0]
            ImageRatio = float(ImageShow.shape[0] / ImageShow.shape[1])
            image_width = self.ImageWidthInGraphicsView
            show = cv2.resize(ImageShow, (image_width, int(image_width * ImageRatio)))
            show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)  # 视频色彩转换回RGB,这样才是现实的颜色
            showImage = QImage(show.data, show.shape[1], show.shape[0],
                               QImage.Format_RGB888)  # 把读取到的视频数据变成QImage形式
            item = QGraphicsPixmapItem(QPixmap.fromImage(showImage))
            self.scene.clear()
            self.scene.addItem(item)
            self.scene.setSceneRect(0, 0, image_width + 1, image_width * ImageRatio + 1)
            self.ui.graphicsView.setScene(self.scene)
            self.ui.graphicsView.show()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()

    sys.exit(app.exec_())

你可能感兴趣的:(python,开发语言)