背景

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_ExclusiveMvCamCtrldll = 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_boolimport cv2 as cv
import numpy as npfrom 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 MvCamerafrom 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信息")passelse:print("FJ信息数量",len(infos))for info in infos:print("FJ信息", info)passendTime = 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 deviceret = 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)).contentsif 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:breakstrModeName = strModeName + chr(per)print("device model name: %s" % strModeName)strSerialNumber = ""for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:if per == 0:breakstrSerialNumber = 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 Objectcam = MvCamera()# ch:选择设备并创建句柄 | en:Select device and create handlestDeviceList = cast(deviceList.pDeviceInfo[int(constants.ConnectionCameraNum)], POINTER(MV_CC_DEVICE_INFO)).contentsret = cam.MV_CC_CreateHandle(stDeviceList)if ret != 0:print("create handle fail! ret[0x%x]" % ret)sys.exit()# ch:打开设备 | en:Open deviceret = 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 offret = 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:设置亮度为100ret = cam.MV_CC_SetIntValue("Brightness", constants.Brightness)if ret != 0:print("设置亮度为100 失败! ret[0x%x]" % ret)sys.exit()# ch:设置像素格式为PixelType_Gvsp_YUV422_Packedret = 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 imageret = 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 = TruehThreadHandle.join()# ch:停止取流 | en:Stop grab imageret = cam.MV_CC_StopGrabbing()if ret != 0:print("stop grabbing fail! ret[0x%x]" % ret)sys.exit()# ch:关闭设备 | Close deviceret = cam.MV_CC_CloseDevice()if ret != 0:print("close deivce fail! ret[0x%x]" % ret)sys.exit()# ch:销毁句柄 | Destroy handleret = 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 海康工业相机相关推荐

  1. python调用海康工业相机并用opencv显示(整体实现)

    python语言调用海康机器人(hikrobotics)工业相机 系列文章目录 第一章 python调用海康工业相机并用opencv显示(整体实现) 第二章 python 调用海康工业相机图像获取方式 ...

  2. Python调用海康工业相机:包含相机参数修改、彩色原图显示(不失真)

    [引言] 海康相机作为目前工业检测.视觉定位等领域应用较为广泛的国产品牌相机,其搭配有一套专用视觉软件VM,而在完成具有复杂场景.复杂任务的科研项目时依靠其VM算法平台提供的视觉算法往往无法满足项目要 ...

  3. python 调用海康工业相机调试后出现被占用问题

    问题描述: 在使用海康工业网口相机开发过程中,调试阶段异常结束程序调试时,经常出现相机被占用的异常情况,无论是使用海康的客户端界面连接相机还是使用调试代码连接相机,都没有权限再次打开相机,需要相机断电 ...

  4. 海康工业相机 python 图像数据转opencv格式

    python调用海康工业相机,官方给出的SDK里没有转到opencv格式的例子,网上找了下,只有一篇,转换部分的代码如下: temp = np.asarray(pData2) # 将c_ubyte_A ...

  5. Python 实现海康机器人工业相机 MV-CU060-10GM 的实时显示视频流及拍照功能

    一.背景介绍 1.最近项目中需要给客户对接海康机器人工业相机 MV-CU060-10GM: 2.客户要求通过部署的管理平台,可以在页面上实现如下功能: 1)相机视频流开始预览: 2)相机视频流停止预览 ...

  6. python 海康工业相机二次开发 参数获取

    对于调用海康相机的整个流程,下面的这个博客说的很详细了,我一开始也是看的这个 python调用海康工业相机并用opencv显示(整体实现)_J&A~ing的博客-CSDN博客_python调用 ...

  7. 调用海康工业相机图像获取方式之主动取流(getimagebuffer )

    本文仅作记录!原文链接!:https://blog.csdn.net/qq_39570716/article/details/114304110 目录 一.使用前提 二.主动取流(官方例程说明) 1. ...

  8. python语言下使用pyqt中的QImage对海康工业相机获取到的图像进行显示

    系列文章目录 文章目录 目录 系列文章目录 文章目录 前言 二.使用海康工业相机API接口GetImagebuffer配合pyqt中的QImage类进行界面显示 1.海康工业相机API接口GetIma ...

  9. Opencv如何打开使用海康工业相机

    文章转载:python语言下使用opencv接口cv2.VideoCapture()接口调用海康机器人工业相机 一.Opencv接口 使用 opencv 的官方接口 VideoCapture() 接口 ...

最新文章

  1. c#测试字符串是否为GUID的几种方法
  2. 大数据教父Micheal Stonebraker告诉你大数据的秘密
  3. Skpi List跳表
  4. linux 查看cpu
  5. start.s中的.balignl 16,0xdeadbeef
  6. 前端学习(3026):vue+element今日头条管理-让导航栏和路由对应起来
  7. 解决办法!!!!UnsupportedClassVersionError Unsupported major.minor version 52.0
  8. 电脑没声音解决方法,win11
  9. VMware View:VMware 专为 Kindle Fire 打造的视图客户端
  10. Android studio 三大模拟器比较
  11. QingYan相机隐私政策
  12. 【爬坑记录】记录搭建fabric 遇到的问题-network e2ecli_default not found
  13. 我们为什么需要 DAO 操作系统?
  14. 原理 + 代码 | Apriori 算法与基于关联规则的购物篮推荐
  15. macbook配置java环境变量_MAC安装JDK及环境变量配置
  16. 与网友“阵春风”交流
  17. 其实人是有潜力的,比如吃西瓜
  18. 【文件】Notepad3下载和配置
  19. 【Promise】自定义 - 手写Promise - Promise.all - Promise(executor)
  20. 周期函数的导数周期(含证明)

热门文章

  1. 使用“QQ聊天记录专家2007”查看QQ聊天图片案例
  2. [DOTA2地图编辑器记录]
  3. 利用青龙面板给天翼云盘实现天翼云盘自动签到
  4. 书评:Mastering KVM virtualization;Humble
  5. python-parsel
  6. FPGA实战-两个D触发器的疑惑
  7. cuda10.0最佳实践的翻译(八到十七及两个附录)
  8. 3d机器学习笔记5--TriangleMesh处理(1)
  9. 如何学习java c语言_20165237 学习基础和C语言基础调查
  10. 为什么GetAsyncKeyState() 0x8000