Python realizes the real-time display video stream and camera function of Hikvision robot industrial camera MV-CU060-10GM

1. Background introduction

1. In the recent project, it is necessary to connect the Hikvision robot industrial camera MV-CU060-10GM to the customer;
2. The customer requires that the following functions can be realized on the page through the deployed management platform:

1) The camera video stream starts previewing;
2) The camera video stream stops previewing;
3) The camera takes pictures.

Demand background: The customer needs to conduct regular sampling quality inspections on the products produced, which involves appearance inspections, such as the surface cleanliness and appearance of the samples. Therefore, it is necessary to click the "Preview" button of the camera through the management platform to preview the real-time effect of the camera shooting. When the customer thinks that the clarity and angle meet the conditions, he will click the "Photograph" button to take pictures, so as to ensure the quality of the photos. It is meaningful and practical.


2. Research process

Since the colleagues in the project team had not done any development related to industrial camera video and photography before, they started a "long" and "suffering" research road (continued intermittently for more than a month).

Finally, on December 6, 2022, the above three functions were "perfectly" realized through Python .
This blog is specially written for the reference of netizens in need, so as to avoid many detours.

1. Hikvision Industrial Camera official website

https://www.hikrobotics.com/cn/machinevision/productdetail?id=8518&pageNumber=13&pageSize=20
insert image description here

2. Example of official website

You can go to the client tool MVS provided by Hikvision Robot’s official website , “ Help ” –> “ Development ”, click “ Development ” to jump to the installation directory, and get some official simple examples from “ Samples ”.

If you have installed MVS, you can directly enter the C:\Program Files (x86)\MVS\Development\Samples directory to see it. Currently, it supports C#, C++, Java , OpenCV, Python , VB and other languages.

I mainly use Java and Python , so this blog post mainly investigates the implementation scheme from Java and Python.
insert image description here
insert image description here

1) Java example

SaveImage has an example of getting an image , but there is no example of getting and displaying a video stream. (relatively few reference examples)
insert image description here

2) Python example

There are examples of obtaining images under the GrabImage directory , and examples of obtaining video streams under the Recording directory , but there is no example of returning video streams to the front end. (There are relatively many reference examples)
insert image description here

3. Online blog reference

1) RTSP (Runtime Stream Protocol) protocol direction

(Unfortunately, this way is blocked!!!) Because the Hikvision robot industrial camera MV-CU060-10GM
we use does not support the RTSP protocol .

The following blogs are applicable to Hikvision cameras, but not Hikvision industrial cameras. If you are using Hikvision cameras, you can refer to it.

Reference blog: Hikvision camera docking SDK real-time preview function and capture function, lazy cancer benefits, direct CV

2) Java implementation direction

(It cannot fully meet the needs of customers, this road is not perfect!!!)

Using Java to refer to the example on the official website, the function of image capture and upload is realized , but the function of real-time acquisition and display of video stream is not realized .

If the requirement is only to obtain pictures and does not require real-time display of video streams, it can be realized through Java.

3) Python implementation direction

(At present, there is no one on the Internet that can directly fully meet the above three requirements. I realized it through reference, integration, and the Flask framework.)

The reference blog is as follows:
Use the opencv interface cv2.VideoCapture() interface to call the Hikvision robot industrial camera in the python language (you can focus on this blog post!!!)

Call the Hikvision industrial camera through python and store the image, and use opencv to display the real-time image (the data flow problem has been solved)

Python calls Haikang industrial camera and displays it with opencv (overall implementation) (you can focus on this blog post!!!)

pyQT5 learning and using notes 6 pyQt5+opencv displays the dynamic video stream of Haikang GIGE camera (although this method realizes the real-time display of video, it cannot be directly called by the front end)

Real-time display of camera images on the web (python) (this blog post can be focused!!!)

4) Flask related tutorials and blogs
  • https://www.w3cschool.cn/flask/
  • https://dormousehole.readthedocs.io/en/latest/
  • https://blog.csdn.net/weixin_44239541/article/details/89390139
  • https://zhuanlan.zhihu.com/p/104273184
  • https://blog.csdn.net/tulan_xiaoxin/article/details/79132214

3. Python code implementation

1. Python environment

1) Python version

I personally use Python 3.8.5 (this version or higher is recommended)
Python official website address: https://www.python.org/
Installation and configuration can refer to: https://www.runoob.com/python3 /python3-install.html

2) Python virtual environment

Personally, I suggest creating a separate Python virtual environment (such as hikrobotEnv) for this project. If the site fails to connect to the external network, you can directly copy the virtual environment for deployment, which is more convenient.
Of course, if it is not a special case, multiple project projects can share a virtual environment, and you can use virtualenv, anaconda, PyCharm, etc. to create a virtual environment.
Taking virtualenv as an example, the command to create the hikrobotEnv virtual environment is as follows:

virtualenv hikrobotEnv --python=python3.8.5 

If virtualenv is not installed , you can install it with the following command:

pip install virtualenv

For the introduction and use of virtualenv, you can refer to the blog: Detailed explanation of Python virtual environment Virtualenv

2. Test code

1) Grab the picture test code

Precondition: copy the MvImport directory under * C:\Program Files (x86)\MVS\Development\Samples\Python* directory to your own project directory, and create a test file TestGrabImage.py

code show as below:

# -- coding: utf-8 --
import cv2
import sys
import copy
import msvcrt
import numpy as np

from ctypes import *

sys.path.append("./MvImport")
from MvCameraControl_class import *

