美文网首页
2022-05-16 Python OpenCV 海康工业相机

2022-05-16 Python OpenCV 海康工业相机

作者: 颈椎以上瘫痪 | 来源:发表于2022-05-16 14:06 被阅读0次

    背景

    Python OpenCV连接海康工业相机做图像处理

    环境

    Python:3.9.9
    OpenCV:4.5.5
    numpy:1.19.3


    2022-05-16_112138.png

    相机

    海康:MV-CE200-10GC
    协议:GigE


    2022-05-16_112542.png

    MVS

    海康网站下载的相机调试工具,可以预览相机和设置相机的一些参数。

    网站:https://www.hikrobotics.com/cn

    下载你相机型号相关的安装包,这个要是有困难可以找海康售后帮忙,你提供的相机型号,工作人员会知道你下载和操作,海康售后挺好的,当初笔者不熟悉的时候也是很耐心的指导我操作。

    主机通过网线连接相机,主机的IP地址选择自动分配,这样就会和相机在一个网段内。打开MVS工具,会识别主机中的网卡,如果该网卡中有相机连接,会显示出来。

    2022-05-16_114020.png

    选中相机后点击连接按钮,可以连接到该相机

    2022-05-16_114116.png

    连接成功后,界面右侧会出现相机的属性列表

    2022-05-16_114225.png

    点击预览按钮,界面中间会出现相机的预览画面

    2022-05-16_114506.png

    笔者记得第一次连接的时候,好像预览画面中什么都没有是黑色的,如果你也是这样不必着急,因为相机需要设置。首先相机上面有1个调亮度和1个调焦距的物理旋钮。你需要将物理按钮的属性调整到合适位置,确保那个环节没有问题。再一个这个工具中可以设置相机的属性,笔者这里在相机的默认属性基础上调节了4个属性:

    常用属性
    * 自动曝光:连续
    * 自动增益:连续
    * 亮度:100
    高级属性
    * 像素格式:UV 422 Packed
    

    完成上述操作,应该能正常预览相机的画面。

    Python

    在Python中连接工业相机获取帧图像处理,我们首先需要连接相机,然后获取到每一帧图像,将图像送给业务层。

    在Python中需要连接相机,需要借助海康提供的.dll文件和其API文件

    这些API文件和.dll文件,在安装完MVS软件后会获得。


    2022-05-16_115925.png 2022-05-16_120008.png

    笔者的工程结构如下:


    2022-05-16_115825.png

    将5个API文件和.dll文件夹里面的所有文件都拷贝到你的工程中,存放路径你可以自己定义。但是在MvCameraControl_class.py文件中,会有一行代码是输入MvCameraControl.dll文件路径的:

    from ctypes import *
    
    from org.venus.std.src.pro.input.hk2000w.api.CameraParams_const import MV_ACCESS_Exclusive
    
    MvCamCtrldll = WinDLL("./api/dll/MvCameraControl.dll")
    

    这里请根据你自己的存放目录填写。补充一下,在Python中填写相对路径时在路径前面加./,就是你项目运行的根目录。这个连接代码是参考示例程序的,示例程序里面填写的路径就是一个MvCameraControl.dll文件,但是笔者在使用过程中只导入这一个文件程序运行有错误,肯定的是这个文件找到了,但是这个文件中也引用到了其他的.dll文件,有人说通过什么工具之类的可以查看这个.dll文件引用到了其他的具体哪一个.dll文件,笔者对那个操作不熟悉,就没有那样做,而是把那个.dll文件夹中所有的文件都拷贝过来,约60M,省事。

    将这些文件拷贝到项目中就可以开始写代码了:

    import msvcrt
    import sys
    import threading
    import time
    from ctypes import POINTER, sizeof, byref, memset, c_ubyte, cdll, cast, c_bool
    
    import cv2 as cv
    import numpy as np
    
    from org.venus.std.src.config import constants
    from org.venus.std.src.pro.input.hk2000w import watch
    from org.venus.std.src.pro.input.hk2000w.api.CameraParams_const import MV_GIGE_DEVICE, MV_USB_DEVICE, MV_ACCESS_Exclusive
    from org.venus.std.src.pro.input.hk2000w.api.CameraParams_header import MV_CC_DEVICE_INFO_LIST, MV_CC_DEVICE_INFO, \
        MV_TRIGGER_MODE_OFF, MV_FRAME_OUT, MV_EXPOSURE_AUTO_MODE_CONTINUOUS, MV_GAIN_MODE_CONTINUOUS
    from org.venus.std.src.pro.input.hk2000w.api.MvCameraControl_class import MvCamera
    
    from org.venus.std.src.pro.input.hk2000w.api.PixelType_header import PixelType_Gvsp_YUV422_Packed
    
    
    # 为线程定义一个函数
    def work_thread(cam=0, pData=0, nDataSize=0):
        stOutFrame = MV_FRAME_OUT()
        memset(byref(stOutFrame), 0, sizeof(stOutFrame))
        while True:
            startFrame = time.perf_counter()
            ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000)
            # print("相机像素格式(PixelType_header.py文件中根据编码找到对应的格式)",stOutFrame.stFrameInfo.enPixelType)
            if None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 17301505:
                print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
                    stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
                pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)()
                cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,
                                   stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)
                data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight),
                                     dtype=np.uint8)
                image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
            elif None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 17301514:
                print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
                    stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
                pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)()
                cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,
                                   stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)
                data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight),
                                     dtype=np.uint8)
                image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
            elif None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 35127316:
                print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
                    stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
                pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 3)()
                cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,
                                   stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 3)
                data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 3),
                                     dtype=np.uint8)
                image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
            elif None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 34603039:
                print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
                    stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
                pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 2)()
                cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,
                                   stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 2)
                data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 2),
                                     dtype=np.uint8)
                image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
            else:
                print("no data[0x%x]" % ret)
            nRet = cam.MV_CC_FreeImageBuffer(stOutFrame)
    
            endFrame = time.perf_counter()
            print("相机一帧时间",str(endFrame-startFrame))
    
    
    # 需要显示的图像数据转换
    def image_control(data, stFrameInfo):
        if stFrameInfo.enPixelType == 17301505:
            image = data.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
            image_show(image=image, name=stFrameInfo.nHeight)
        elif stFrameInfo.enPixelType == 17301514:
            data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
            image = cv.cvtColor(data, cv.COLOR_BAYER_GB2RGB)
            image_show(image=image, name=stFrameInfo.nHeight)
        elif stFrameInfo.enPixelType == 35127316:
            data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
            image = cv.cvtColor(data, cv.COLOR_RGB2BGR)
            image_show(image=image, name=stFrameInfo.nHeight)
        elif stFrameInfo.enPixelType == 34603039:
            data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
            image = cv.cvtColor(data, cv.COLOR_YUV2BGR_Y422)
            image_show(image=image, name=stFrameInfo.nHeight)
    
    
    # 显示图像
    def image_show(image, name):
        startTime = time.perf_counter()
        infos = watch.run(image)
        if infos is None:
            print("没有识别到FJ信息")
            pass
        else:
            print("FJ信息数量",len(infos))
            for info in infos:
                print("FJ信息", info)
                pass
        endTime = time.perf_counter()
        #我们可以使用time.perf_counter()方法来查找程序的执行时间。
        #方法time.perf_counter()返回以秒为单位的时间浮点值
        print("拿到图像时间", str(startTime),"图像处理完毕时间", str(startTime),"耗时", str(endTime-startTime))
        if constants.WaitNextFrame:
            cv.waitKey()
    
    def run():
        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 = input("please input the number of the device to connect:") #输入一个相机编号进行连接
    
        if int(constants.ConnectionCameraNum) >= 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(constants.ConnectionCameraNum)], 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)
    
        stBool = c_bool(False)
        ret = cam.MV_CC_GetBoolValue("AcquisitionFrameRateEnable", stBool)
        if ret != 0:
            print("get AcquisitionFrameRateEnable fail! ret[0x%x]" % ret)
            sys.exit()
    
        # 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()
    
    
        '''
        设置一些属性:
        自动曝光:连续
        亮度:100
        自动增益:连续
        像素格式:YUV 422 Packed
        '''
    
        # ch:设置自动曝光为连续
        ret = cam.MV_CC_SetEnumValue("ExposureAuto", MV_EXPOSURE_AUTO_MODE_CONTINUOUS)
        if ret != 0:
            print("设置自动曝光为连续 失败! ret[0x%x]" % ret)
            sys.exit()
    
        # ch:设置自动增益为连续
        ret = cam.MV_CC_SetEnumValue("GainAuto", MV_GAIN_MODE_CONTINUOUS)
        if ret != 0:
            print("设置自动增益为连续 失败! ret[0x%x]" % ret)
            sys.exit()
    
        # ch:设置亮度为100
        ret = cam.MV_CC_SetIntValue("Brightness", constants.Brightness)
        if ret != 0:
            print("设置亮度为100 失败! ret[0x%x]" % ret)
            sys.exit()
    
        # ch:设置像素格式为PixelType_Gvsp_YUV422_Packed
        ret = cam.MV_CC_SetEnumValue("PixelFormat", PixelType_Gvsp_YUV422_Packed)
        if ret != 0:
            print("设置像素格式为PixelType_Gvsp_YUV422_Packed 失败! ret[0x%x]" % ret)
            sys.exit()
    
        # ch:开始取流 | en:Start grab image
        ret = cam.MV_CC_StartGrabbing()
        if ret != 0:
            print("start grabbing fail! ret[0x%x]" % ret)
            sys.exit()
    
        try:
            hThreadHandle = threading.Thread(target=work_thread, args=(cam, None, None))
            hThreadHandle.start()
        except:
            print("error: unable to start thread")
    
        print("press a key to stop grabbing.")
        msvcrt.getch()
    
        g_bExit = True
        hThreadHandle.join()
    
        # ch:停止取流 | en:Stop grab image
        ret = cam.MV_CC_StopGrabbing()
        if ret != 0:
            print("stop grabbing fail! ret[0x%x]" % ret)
            sys.exit()
    
        # ch:关闭设备 | Close device
        ret = cam.MV_CC_CloseDevice()
        if ret != 0:
            print("close deivce fail! ret[0x%x]" % ret)
            sys.exit()
    
        # ch:销毁句柄 | Destroy handle
        ret = cam.MV_CC_DestroyHandle()
        if ret != 0:
            print("destroy handle fail! ret[0x%x]" % ret)
            sys.exit()
    
    if __name__ =="__main__":
        print("连接hk工业相机图像")
        run()
    

    上述代码是工程结构中/org/venus/std/src/pro/input/hk2000w/main.py文件的内容。代码不多,可以简单解读一下。

    程序入口main函数中执行run函数,在run函数中查找所有的相机设备,虽然我平常只是连接着一个相机,但是的你主机中可以连接多个相机,在找到所有的相机后,相机列表中每个相机会有个编号标识符。通过相机编号连接相机创建句柄并打开相机,然后为相机设置一些属性,就可以开始获取相机的视频流了,run函数中通过如下2行代码将取流操作执行到work_thread线程中,主线程就等待取流线程的工作,结束后就关闭设备,销毁句柄等释放资源操作。

    hThreadHandle = threading.Thread(target=work_thread, args=(cam, None, None))
    hThreadHandle.start()
    

    work_thread取流线程是一个while True操作,在获取视频流时后根据上文中设置相机的一个属性(像素格式),来执行不同的操作,因为不同的像素格式,视频流转换为图像的操作是不一样的,所以上文中设置像素格式的操作也不是任意设置的,笔者设置的值YUV 422 Packed是因为在示例程序中列出了4种像素格式的转换代码,YUV 422 Packed是其中的一个,所以笔者使用了这一个,代码中使用的匹配方式是如(34603039)类型的数据,这个数据对应的像素格式可以在API文件PixelType_header.py中进行匹配,看看你使用的究竟是哪种像素格式。在对像素格式匹配后,程序执行到image_control函数,将视频流数据传送到image_control函数,继续对像素格式进行匹配,并将视频流转换成图像对象,程序继续执行到image_show函数,这个函数中传递过来的就是图像对象了,可以将图像对象传递到业务层进行处理。

    补充一下:在代码中连接相机后可以设置一些属性,是示例代码中只有一个设置触发模式的示例,比如笔者还需要设置一些属性:自动曝光:连续/亮度:100/自动增益:连续/像素格式:YUV 422 Packed,设置属性是调用MvCameraControl_class.py文件中提供的接口函数,接口中并没有直接设置某一个属性的函数,接口是设置某一类型的调用方式。大致有MV_CC_SetIntValue设置整形数据,MV_CC_SetEnumValue设置枚举数据,MV_CC_SetFloatValue设置小数数据,MV_CC_SetBoolValue设置开关数据,MV_CC_SetStringValue设置字符串数据等。这里不过多阐述,当你有这个业务时可以自行研究。

    根据笔者想要设置的属性来看,要用到设置枚举和整形的接口。调用设置枚举的接口,需要传入一个key和一个枚举,枚举就是具体的值,这个可以在CameraParams_header.py文件中找到你所要设置的参数的枚举值。那么这个key哪里来呢?key是个字符串,笔者尝试像实例中设置触发模式一样TriggerMode表示触发模式,那么我想设置自动曝光模式应该就是ExposureAutoMode,但是执行时这里却有错误,很显然没有这个key。那么就要再次用到MVS工具了,在工具中连接相机后找到属性树菜单,列表里面都是英文名称对应着相机的属性,找到你要设置相机参数的英文名称,比如截图中的自动曝光就是Exposure Auto属性,点击属性在列表下方会出现该属性的信息,其中有个Node Name属性是ExposureAuto,这个属性就是我们在代码中使用API设置相机属性的key,其他属性同理都可以在这里找到。

    2022-05-16_135920.png

    至此Python OpenCV中连接海康工业相机就可以调试通过。

    项目地址:https://gitee.com/premeditate/Tools-Camera

    相关文章

      网友评论

          本文标题:2022-05-16 Python OpenCV 海康工业相机

          本文链接:https://www.haomeiwen.com/subject/mhniurtx.html