更新:2022/11/18
代码已完成,详情请参考:大恒相机Python版Qt界面(二)_△ф的博客-CSDN博客
首先,很抱歉之前写的Qt、C++的UI界面鸽了。。。这次的大恒相机界面python版一定坚持写完。
在大恒相机给的Python示例中找不到完整界面的示例代码,且示例很不完善。由于示例中未使用到多线程的概念(threading),因此也无法直接从示例中修改代码,做一个有效界面。因此,我仿照大恒相机linux系统下的C代码示例,写了这个大恒相机UI界面。由于功能众多,长期更新中。
本节内容:实现大恒相机采图与UI中显示功能。
目前功能有:1、更新相机列表;2、打开/关闭相机;3、开始/停止采集;4、界面中显示采集图片及放大缩小功能;
注:1、大恒相机的开发文档其实很详细,因此环境搭建等问题这里不再叙述。
2、PyQt用法不再叙述。
1、项目结构:
其中,gxipy为大恒提供的库文件,UI中存放了UI界面,DahengCamera.py是自己写的相机控制类(相机采集使用了回调函数方式),mian.py为主界面函数。
2、UI界面:
UI界面如下:
这里有个注意事项:在本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_())