if __name__ == "__main__":

    deviceList = MV_CC_DEVICE_INFO_LIST()
    tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE

    # ch:枚举设备 | en:Enum device
    ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
    if ret != 0:
        print ("enum devices fail! ret[0x%x]" % ret)
        sys.exit()

    if deviceList.nDeviceNum == 0:
        print ("find no device!")
        sys.exit()

    print ("find %d devices!" % deviceList.nDeviceNum)

    for i in range(0, deviceList.nDeviceNum):
        mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
        if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
            print ("\ngige device: [%d]" % i)
            strModeName = ""
            for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
                strModeName = strModeName + chr(per)
            print ("device model name: %s" % strModeName)

            nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
            nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
            nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
            nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
            print ("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
        elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
            print ("\nu3v device: [%d]" % i)
            strModeName = ""
            for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
                if per == 0:
                    break
                strModeName = strModeName + chr(per)
            print ("device model name: %s" % strModeName)

            strSerialNumber = ""
            for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
                if per == 0:
                    break
                strSerialNumber = strSerialNumber + chr(per)
            print ("user serial number: %s" % strSerialNumber)

    nConnectionNum = 0

    if int(nConnectionNum) >= deviceList.nDeviceNum:
        print ("intput error!")
        sys.exit()

    # ch:创建相机实例 | en:Creat Camera Object
    cam = MvCamera()

    # ch:选择设备并创建句柄 | en:Select device and create handle
    stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents

    ret = cam.MV_CC_CreateHandle(stDeviceList)
    if ret != 0:
        print ("create handle fail! ret[0x%x]" % ret)
        sys.exit()

    # ch:打开设备 | en:Open device
    ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
    if ret != 0:
        print ("open device fail! ret[0x%x]" % ret)
        sys.exit()

    # ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
    if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
        nPacketSize = cam.MV_CC_GetOptimalPacketSize()
        if int(nPacketSize) > 0:
            ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
            if ret != 0:
                print ("Warning: Set Packet Size fail! ret[0x%x]" % ret)
        else:
            print ("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)

    # ch:设置触发模式为off | en:Set trigger mode as off
    ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
    if ret != 0:
        print ("set trigger mode fail! ret[0x%x]" % ret)
        sys.exit()

    # ch:获取数据包大小 | en:Get payload size
    stParam = MVCC_INTVALUE()
    memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))

    ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
    if ret != 0:
        print ("get payload size fail! ret[0x%x]" % ret)
        sys.exit()
        
    nPayloadSize = stParam.nCurValue

    # ch:开始取流 | en:Start grab image
    ret = cam.MV_CC_StartGrabbing()
    if ret != 0:
        print ("start grabbing fail! ret[0x%x]" % ret)
        sys.exit()

    stDeviceList = MV_FRAME_OUT_INFO_EX()
    memset(byref(stDeviceList), 0, sizeof(stDeviceList))
    data_buf = (c_ubyte * nPayloadSize)()

    ret = cam.MV_CC_GetOneFrameTimeout(byref(data_buf), nPayloadSize, stDeviceList, 1000)
    if ret == 0:
        print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stDeviceList.nWidth, stDeviceList.nHeight, stDeviceList.nFrameNum))

        nRGBSize = stDeviceList.nWidth * stDeviceList.nHeight * 3
        stConvertParam=MV_SAVE_IMAGE_PARAM_EX()
        stConvertParam.nWidth = stDeviceList.nWidth
        stConvertParam.nHeight = stDeviceList.nHeight
        stConvertParam.pData = data_buf
        stConvertParam.nDataLen = stDeviceList.nFrameLen
        stConvertParam.enPixelType = stDeviceList.enPixelType
        stConvertParam.nImageLen = stConvertParam.nDataLen
        stConvertParam.nJpgQuality = 70
        stConvertParam.enImageType = MV_Image_Jpeg
        stConvertParam.pImageBuffer = (c_ubyte * nRGBSize)()
        stConvertParam.nBufferSize = nRGBSize
        # ret = cam.MV_CC_ConvertPixelType(stConvertParam)
        print(stConvertParam.nImageLen)
        ret = cam.MV_CC_SaveImageEx2(stConvertParam)
        if ret != 0:
            print ("convert pixel fail ! ret[0x%x]" % ret)
            del data_buf
            sys.exit()
        file_path = "AfterConvert_RGB2.jpg"
        file_open = open(file_path.encode('ascii'), 'wb+')
        img_buff = (c_ubyte * stConvertParam.nImageLen)()
        cdll.msvcrt.memcpy(byref(img_buff), stConvertParam.pImageBuffer, stConvertParam.nImageLen)
        file_open.write(img_buff)
    print ("Save Image succeed!")


    # ch:停止取流 | en:Stop grab image
    ret = cam.MV_CC_StopGrabbing()
    if ret != 0:
        print ("stop grabbing fail! ret[0x%x]" % ret)
        del data_buf
        sys.exit()

    # ch:关闭设备 | Close device
    ret = cam.MV_CC_CloseDevice()
    if ret != 0:
        print ("close deivce fail! ret[0x%x]" % ret)
        del data_buf
        sys.exit()

    # ch:销毁句柄 | Destroy handle
    ret = cam.MV_CC_DestroyHandle()
    if ret != 0:
        print ("destroy handle fail! ret[0x%x]" % ret)
        del data_buf
        sys.exit()

    del data_buf

In the python virtual environment, after running python TestGrabImage.py , an image file named AfterConvert_RGB2.jpg will be generated in the current directory .

If it prompts that the module does not exist during the running process, you can install the corresponding module through the pip install command. It is recommended to use the Tsinghua source . For example, to install opencv-python, the command is as follows:

pip install opencv-python==4.1.2.30 -i https://pypi.tuna.tsinghua.edu.cn/simple
2) Python+Qt realizes video stream real-time display test code

Prerequisite: copy the MvImport directory under * C:\Program Files (x86)\MVS\Development\Samples\Python* directory to your own project directory, and create a test file TestVideoStream.py

code show as below:

# -- coding: utf-8 --
import cv2
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import *
import numpy as np
 
#from CameraControl_header import MV_CC_DEVICE_INFO_LIST
#from mainWindow import Ui_MainWindow  # 导入创建的GUI类
import sys
import threading
import msvcrt
from ctypes import *

sys.path.append("./MvImport")
from MvCameraControl_class import *
from Ui_MainWindow  import *
from CameraParams_header import *
 
