通过python调用海康威视工业摄像头并进行图像存储,同时使用opencv实时图像显示。

1:图像存储方式

先说情况,本人是做视觉检测的需要高倍率摄像头进行实时检测,也就是需要深度学习进行图片数据处理,但是这个又是python来进行分析,而海康威视主要程序代码是以C为主的,传过来的数据我也尝试的去解析都是不能转化成python的BGR图像。
一开始参照了:通过cv2调用海康威视摄像头,但这个不能调用工业摄像头,通过官方给一个400什么软件要激活摄像头,可是却并不能检测到工业摄像头,通过mvs软件调用到摄像头地址进行测试也无法获取到摄像头数据,这和我之前说的,工业摄像头是基于C编写的,python的调用都是使用C的包,经过大量询问工作人员(最后还是课题组有老主顾,才找到一个稍微靠谱的),给了一个不太一样的代码自己分析后才成功,中间还是删掉了一些感觉没什么用的东西。
先给整体代码,然后再逐步分析代码功能。

# -- 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

首先看import,代码如下

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

这是整个代码的核心,把文件放到其他地方的时候一定要按照原有代码路径放置,文件里面内容如下,具体可以下载MVS里面例程里面有:

海康 python接口 python调用海康摄像头_mvc


海康 python接口 python调用海康摄像头_python_02


其中的代码都有注释,具体要刨根问底的分析我建议直接入职海康威视可能会更快一点,我们只是使用它,所以这一系列步骤缺一不可。到后面就是比较让人苦恼的图片数据流获取,具体代码如下:

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!")

我们只要看上面file path这一步就好了,这一步骤是设定自己的存储路径以及图片名字,设定好以后直接便能在每一次拍摄存储图片。在此处可以不断循环这一段代码不断的进行取帧!也可以实现图像的实时获取,效率上会更低一些。不过对于做学术来说,能够实现即可。

2:OPENCV图像实时显示

最近实在是没有办法,要做出一个方案进行实时监测,又对客服进行狂轰滥炸,结果啥也不懂啥也不会。。海康客服真实一言难尽。。。。最后找到了这一片博主的文章:pyqt5实时显示图像终于看到了解决方法。

工业相机的曝光度可以大范围的调控,导致很多质量检测的小伙伴都需要使用低曝光度或者高倍率的实时监测,好在海康威视可以通过MVS调控之后将更改的数据存储在相机里面,不需要每次使用都进行调控。

海康 python接口 python调用海康摄像头_计算机视觉_03


其中你需要记住几个数据,这几个数据需要后续使用,一个是从MVS找到的相机宽和高,其中工业相机还存在着只能获得灰度图像的相机,这个也是需要自己注意。

下面代码建议直接使用sample - python-grabimage里面的源代码进行修改,修改点如下

# ch:开始取流 | en:Start grab image
    ret = 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=work_thread, args=(cam, data_buf, nPayloadSize))
        hThreadHandle.start()
    except:
        print ("error: unable to start thread")

在开始取流这一块有一个线程模块,这里的data_buf需要删减成如代码所示,不然无法进行识别。

def work_thread(cam=0, pData=0, nDataSize=0):
    stFrameInfo = MV_FRAME_OUT_INFO_EX()
    memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
    while True:
        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))
            temp = np.array(pData)  # 将c_ubyte_Array转化成ndarray得到(3686400,)
            temp = temp.reshape((2048, 2592, 1))# 根据自己分辨率进行转化
            temp = cv2.resize(temp,(1280,1500))
            # print(temp)
            print(temp.shape)
            temp = cv2.cvtColor(temp, cv2.COLOR_BGR2RGB)  # 这一步获取到的颜色不对,因为默认是BRG,要转化成RGB,颜色才正常
            cv2.namedWindow("result", cv2.WINDOW_AUTOSIZE)
            cv2.imshow("result", temp)
        else:
            print ("no data[0x%x]" % ret)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        if g_bExit == True:
                break

目前评论区存在的问题总结:
1.图片乱码
在线程中,引用的可能是pData的全局变量,在进行数据处理得同时可能但应该是在图片完整获取之后再对数据进行转换而不是直接不断转换,这样可能会导致数据没有更新完毕,把图片转换放到了if ret==0里面即可。
如果仍然存在问题,可以采用评论区中有人采用取帧法来替代,就是将单取一张图像的流程不断重复来实现数据流。
2.灰度图像问题
在进行图像获取的时候,一定要在MVS上先进行相机测试,看看设置中长和宽,并且是否为彩色图像。如果没有问题,可以看看
temp = temp.reshape((2048, 2592, 1))
这一步需要把最后的通道变为3(如果彩色图像写1也会报其它错误),获得的是BGR图像(也可能每个相机获得的图像类不同,需要多确认以下是BGR、YUV、H246等)
3.相机打开错误
device model name: MV-xxxxx-50UC
user serial number: 00xxxx0847
open device fail! ret[0x80000305]
一般这种情况可能是你打开了mvs连接了相机,这个时候是没有办法用代码使用相机的!!请退出mvs。
第二种情况可能就是端口不对,设置的端口0,1,2都可以试试,不要插网卡!

这个文章看的人真的很多啊····之后会陆陆续续根据大伙的问题继续更新,有些大伙存在的问题我是真的无能为力,因为我已经毕业工作了,后续会把ubuntu的安装流程写上来。谢谢大家,请多多点赞和收藏!!!!这是我实时更新动力,也能更好帮助大家。

——————————————————————————分割线牢骚区域——————————————————————————