class mywindow(QtWidgets.QMainWindow, Ui_MainWindow):
    sendAddDeviceName = pyqtSignal() #定义一个添加设备列表的信号。
    deviceList = MV_CC_DEVICE_INFO_LIST()
    g_bExit = False
    # ch:创建相机实例 | en:Creat Camera Object
    cam = MvCamera()
 
    def connect_and_emit_sendAddDeviceName(self):
        # Connect the sendAddDeviceName signal to a slot.
        self.sendAddDeviceName.connect(self.SelectDevice)
        # Emit the signal.
        self.sendAddDeviceName.emit()
 
    def __init__(self):
        super(mywindow, self).__init__()
        self.setupUi(self)
        self.connect_and_emit_sendAddDeviceName()
        self.butopenCam.clicked.connect(lambda:self.openCam(self.camSelect.currentData()))
        self.butcloseCam.clicked.connect(self.closeCam)
 
        # setting main window geometry
        desktop_geometry = QtWidgets.QApplication.desktop()  # 获取屏幕大小
        main_window_width = desktop_geometry.width()  # 屏幕的宽
        main_window_height = desktop_geometry.height()  # 屏幕的高
        rect = self.geometry()  # 获取窗口界面大小
        window_width = rect.width()  # 窗口界面的宽
        window_height = rect.height()  # 窗口界面的高
        x = (main_window_width - window_width) // 2  # 计算窗口左上角点横坐标
        y = (main_window_height - window_height) // 2  # 计算窗口左上角点纵坐标
        self.setGeometry(x, y, window_width, window_height)  # 设置窗口界面在屏幕上的位置
        # 无边框以及背景透明一般不会在主窗口中用到,一般使用在子窗口中,例如在子窗口中显示gif提示载入信息等等
       # self.setWindowFlags(Qt.FramelessWindowHint)
       # self.setAttribute(Qt.WA_TranslucentBackground)
 
    #打开摄像头。
    def openCam(self,camid):
        self.g_bExit = False
        # ch:选择设备并创建句柄 | en:Select device and create handle
        stDeviceList = cast(self.deviceList.pDeviceInfo[int(camid)], POINTER(MV_CC_DEVICE_INFO)).contents
        ret = self.cam.MV_CC_CreateHandle(stDeviceList)
        if ret != 0:
            print("create handle fail! ret[0x%x]" % ret)
            sys.exit()
        # ch:打开设备 | en:Open device
 
        ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
        if ret != 0:
            print("open device fail! ret[0x%x]" % ret)
            sys.exit()
 
        # ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
        if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
            nPacketSize = self.cam.MV_CC_GetOptimalPacketSize()
            if int(nPacketSize) > 0:
                ret = self.cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
                if ret != 0:
                    print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
            else:
                print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
 
        # ch:设置触发模式为off | en:Set trigger mode as off
        ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
        if ret != 0:
            print("set trigger mode fail! ret[0x%x]" % ret)
            sys.exit()
            # ch:获取数据包大小 | en:Get payload size
        stParam = MVCC_INTVALUE()
        memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
 
        ret = self.cam.MV_CC_GetIntValue("PayloadSize", stParam)
        if ret != 0:
            print("get payload size fail! ret[0x%x]" % ret)
            sys.exit()
        nPayloadSize = stParam.nCurValue
 
        # ch:开始取流 | en:Start grab image
        ret = self.cam.MV_CC_StartGrabbing()
        if ret != 0:
            print("start grabbing fail! ret[0x%x]" % ret)
            sys.exit()
 
        data_buf = (c_ubyte * nPayloadSize)()
 
        try:
            hThreadHandle = threading.Thread(target=self.work_thread, args=(self.cam, data_buf, nPayloadSize))
            hThreadHandle.start()
        except:
            print("error: unable to start thread")
 
    #关闭相机
    def closeCam(self):
        self.g_bExit=True
        # ch:停止取流 | en:Stop grab image
        ret = self.cam.MV_CC_StopGrabbing()
        if ret != 0:
            print("stop grabbing fail! ret[0x%x]" % ret)
            sys.exit()
 
        # ch:关闭设备 | Close device
        ret = self.cam.MV_CC_CloseDevice()
        if ret != 0:
            print("close deivce fail! ret[0x%x]" % ret)
 
        # ch:销毁句柄 | Destroy handle
        ret = self.cam.MV_CC_DestroyHandle()
        if ret != 0:
            print("destroy handle fail! ret[0x%x]" % ret)
 
    def work_thread(self,cam=0, pData=0, nDataSize=0):
        stFrameInfo = MV_FRAME_OUT_INFO_EX()
        memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
        while True:
            QIm = np.asarray(pData)  # 将c_ubyte_Array转化成ndarray得到(3686400,)
            QIm = QIm.reshape((2048, 3072, 1))  # 根据自己分辨率进行转化
                # print(temp)
                # print(temp.shape)
            QIm = cv2.cvtColor(QIm, cv2.COLOR_BGR2RGB)  # 这一步获取到的颜色不对,因为默认是BRG,要转化成RGB,颜色才正常
            pyrD1=cv2.pyrDown(QIm) #向下取样
            pyrD2 = cv2.pyrDown(pyrD1)  # 向下取样
            image_height, image_width, image_depth = pyrD2.shape  # 读取图像高宽深度
            pyrD3 = QImage(pyrD2, image_width, image_height,  image_width * image_depth,QImage.Format_RGB888)
            self.label.setPixmap(QPixmap.fromImage(pyrD3))
            #cv2.namedWindow("result", cv2.WINDOW_AUTOSIZE)
            #cv2.imshow("result", temp)
            #if cv2.waitKey(1) & 0xFF == ord('q'):
            #    break
 
            ret = cam.MV_CC_GetOneFrameTimeout(pData, nDataSize, stFrameInfo, 1000)
            if ret == 0:
                print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
                    stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
            else:
                print("no data[0x%x]" % ret)
            if self.g_bExit == True:
                del pData
                break
 
 
    #获得所有相机的列表存入cmbSelectDevice中
    def SelectDevice(self):
        '''选择所有能用的相机到列表中,
             gige相机需要配合 sdk 得到。
        '''
        #得到相机列表
 
        tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
    # ch:枚举设备 | en:Enum device
        ret = MvCamera.MV_CC_EnumDevices(tlayerType, self.deviceList)
        if ret != 0:
            print("enum devices fail! ret[0x%x]" % ret)
            sys.exit()
        if self.deviceList.nDeviceNum == 0:
            print("find no device!")
            sys.exit()
 
        print("Find %d devices!" % self.deviceList.nDeviceNum)
        for i in range(0, self.deviceList.nDeviceNum):
            mvcc_dev_info = cast(self.deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
            if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
                print("\ngige device: [%d]" % i)
                strModeName = ""
                for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
                    strModeName = strModeName + chr(per)
                print("device model name: %s" % strModeName)
                self.camSelect.addItem(strModeName,i) #写入设备列表。
 
    def pushbutton_function(self):
        #do some things
        Img=cv2.imread('JP1.JPG') #通过opencv读入一张图片
        image_height, image_width, image_depth=Img.shape #读取图像高宽深度
        QIm=cv2.cvtColor(Img,cv2.COLOR_BGR2RGB)
        QIm=QImage(QIm.data, image_width, image_height,  image_width * image_depth,QImage.Format_RGB888)
        self.label.setPixmap(QPixmap.fromImage(QIm))
 
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = mywindow()
    window.show()
    sys.exit(app.exec_())

Ui_MainWindow.py used in the above code is placed in the MvImport directory.

code show as below:

# -*- coding: utf-8 -*-
 
# Form implementation generated from reading ui file 'mainWindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.
 
 
from PyQt5 import QtCore, QtGui, QtWidgets
 
 
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(589, 530)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.camSelect = QtWidgets.QComboBox(self.centralwidget)
        self.camSelect.setObjectName("camSelect")
        self.verticalLayout.addWidget(self.camSelect)
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.butopenCam = QtWidgets.QPushButton(self.centralwidget)
        self.butopenCam.setObjectName("butopenCam")
        self.verticalLayout.addWidget(self.butopenCam)
        self.butcloseCam = QtWidgets.QPushButton(self.centralwidget)
        self.butcloseCam.setObjectName("butcloseCam")
        self.verticalLayout.addWidget(self.butcloseCam)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 589, 23))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
 
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
 
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "TextLabel"))
        self.butopenCam.setText(_translate("MainWindow", "打开相机"))
        self.butcloseCam.setText(_translate("MainWindow", "关闭相机"))

In the python virtual environment, execute python TestVideoStream.py to run, and you can see the effect in the following video:
insert image description here

If it prompts that the module does not exist during the running process, you can install the corresponding module through the pip install command. It is recommended to use the Tsinghua source. For example, to install opencv-python, the command is as follows:

pip install opencv-python==4.1.2.30 -i https://pypi.tuna.tsinghua.edu.cn/simple

It can be seen from the effect in the video: Although the real-time acquisition and display of the video stream is realized, it cannot be directly connected to the front-end display, which is not friendly enough.


4. Final code (full version)

The previous two test codes are only for test reference, not the final code! ! !

The code in this paragraph is the final code, you can focus on it for reference! ! !

Using Python + OpenCV + Flask to achieve real-time acquisition of video, and return it to the front end through GET request for real-time display, or grab pictures, save and upload.

1. Add dependent files

Copy the MvImport directory under the * C:\Program Files (x86)\MVS\Development\Samples\Python* directory to your own project directory.

2. Install the plug-in DirectShow

1) Enter the third-party plug-in DirectShow path
cd C:\Program Files (x86)\MVS\Development\ThirdPartyPlatformAdapter\DirectShow\x64\MvDSS2

insert image description here

2) Install DirectShow

Run InstallDSSvc_x64.bat with administrator privileges

Note: I am using a 64-bit Windows operating system, and the running code is also based on 64-bit operation, so I use this version; the 32-bit system also exists in the previous directory, and you can install it according to the actual situation.

3. Install dependent modules

1) Install opencv-python
pip install opencv-python==4.1.2.30 -i https://pypi.tuna.tsinghua.edu.cn/simple

I personally use Python 3.8.5 , and opencv-python corresponds to version 4.1.2.30 .
If the version of opencv-python used is too high, the following error may be reported:

cv2.error: Unknown C++ exception from OpenCV code.
2) Install Flask
pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple

You can also specify the version of Flask, such as:

pip install flask=2.2.2 -i https://pypi.tuna.tsinghua.edu.cn/simple

4. Core code

The project code structure is as follows:
insert image description here

0)index.html

Create a templates directory in the project to store index.hml

<html>
  <head>
  </head>
  <body>
      <h1>拍照预览</h1>
    <img src="{
     
     { url_for('startPreview') }}" width="50%">
  </body>
</html>
1)JsonResponse.py

Used to standardize the data type returned to the front end, the code is as follows:

# -- coding: utf-8 --

class JsonResponse(object):
    """
    统一的json返回格式
    """

    def __init__(self, code, msg, data):
        self.code = code
        self.msg = msg
        self.data = data

    @classmethod
    def success(cls, code=0, msg='success', data=None):
        return cls(code, msg, data)

    @classmethod
    def error(cls, code=-1, msg='error', data=None):
        return cls(code, msg, data)

    def to_dict(self):
        return {
    
    
            "code": self.code,
            "msg": self.msg,
            "data": self.data
        }
2)JsonFlask.py

Used to redefine the data return format, the code is as follows:

# -- coding: utf-8 --

from flask import Flask, jsonify
from JsonResponse import *

class JsonFlask(Flask):
    def make_response(self, rv):
        """视图函数可以直接返回: list、dict、None"""
        if rv is None or isinstance(rv, (list, dict)):
            rv = JsonResponse.success(rv)

        if isinstance(rv, JsonResponse):
            rv = jsonify(rv.to_dict())

        return super().make_response(rv)
3)HikRobotCamera.py

The core code file implements the following functions:
start previewing video stream , stop previewing video stream , obtain pictures , record logs , etc.

code show as below:

# -- coding: utf-8 --
import cv2
from flask import Flask, render_template, Response

import sys
import msvcrt
import base64
import datetime
import logging

sys.path.append("./MvImport")
from MvCameraControl_class import *
from JsonResponse import *
from JsonFlask import *

logging.basicConfig(level=logging.DEBUG,#控制台打印的日志级别
                    filename='hikrobot.log',
                    filemode='a',##模式,有w和a,w就是写模式,每次都会重新写日志,覆盖之前的日志
                    #a是追加模式,默认如果不写的话,就是追加模式
                    format=
                    '%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
                    #日志格式
                    )

# 这里配置一下 template_folder为当前目录,不然可以找不到 index.html
app = JsonFlask(__name__, template_folder='.')

# index
@app.route('/')
def index():
    return render_template('./templates/index.html')

# 获取码流
def generate(cap):
    # 捕获异常信息
    try:
        while True:
            # 如果是关闭相机,先退出取视频流的循环
            global open
            if (not open):
                break;
            retgrab = cap.grab()
            if retgrab == True:
                logging.debug("Grab true")
            ret1, frame = cap.retrieve()
            # print(type(frame))
            if frame is None:
                logging.error("frame is None")
                continue
            ret1, jpeg = cv2.imencode('.jpg', frame)
            jpg_frame = jpeg.tobytes()
            yield (b'--frame\r\n'
                b'Content-Type: image/jpeg\r\n\r\n' + jpg_frame + b'\r\n')
    except Exception as e:
        logging.error("generate error: %s" % str(e))


# 开始预览
@app.route('/startPreview')
def startPreview():
    logging.info("======================================")
    logging.info("start to preview video stream, current_time: " + str(datetime.datetime.now()))
    # 全局变量,用于控制获取视频流的开关状态
    global open
    open = True

    # 全局变量,获取视频连接
    global cap
    cap = cv2.VideoCapture(1)

    if False == cap.isOpened():
        logging.error("can't open camera")
        quit()
    else:
        logging.info("start to open camera")

    logging.info("open camera ok")

    # 分辨率设置 3072*2048(海康机器人工业相机 MV-CU060-10GM)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 3072)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2048)
    # 帧率配置
    cap.set(cv2.CAP_PROP_FPS, 15)   
    return Response(generate(cap), mimetype='multipart/x-mixed-replace;boundary=frame')

# 停止预览
@app.route('/stopPreview')
def stopPreview():
    logging.info("======================================")
    logging.info("stop to preview video stream, current_time: " + str(datetime.datetime.now()))
    logging.info("start to close camera")

    # 全局变量,用于停止循环
    global open
    open = False

    logging.info("release resources start")
    # 全局变量,用于释放相机资源
    try:
        global cap
        cap.release()
        cv2.destroyAllWindows()
    except Exception as e:
        logging.error("stopPreview error: %s" % str(e))
    logging.info("release resources end")
    logging.info("camera closed successfully, current_time: " + str(datetime.datetime.now()))
    logging.info("======================================")
    return "stop to preview"


@app.route('/openAndSave')
def openAndSave():
    logging.info("======================================")
    logging.info("start to grab image, current_time: " + str(datetime.datetime.now()))
    code = 100000
    msg = "连接相机时发生错误"
    # img_base64 = None
    try:
        deviceList = MV_CC_DEVICE_INFO_LIST()
        tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE

        # ch:枚举设备 | en:Enum device
        ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
        if ret != 0:
            logging.error("enum devices fail! ret[0x%x]" % ret)
            sys.exit()

        if deviceList.nDeviceNum == 0:
            logging.error("find no device!")
            sys.exit()

        logging.info("find %d devices!" % deviceList.nDeviceNum)

        for i in range(0, deviceList.nDeviceNum):
            mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
            if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
                logging.info("\ngige device: [%d]" % i)
                strModeName = ""
                for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
                    strModeName = strModeName + chr(per)
                logging.info("device model name: %s" % strModeName)

                nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
                nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
                nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
                nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
                logging.info("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
            elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
                logging.info("\nu3v device: [%d]" % i)
                strModeName = ""
                for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
                    if per == 0:
                        break
                    strModeName = strModeName + chr(per)
                logging.info("device model name: %s" % strModeName)

                strSerialNumber = ""
                for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
                    if per == 0:
                        break
                    strSerialNumber = strSerialNumber + chr(per)
                logging.info("user serial number: %s" % strSerialNumber)

        nConnectionNum = 0

        if int(nConnectionNum) >= deviceList.nDeviceNum:
            logging.error("intput error!")
            sys.exit()

        # ch:创建相机实例 | en:Creat Camera Object
        cam = MvCamera()

        # ch:选择设备并创建句柄 | en:Select device and create handle
        stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents

        ret = cam.MV_CC_CreateHandle(stDeviceList)
        if ret != 0:
            logging.error("create handle fail! ret[0x%x]" % ret)
            sys.exit()

        # ch:打开设备 | en:Open device
        ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
        if ret != 0:
            logging.error("open device fail! ret[0x%x]" % ret)
            sys.exit()

        # ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
        if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
            nPacketSize = cam.MV_CC_GetOptimalPacketSize()
            if int(nPacketSize) > 0:
                ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
                if ret != 0:
                    logging.warn("Warning: Set Packet Size fail! ret[0x%x]" % ret)
            else:
                logging.warn("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)

        # ch:设置触发模式为off | en:Set trigger mode as off
        ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
        if ret != 0:
            logging.error("set trigger mode fail! ret[0x%x]" % ret)
            sys.exit()

        # ch:获取数据包大小 | en:Get payload size
        stParam = MVCC_INTVALUE()
        memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))

        ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
        if ret != 0:
            logging.error("get payload size fail! ret[0x%x]" % ret)
            sys.exit()
            
        nPayloadSize = stParam.nCurValue

        # ch:开始取流 | en:Start grab image
        ret = cam.MV_CC_StartGrabbing()
        if ret != 0:
            logging.error("start grabbing fail! ret[0x%x]" % ret)
            sys.exit()

        stDeviceList = MV_FRAME_OUT_INFO_EX()
        memset(byref(stDeviceList), 0, sizeof(stDeviceList))
        data_buf = (c_ubyte * nPayloadSize)()
        
        ret = cam.MV_CC_GetOneFrameTimeout(byref(data_buf), nPayloadSize, stDeviceList, 1000)
        if ret == 0:
            logging.info("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stDeviceList.nWidth, stDeviceList.nHeight, stDeviceList.nFrameNum))

            nRGBSize = stDeviceList.nWidth * stDeviceList.nHeight * 3
            stConvertParam=MV_SAVE_IMAGE_PARAM_EX()
            stConvertParam.nWidth = stDeviceList.nWidth
            stConvertParam.nHeight = stDeviceList.nHeight
            stConvertParam.pData = data_buf
            stConvertParam.nDataLen = stDeviceList.nFrameLen
            stConvertParam.enPixelType = stDeviceList.enPixelType
            stConvertParam.nImageLen = stConvertParam.nDataLen
            stConvertParam.nJpgQuality = 70
            stConvertParam.enImageType = MV_Image_Jpeg
            stConvertParam.pImageBuffer = (c_ubyte * nRGBSize)()
            stConvertParam.nBufferSize = nRGBSize
            # ret = cam.MV_CC_ConvertPixelType(stConvertParam)
            logging.info("nImageLen: %d" % stConvertParam.nImageLen)
            ret = cam.MV_CC_SaveImageEx2(stConvertParam)
            if ret != 0:
                logging.error("convert pixel fail ! ret[0x%x]" % ret)
                del data_buf
                sys.exit()
            #file_path = "AfterConvert_RGB2.jpg"
            #file_open = open(file_path, 'wb+')
            #file_open = open(file_path.encode('utf8'), 'wb')
            img_buff = (c_ubyte * stConvertParam.nImageLen)()
            cdll.msvcrt.memcpy(byref(img_buff), stConvertParam.pImageBuffer, stConvertParam.nImageLen)
            #file_open.write(img_buff)
            
            # 对返回的图片进行 base64 格式转换
            img_base64 = "data:image/jpg;base64," + str(base64.b64encode(img_buff)).split("'")[1]
            code = 200
            msg = "success"
        logging.info("Save Image succeed!")

        # ch:停止取流 | en:Stop grab image
        ret = cam.MV_CC_StopGrabbing()
        if ret != 0:
            logging.error("stop grabbing fail! ret[0x%x]" % ret)
            del data_buf
            sys.exit()

        # ch:关闭设备 | Close device
        ret = cam.MV_CC_CloseDevice()
        if ret != 0:
            logging.error("close deivce fail! ret[0x%x]" % ret)
            del data_buf
            sys.exit()

        # ch:销毁句柄 | Destroy handle
        ret = cam.MV_CC_DestroyHandle()
        if ret != 0:
            logging.error("destroy handle fail! ret[0x%x]" % ret)
            del data_buf
            sys.exit()

        del data_buf
    except Exception as e:
        logging.error("openAndSave error: %s" % str(e))
    # print("openAndSave finished, current_time: " + str(datetime.datetime.now()))  
  
    return JsonResponse(code, msg, img_base64)

# 执行web服务, 端口号可自行修订
logging.info("start to run camera app, current_time: " + str(datetime.datetime.now()))
app.run(host='0.0.0.0', port=65432, debug=True, threaded=True)

5. Run the code

Enter the Python virtual environment and execute the following command:

python HikRobotCamera.py

6. Function verification

1) Start preview

You can use http://127.0.0.1:65432/startPreview to display the real-time preview effect of the video with the img tag on the front end .

http://127.0.0.1:65432/startPreview
2) Stop preview

Since the Hikvision robot industrial camera (MV-CU060-10GM) used can only create one connection , after previewing the real-time video, you need to call this interface to release camera resources to avoid long-term occupation of resources.

http://127.0.0.1:65432/stopPreview
3) Get pictures

The image in base64 format is returned , and the front end can directly receive and display it, and call the upload interface to save it.

http://127.0.0.1:65432/openAndSave

7. Operation effect

Due to internet speed issues and the lack of an aperture on the camera, the camera was a little blurry. The running effect is as follows:
insert image description here

8. Log view

Return to the production log file hikrobot.log in the project directory , the content is as follows:
insert image description here

9. Easter eggs

The improved and optimized version of HikRobotCamera.py simplifies the method of obtaining pictures and is more concise. code show as below:

# -- coding: utf-8 --
import cv2
from flask import Flask, render_template, Response

import sys
import msvcrt
import base64
import datetime
import logging

sys.path.append("./MvImport")
from MvCameraControl_class import *
from JsonResponse import *
from JsonFlask import *

logging.basicConfig(level=logging.DEBUG,#控制台打印的日志级别
                    filename='hikrobot.log',
                    filemode='a',##模式,有w和a,w就是写模式,每次都会重新写日志,覆盖之前的日志
                    #a是追加模式,默认如果不写的话,就是追加模式
                    format=
                    '%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
                    #日志格式
                    )

# 这里配置一下 template_folder为当前目录,不然可以找不到 index.html
app = JsonFlask(__name__, template_folder='.')

# index
@app.route('/')
def index():
    return render_template('./templates/index.html')

# 获取码流
def generate(cap):
    # 捕获异常信息
    try:
        while True:
            # 如果是关闭相机,先退出取视频流的循环
            global open
            if (not open):
                break;
            retgrab = cap.grab()
            if retgrab == True:
                logging.debug("Grab true")
            ret1, frame = cap.retrieve()
            # print(type(frame))
            if frame is None:
                logging.error("frame is None")
                continue
            ret1, jpeg = cv2.imencode('.jpg', frame)
            jpg_frame = jpeg.tobytes()
            yield (b'--frame\r\n'
                b'Content-Type: image/jpeg\r\n\r\n' + jpg_frame + b'\r\n')
    except Exception as e:
        logging.error("generate error: %s" % str(e))


# 开始预览
@app.route('/startPreview')
def startPreview():
    logging.info("================== startPreview start ====================")
    logging.info("start to preview video stream, current_time: " + str(datetime.datetime.now()))
    # 全局变量,用于控制获取视频流的开关状态
    try:
        global open
        open = True

        # 全局变量,获取视频连接
        global cap
        cap = cv2.VideoCapture(1)

        if False == cap.isOpened():
            logging.error("startPreview -- can't open camera")
            quit()
        else:
            logging.info("startPreview -- start to open camera")

        logging.info("startPreview -- open camera ok")

        # 分辨率设置 3072*2048(海康机器人工业相机 MV-CU060-10GM)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 3072)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2048)
        # 帧率配置
        cap.set(cv2.CAP_PROP_FPS, 15)   
        response = Response(generate(cap), mimetype='multipart/x-mixed-replace;boundary=frame')
    except Exception as e:
        logging.error("startPreview error: %s" % str(e))
    return response

# 停止预览
@app.route('/stopPreview')
def stopPreview():
    logging.info("================== stopPreview start ====================")
    logging.info("stop to preview video stream, current_time: " + str(datetime.datetime.now()))
    logging.info("start to close camera")

    # 全局变量,用于停止循环
    global open
    open = False

    logging.info("release resources start")
    # 全局变量,用于释放相机资源
    try:
        global cap
        cap.release()
        cv2.destroyAllWindows()
    except Exception as e:
        logging.error("stopPreview error: %s" % str(e))
    logging.info("release resources end")
    logging.info("camera closed successfully, current_time: " + str(datetime.datetime.now()))
    logging.info("=================== stopPreview end ===================")
    return "stop to preview"

# 获取base64图片
@app.route('/grabImage')
def grabImage():
    code = 100000
    msg = "连接相机时发生错误"
    # 捕获异常信息
    try:
        print("grabImage -- stopPreview start")
        stopPreview()
        print("grabImage -- stopPreview end")

        # 全局变量,获取视频连接
        global cap
        cap = cv2.VideoCapture(1)

        if False == cap.isOpened():
            logging.error("can't open camera")
            quit()
        else:
            logging.info("grabImage -- start to open camera")

        logging.info("grabImage -- open camera ok")

        # 分辨率设置 3072*2048(海康机器人工业相机 MV-CU060-10GM)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 3072)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2048)
        # 帧率配置
        cap.set(cv2.CAP_PROP_FPS, 15)   
        
        retgrab1 = cap.grab()
        if retgrab1 == True:
            logging.debug("grabImage -- Grab true")
        ret1, frame = cap.retrieve()
        ret1, jpeg = cv2.imencode('.jpg', frame)
        img_buff = jpeg.tobytes()

        # 对返回的图片进行 base64 格式转换
        img_base64 = "data:image/jpg;base64," + str(base64.b64encode(img_buff)).split("'")[1]
        code = 200
        msg = "success"

        stopPreview()
    except Exception as e:
        logging.error("generate error: %s" % str(e))

    return JsonResponse(code, msg, img_base64)

# 获取base64图片
@app.route('/hik/openAndSave')
def openAndSave():
    code = 100000
    msg = "连接相机时发生错误"
    # 捕获异常信息
    try:
        logging.info("================= openAndSave start =====================")
        # 拍照之前,先停止预览
        stopPreview()
        logging.info("start to grab image, current_time: " + str(datetime.datetime.now()))

        # 全局变量,获取视频连接
        global cap
        cap = cv2.VideoCapture(1)

        if False == cap.isOpened():
            logging.error("openAndSave -- can't open camera")
            quit()
        else:
            logging.info("openAndSave -- start to open camera")

        logging.info("openAndSave -- open camera ok")

        # 分辨率设置 3072*2048(海康机器人工业相机 MV-CU060-10GM)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 3072)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2048)
        # 帧率配置
        cap.set(cv2.CAP_PROP_FPS, 15)   
        
        retgrab1 = cap.grab()
        if retgrab1 == True:
            logging.debug("openAndSave -- Grab true")
        ret1, frame = cap.retrieve()
        ret1, jpeg = cv2.imencode('.jpg', frame)
        img_buff = jpeg.tobytes()

        # 对返回的图片进行 base64 格式转换
        img_base64 = "data:image/jpg;base64," + str(base64.b64encode(img_buff)).split("'")[1]
        code = 200
        msg = "success"

        stopPreview()
        logging.info("================= openAndSave end =====================")
    except Exception as e:
        logging.error("openAndSave error: %s" % str(e))

    return JsonResponse(code, msg, img_base64)

@app.route('/hik/openAndSave2')
def openAndSave2():
    # 拍照之前,先停止预览
    stopPreview()
    logging.info("======================================")
    logging.info("start to grab image, current_time: " + str(datetime.datetime.now()))
    code = 100000
    msg = "连接相机时发生错误"
    # img_base64 = None
    try:
        deviceList = MV_CC_DEVICE_INFO_LIST()
        tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE

        # ch:枚举设备 | en:Enum device
        ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
        if ret != 0:
            logging.error("enum devices fail! ret[0x%x]" % ret)
            sys.exit()

        if deviceList.nDeviceNum == 0:
            logging.error("find no device!")
            sys.exit()

        logging.info("find %d devices!" % deviceList.nDeviceNum)

        for i in range(0, deviceList.nDeviceNum):
            mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
            if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
                logging.info("\ngige device: [%d]" % i)
                strModeName = ""
                for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
                    strModeName = strModeName + chr(per)
                logging.info("device model name: %s" % strModeName)

                nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
                nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
                nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
                nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
                logging.info("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
            elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
                logging.info("\nu3v device: [%d]" % i)
                strModeName = ""
                for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
                    if per == 0:
                        break
                    strModeName = strModeName + chr(per)
                logging.info("device model name: %s" % strModeName)

                strSerialNumber = ""
                for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
                    if per == 0:
                        break
                    strSerialNumber = strSerialNumber + chr(per)
                logging.info("user serial number: %s" % strSerialNumber)

        nConnectionNum = 0

        if int(nConnectionNum) >= deviceList.nDeviceNum:
            logging.error("intput error!")
            sys.exit()

        # ch:创建相机实例 | en:Creat Camera Object
        cam = MvCamera()

        # ch:选择设备并创建句柄 | en:Select device and create handle
        stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents

        ret = cam.MV_CC_CreateHandle(stDeviceList)
        if ret != 0:
            logging.error("create handle fail! ret[0x%x]" % ret)
            sys.exit()

        # ch:打开设备 | en:Open device
        ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
        if ret != 0:
            logging.error("open device fail! ret[0x%x]" % ret)
            sys.exit()

        # ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
        if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
            nPacketSize = cam.MV_CC_GetOptimalPacketSize()
            if int(nPacketSize) > 0:
                ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
                if ret != 0:
                    logging.warn("Warning: Set Packet Size fail! ret[0x%x]" % ret)
            else:
                logging.warn("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)

        # ch:设置触发模式为off | en:Set trigger mode as off
        ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
        if ret != 0:
            logging.error("set trigger mode fail! ret[0x%x]" % ret)
            sys.exit()

        # ch:获取数据包大小 | en:Get payload size
        stParam = MVCC_INTVALUE()
        memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))

        ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
        if ret != 0:
            logging.error("get payload size fail! ret[0x%x]" % ret)
            sys.exit()
            
        nPayloadSize = stParam.nCurValue

        # ch:开始取流 | en:Start grab image
        ret = cam.MV_CC_StartGrabbing()
        if ret != 0:
            logging.error("start grabbing fail! ret[0x%x]" % ret)
            sys.exit()

        stDeviceList = MV_FRAME_OUT_INFO_EX()
        memset(byref(stDeviceList), 0, sizeof(stDeviceList))
        data_buf = (c_ubyte * nPayloadSize)()
        
        ret = cam.MV_CC_GetOneFrameTimeout(byref(data_buf), nPayloadSize, stDeviceList, 1000)
        if ret == 0:
            logging.info("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stDeviceList.nWidth, stDeviceList.nHeight, stDeviceList.nFrameNum))

            nRGBSize = stDeviceList.nWidth * stDeviceList.nHeight * 3
            stConvertParam=MV_SAVE_IMAGE_PARAM_EX()
            stConvertParam.nWidth = stDeviceList.nWidth
            stConvertParam.nHeight = stDeviceList.nHeight
            stConvertParam.pData = data_buf
            stConvertParam.nDataLen = stDeviceList.nFrameLen
            stConvertParam.enPixelType = stDeviceList.enPixelType
            stConvertParam.nImageLen = stConvertParam.nDataLen
            stConvertParam.nJpgQuality = 70
            stConvertParam.enImageType = MV_Image_Jpeg
            stConvertParam.pImageBuffer = (c_ubyte * nRGBSize)()
            stConvertParam.nBufferSize = nRGBSize
            # ret = cam.MV_CC_ConvertPixelType(stConvertParam)
            logging.info("nImageLen: %d" % stConvertParam.nImageLen)
            ret = cam.MV_CC_SaveImageEx2(stConvertParam)
            if ret != 0:
                logging.error("convert pixel fail ! ret[0x%x]" % ret)
                del data_buf
                sys.exit()
            #file_path = "AfterConvert_RGB2.jpg"
            #file_open = open(file_path, 'wb+')
            #file_open = open(file_path.encode('utf8'), 'wb')
            img_buff = (c_ubyte * stConvertParam.nImageLen)()
            cdll.msvcrt.memcpy(byref(img_buff), stConvertParam.pImageBuffer, stConvertParam.nImageLen)
            #file_open.write(img_buff)
            
            # 对返回的图片进行 base64 格式转换
            img_base64 = "data:image/jpg;base64," + str(base64.b64encode(img_buff)).split("'")[1]
            code = 200
            msg = "success"
        logging.info("Save Image succeed!")

        # ch:停止取流 | en:Stop grab image
        ret = cam.MV_CC_StopGrabbing()
        if ret != 0:
            logging.error("stop grabbing fail! ret[0x%x]" % ret)
            del data_buf
            sys.exit()

        # ch:关闭设备 | Close device
        ret = cam.MV_CC_CloseDevice()
        if ret != 0:
            logging.error("close deivce fail! ret[0x%x]" % ret)
            del data_buf
            sys.exit()

        # ch:销毁句柄 | Destroy handle
        ret = cam.MV_CC_DestroyHandle()
        if ret != 0:
            logging.error("destroy handle fail! ret[0x%x]" % ret)
            del data_buf
            sys.exit()

        del data_buf
    except Exception as e:
        logging.error("openAndSave error: %s" % str(e))
    # print("openAndSave finished, current_time: " + str(datetime.datetime.now()))  
  
    return JsonResponse(code, msg, img_base64)

# 执行web服务, 端口号可自行修订
logging.info("start to run camera app, current_time: " + str(datetime.datetime.now()))
if __name__ == '__main__':
    try:
        app.run(host='0.0.0.0', port=65432, debug=True, threaded=True)
    except Exception as e:
        logging.error("app error: %s" % str(e))


Guess you like

Origin blog.csdn.net/aikudexiaohai/article/details/130078959
Recommended