环境说明:Ubuntu16.04
海康威视SDK包:MVS-2.1.0_x86_64_20201228.tar.gz
如若在Windows环境下配合Visual Studio使用请移至:
海康威视工业相机SDK二次开发(VS+Opencv+QT+海康SDK+C++)(一)
海康威视工业相机SDK二次开发(VS+Opencv+QT+海康SDK+C++)(二)
记录自己的学习过程,方便以后查阅,若有错误或遗漏,欢迎大佬指正补充。
这里写目录标题
- 1.准备
- 1.1 相关资料
- 1.2 Debug常备
- 1.3 针对错误码的解析
- MV_E_HANDLE 0x80000000 错误或无效的句柄
- MV_E_SUPPORT 0x80000001 不支持的功能
- MV_E_BUFOVER 0x80000002 缓存已满
- MV_E_CALLORDER 0x80000003 函数调用顺序有误
- MV_E_PARAMETER 0x80000004 错误的参数
- MV_E_RESOURCE 0x80000006 资源申请失败
- MV_E_NODATA 0x80000007 无数据
- MV_E_NOENOUGH_BUF 0x8000000A 传入的内存空间不足
- MV_E_UNKNOW 0x800000FF 未知的错误
- MV_E_GC_GENERIC 0x80000100 通用错误
- MV_E_GC_RANGE 0x80000102 值超出范围
- MV_E_GC_ACCESS 0x80000106 节点访问条件有误
- MV_E_GC_TIMEOUT 0x80000107 超时
- MV_E_ACCESS_DENIED 0x80000203 设备无访问权限
- MV_E_NETER 0x80000206 网络相关错误
- MV_E_IP_CONFLICT 0x80000221 设备IP冲突
- MV_E_USB_READ 0x80000300 读USB出错
- MV_E_USB_WRITE 0x80000301 写USB出错
- MV_E_USB_DRIVER 0x80000305 驱动不匹配或者未装驱动
- 2.通过海康相机SDK熟悉C接口取图流程和取图方式
- 2.1 设备连接接口流程
- 2.2 相机取图——主动取流
- 2.3 设置相机的一些参数
- 3.将相机抓取到的图像转为Mat格式,方便后续使用
- 4.将Mat格式图像转为QImage格式,用于UI界面上控件的显示
1.准备
首先是安装海康威视的MVS软件,VMware虚拟机中Ubuntu16.04系统下通过MVS运行海康威视工业相机。
1.1 相关资料
- 工业相机SDK(C)开发指南
这是最重要的资料,里面有(环境配置:SDK编程环境配置,包括网络配置、驱动安装等)、(编程导引:相机连接流程和取图方式介绍)、相机的常用节点等等。 - 相机的常用节点查询
这部分主要是用于SDK提供API接口中的参数设置,如设置曝光模式、曝光值、增益模式、增益值、外触发等等。
根据我实际开发过程中常用的相机节点值如下: - CameraParams.h:包含编程需要的所有结构体、宏定义和枚举量
- MvCameraControl.h:包含所有控制相机的API接口
- 相关示例程序:
1.2 Debug常备
如果遇到问题,比如相机未正常打开、相机无法取图等等,及时打印每次调用SDK接口返回的输出值,对照错误码定义去排查问题(记得将输出值转化为十六进制)事半功倍。
如果函数正常完成而没有检测到任何错误,则返回值为MV_OK,否则返回错误码 。
1.3 针对错误码的解析
调试时如果遇到常见的错误码,如0x80000000——错误或无效的句柄,很自然的我们就会去排查是不是对于句柄的操作有误,好歹给了我们一些方向,但有些错误码如0x80000006——资源申请失败或者通用错误这些,仅凭类型信息描述根本无法提供方向去排查,这里列出我网上找到的和我实际遇到的及其解决方法供大家参考。
参考:https://zhuanlan.zhihu.com/p/437976222
MV_E_HANDLE 0x80000000 错误或无效的句柄
解析:-2147483648/0x80000000 无效句柄。
常见问题如下:
用户没有申请句柄,直接调用接口,新手常犯的错误,要引导去学习我们接口使用流程。
用户创建了句柄,但是其他地方销毁了句柄,用户没有注意到,需要仔细排查代码。
MV_E_SUPPORT 0x80000001 不支持的功能
解析:-2147483647/0x80000001
SDK接口的bayer空域降噪、无损压缩、色彩矫正等ISP功能,需要配合CS-Pro系列相机支持。
格式转化时,不同格式的相互转化,超出了算法能力集,详情请仔细查阅SDK接口说明。
MV_E_BUFOVER 0x80000002 缓存已满
解析:-2147483646/0x80000002
常见于gige驱动启动时报错,低版本SDK在某些网卡上面易发,驱动启动失败后,走socket协议发送接收相机数据,效率低,CPU负载大 推荐使用SDK3.5版本解决此问题。
MV_E_CALLORDER 0x80000003 函数调用顺序有误
解析:-2147483645/0x80000003
sdk接口调用,有一定流程顺序,例如getimagebuffer在startgrabing之前调用,就违反了接口流程,就会报错顺序调用错误 还比如,没有调用startgrabing接口,就去调用频繁调用stopgrabing接口,也会报此错误。
MV_E_PARAMETER 0x80000004 错误的参数
解析:-2147483644/0x80000004
常见问题: 常见于格式转化、图像保存等需要补充数据结构的接口调用,部分参数传入错误,或者没有传入,这个时候,要去仔细检查参数的传入是否正确。
MV_E_RESOURCE 0x80000006 资源申请失败
解析:-2147483654/0x80000004
这个问题我在另一台设备上部署相机服务端时,枚举相机第一步就报了这个错误,最后是安装了MVS后才得以解决,推测是缺了什么依赖库,但ldd的时候并未显示缺失的库。
2022-9-14记录:今天又遇到了这个问题,原来是缺了libMVGigEVisionSDK.so.3.1.3.0
和libMVGigEVisionSDK.so
这两个库。猜测海康这里是dlopen的这两个库,巨坑!!!通过ldd根本看不出什么错误。
MV_E_NODATA 0x80000007 无数据
解析:-2147483641/0x80000007无数据
相机帧率低,用户调用主动取流接口getimagebuffer/getoneframetimout频率高于相机出图频率,且超时时间短,没有拿到图片,此时应该打印相机帧号,如帧号连续则为正常现象。
相机处于触发模式,没有触发信号给到相机,此时,应该排查用户是否给了软触发或者硬触发信号。
相机停流,此时,建议打开MVS,观察相机状态。
耐心寻找规律,看看是否跟packsize、scpd、取流超时时间不合理所致。
MV_E_NOENOUGH_BUF 0x8000000A 传入的内存空间不足
解析:-2147483638/0x8000000A
1.用户自行开辟的内存大小,小于当前相机图像所需要的图像大小,例如用mono8的图像大小,接收RGB的图像。
2.用户定义的内存大小,中途更换了分辨率更高的相机,导致所需内存较多
3.相机开启了chunk功能,用户开辟缓存大小,仅考虑了图像宽高像素格式,没有考虑到chunk。
解决方法:
排查内存开辟大小,建议使用相机payloadsize大小
检查相机图像格式
关闭相机chunk等功能
MV_E_UNKNOW 0x800000FF 未知的错误
解析:-2147483137/0x800000FF GenICam未知错误
未知错误,形成原因较难分析,建议如下:
更新最新版本的sdk
开启sdk日志等级,通过日志分析形成原因
MV_E_GC_GENERIC 0x80000100 通用错误
解析:-2147483392/0x80000100
通用接口调用,关键词写错,例如曝光:ExposureTime,拼写错误就会报错。
第三方相机链接,例如迈德威视相机链接MVS,也会报通用错误,这是因为迈德威视不是标准的genicam协议的相机导致的。
接口类型用错,例如曝光时float型节点,我们使用了一个int型的节点接口进行读写,访问,那么也会报通用错误。
MV_E_GC_RANGE 0x80000102 值超出范围
解析:-2147483390/0x80000102
接口传入的参数值,超出相机接受范围,例如,曝光存在上下限,图像宽高存在步进,没有按照步进进行设置等等。
MV_E_GC_ACCESS 0x80000106 节点访问条件有误
解析:-2147483386/0x80000106
常见的问题类型有: 相机节点不存在或者无法访问,例如,自动曝光,在手动曝光情况下,自动曝光节点会被隐藏,或者其他相机存在这个参数,而使用的相机无此参数,例如线扫相机的行频参数等等,还比如说部分相机无此功能,例如event参数,部分相机固件暂不支持用户调用。
MV_E_GC_TIMEOUT 0x80000107 超时
解析:-2147483385/0x80000107
GVCP命名包回复超时,一般出现在网络环境不好的情况下,此时应该调用接口加大gvcp命令包等待时间(MV_GIGE_GetGvcpTimeout()),持续出现该报错,应该排查网络环境问题。
MV_E_ACCESS_DENIED 0x80000203 设备无访问权限
解析:-2147483133/0x80000203常见问题有:
相机被其他软件打开占用,关闭其他软件,检查设备管理器中,可能存在的残留进程。
代码debug下,心跳时间问题,等待60s后,可以重新打开(此问题参考心跳问题解决方法,可缩短打开时间)。
其余问题,例如一上电就打不开相机,需要重新插拔网线、USB线,就是其他问题,需要具体问题,具体分析。
MV_E_NETER 0x80000206 网络相关错误
解析:-2147483130/0x80000206
此类报错非常常见,主要分以下集中:
相机掉线,能够在日志中发现大量的206报错,此时需要去区分掉线原因,结合相机上电时间、心跳时间、相机权限,日志等信息,综合判断。
网线异常,导致相机掉线,也需要具体问题分析。
MV_E_IP_CONFLICT 0x80000221 设备IP冲突
解析:-2147483103/0x80000221
常见于IP设置时,当前ip已经被其他设备使用,需要更换ip重新设置。
MV_E_USB_READ 0x80000300 读USB出错
解析:-2147482880/0x80000300
USB读取相机信息失败,此类问题较为复制,与USB接口稳定性、线缆长度、电磁环境相关,往往不好分析,可以尝试插拔一下,或者更换USB接口尝试。
MV_E_USB_WRITE 0x80000301 写USB出错
解析:-2147482879/0x80000301
同0x80000300一样,不好分析,可以尝试插拔一下,或者更换USB接口尝试。
MV_E_USB_DRIVER 0x80000305 驱动不匹配或者未装驱动
解析: -2147482875/0x80000305
同0x80000300一样,问题复杂,除了更换USB接口外,还可以尝试更换sdk版本。
2.通过海康相机SDK熟悉C接口取图流程和取图方式
个人总结:句柄(void *handle)是相机开发中重要的一环,SDK中的接口中,大部分都是针对句柄的操作,如果有多个相机,要将每个相机和其对应的句柄关联好,防止后期使用的过程中未及时释放句柄、造成混乱等。
2.1 设备连接接口流程
对设备进行操作,实现图像采集、参数配置等功能,需要先连接设备(打开设备),具体流程如下图所示:
1.调用 MV_CC_EnumDevices() 枚举子网内指定传输协议对应的所有设备。可以通过 pstDevList 在结构 MV_CC_DEVICE_INFO_LIST 中找到设备的信息。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">#include "MvCameraControl.h"
-
- void main()
- {
- unsigned int nTLayerType = MV_GIGE_DEVICE | MV_USB_DEVICE;
-
- MV_CC_DEVICE_INFO_LIST m_stDevList = {0};
- int nRet = MV_CC_EnumDevices(nTLayerType, &m_stDevList);
- if (MV_OK != nRet)
- {
- printf("error: EnumDevices fail [%x]\n", nRet);
- }
- }
- </code></span></span>
关于搜索到的所有相机设备信息在 MV_CC_DEVICE_INFO_LIST结构体中,
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">/// \~chinese 设备信息列表 \~english Device Information List
- typedef struct _MV_CC_DEVICE_INFO_LIST_
- {
- unsigned int nDeviceNum; ///< [OUT] \~chinese 在线设备数量 \~english Online Device Number
- MV_CC_DEVICE_INFO* pDeviceInfo[MV_MAX_DEVICE_NUM]; ///< [OUT] \~chinese 支持最多256个设备 \~english Support up to 256 devices
-
- }MV_CC_DEVICE_INFO_LIST;
- </code></span></span>
通过MV_CC_DEVICE_INFO结构体可以访问每个设备的详细信息,
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">/// \~chinese 设备信息 \~english Device info
- typedef struct _MV_CC_DEVICE_INFO_
- {
- unsigned short nMajorVer; ///< [OUT] \~chinese 主要版本 \~english Major Version
- unsigned short nMinorVer; ///< [OUT] \~chinese 次要版本 \~english Minor Version
- unsigned int nMacAddrHigh; ///< [OUT] \~chinese 高MAC地址 \~english High MAC Address
- unsigned int nMacAddrLow; ///< [OUT] \~chinese 低MAC地址 \~english Low MAC Address
-
- unsigned int nTLayerType; ///< [OUT] \~chinese 设备传输层协议类型,e.g. MV_GIGE_DEVICE \~english Device Transport Layer Protocol Type, e.g. MV_GIGE_DEVICE
-
- unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved
-
- union
- {
- MV_GIGE_DEVICE_INFO stGigEInfo; ///< [OUT] \~chinese GigE设备信息 \~english GigE Device Info
- MV_USB3_DEVICE_INFO stUsb3VInfo; ///< [OUT] \~chinese USB设备信息 \~english USB Device Info
- MV_CamL_DEV_INFO stCamLInfo; ///< [OUT] \~chinese CameraLink设备信息 \~english CameraLink Device Info
- // more ...
- }SpecialInfo;
-
- }MV_CC_DEVICE_INFO;
- </code></span></span>
这里以我使用的网口相机GigE设备为例,要获取到设备的详细内容:
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">// 我通过std::map<std::string, std::tuple<void*,MV_CC_DEVICE_INFO*>>m_dev_info_list_;来存储枚举到的所示设备。
- // 第一个参数的设备的名称,这个可以自己定义,方便后面对哪一个相机进行操作;
- // 第二个参数是句柄
- // 第三个参数就是MV_CC_DEVICE_INFO设备信息的结构体
- MV_CC_DEVICE_INFO* pstMVDevInfo = std::get<1>(m_dev_info_list_[dev_name]);
-
- if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE) {
- std::string model =
- (char*)pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName;
- std::string cam_ip = "Current IP Address: " ;
- int nIp1 =
- ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >>
- 24);
- int nIp2 =
- ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >>
- 16);
- int nIp3 =
- ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >>
- 8);
- int nIp4 =
- (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);
- }
- </code></span></span>
有些参数我也没搞懂是什么信息。。。
2.在打开指定设备之前,调用 MV_CC_IsDeviceAccessible() 检查指定设备是否可访问。 (这一步在实际使用中并未用到)
3.调用 MV_CC_CreateHandle() 以创建设备句柄。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">#include "MvCameraControl.h"
-
- void main()
- {
- int nRet = -1;
- void* m_handle = NULL;
-
- //枚举子网内指定的传输协议对应的所有设备
- unsigned int nTLayerType = MV_GIGE_DEVICE | MV_USB_DEVICE;
- MV_CC_DEVICE_INFO_LIST m_stDevList = {0};
- int nRet = MV_CC_EnumDevices(nTLayerType, &m_stDevList);
- if (MV_OK != nRet)
- {
- printf("error: EnumDevices fail [%x]\n", nRet);
- return;
- }
-
- int i = 0;
- if (m_stDevList.nDeviceNum == 0)
- {
- printf("no camera found!\n");
- return;
- }
-
- //选择查找到的第一台在线设备,创建设备句柄
- int nDeviceIndex = 0;
-
- MV_CC_DEVICE_INFO m_stDevInfo = {0};
- memcpy(&m_stDevInfo, m_stDevList.pDeviceInfo[nDeviceIndex], sizeof(MV_CC_DEVICE_INFO));
-
- nRet = MV_CC_CreateHandle(&m_handle, &m_stDevInfo);
-
- if (MV_OK != nRet)
- {
- printf("error: CreateHandle fail [%x]\n", nRet);
- return;
- }
-
- //...其他处理
-
- //销毁句柄,释放资源
- nRet = MV_CC_DestroyHandle(m_handle);
- if (MV_OK != nRet)
- {
- printf("error: DestroyHandle fail [%x]\n", nRet);
- return;
- }
- }
- </code></span></span>
4.调用 MV_CC_OpenDevice() 打开设备。
去该API定义后发现,后面两个参数有默认值,所以实际使用中只需要输入第一个参数设备的句柄即可。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">/********************************************************************//**
- * @~chinese
- * @brief 打开设备
- * @param handle [IN] 设备句柄
- * @param nAccessMode [IN] 访问权限
- * @param nSwitchoverKey [IN] 切换访问权限时的密钥
- * @return 成功,返回MV_OK;错误,返回错误码
- * @remarks 根据设置的设备参数,找到对应的设备,连接设备。\n
- 调用接口时可不传入nAccessMode和nSwitchoverKey,此时默认设备访问模式为独占权限。目前设备暂不支持MV_ACCESS_ExclusiveWithSwitch、MV_ACCESS_ControlWithSwitch、MV_ACCESS_ControlSwitchEnable、MV_ACCESS_ControlSwitchEnableWithKey这四种抢占模式。\n
- 对于U3V设备,nAccessMode、nSwitchoverKey这两个参数无效。 */
- #ifndef __cplusplus
- MV_CAMCTRL_API int __stdcall MV_CC_OpenDevice(IN void* handle, IN unsigned int nAccessMode, IN unsigned short nSwitchoverKey);
- #else
- MV_CAMCTRL_API int __stdcall MV_CC_OpenDevice(IN void* handle, IN unsigned int nAccessMode = MV_ACCESS_Exclusive, IN unsigned short nSwitchoverKey = 0);
- #endif
- </code></span></span>
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">int nRet = MV_CC_OpenDevice(m_handle);
- </code></span></span>
5.调用 MV_CC_CloseDevice() 关闭设备。
6.调用 MV_CC_DestroyHandle() 来销毁句柄并释放资源。
2.2 相机取图——主动取流
SDK提供主动获取图像的接口,用户可以在开启取流后直接调用此接口获取图像,也可以使用异步方式(线程、定时器等)获取图像。示例代码详见 GrabImage.cpp 和 GrabImage_HighPerformance.cpp 。
- 主动获取图像有两种方式(两种方式不能同时使用):
方式一:调用 MV_CC_StartGrabbing() 开始采集,需要自己开启一个buffer,然后在应用层循环调用 MV_CC_GetOneFrameTimeout() 获取指定像素格式的帧数据,获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。
方式二:调用 MV_CC_StartGrabbing() 开始采集,然后在应用层调用 MV_CC_GetImageBuffer() 获取指定像素格式的帧数据,然后调用 MV_CC_FreeImageBuffer() 释放buffer,获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。 - 主动取图方式使用的场景:
主动取图方式需要先调用 MV_CC_StartGrabbing() 启动图像采集。上层应用程序需要根据帧率,控制好调用主动取图接口的频率。两种主动取图方式都支持设置超时时间,SDK内部等待直到有数据时返回,可以增加取流平稳性,适合用于对平稳性要求较高的场合。 - 两种主动取图方式的区别:
a、 MV_CC_GetImageBuffer() 需要与 MV_CC_FreeImageBuffer() 配套使用,当处理完取到的数据后,需要用 MV_CC_FreeImageBuffer() 接口将pstFrame内的数据指针权限进行释放。
b、 MV_CC_GetImageBuffer() 与 MV_CC_GetOneFrameTimeout() 相比,有着更高的效率。且其取流缓存的分配是由sdk内部自动分配的,而 MV_CC_GetOneFrameTimeout() 接口是需要客户自行分配。
这里我使用方式一的取图流程。
1.开始取流。
2.停止取流。
3.方式一的取图方式,采用超时机制获取一帧图片,SDK内部等待直到有数据时返回。
所获取的帧属于裸数据,数据保存在pData,并无图像格式(具体数据格式可以提前设定)。pFrameInfo表示输出帧的信息。
可以通过函数MV_CC_Display(IN void* handle, IN void* hWnd)
来实时显示采集到的图像。该函数需要在MV_CC_StartGrabbing
之后调用,显示采集到的图像。如果相机当前采集图像是JPEG压缩的格式,则不支持调用该函数接口进行显示。
可以通过函数MV_CC_SaveImage(IN&OUT MV_SAVE_IMAGE_PARAM* pSaveParam)
将原始图像数据转换成图片格式并保存在指定内存里,再通过函数fwrite写入文件中。
也可以通过函数MV_CC_SaveImageEx
将原始图像数据转换成图片格式并保存在指定内存中,可支持设置JPEG编码质量。
可通过函数memcpy(OUT void* dst, IN void const* src, IN size_t size)
把资源内存(src所指向的内存区域)拷贝到目标内存(dest所指向的内存区域),从而将unsigned char格式的图像数据转换为QImage格式的图像数据,这里要注意输入数据的格式,代码中输入的unsigned char pFrameBuf数据格式分别为Mono8的灰度图像和RGB8_Packed的彩色图像。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">#include "MvCameraControl.h"
-
- void main()
- {
- int nRet = -1;
- void* m_handle = NULL;
-
- //枚举子网内指定的传输协议对应的所有设备
- unsigned int nTLayerType = MV_GIGE_DEVICE | MV_USB_DEVICE;
- MV_CC_DEVICE_INFO_LIST m_stDevList = {0};
- int nRet = MV_CC_EnumDevices(nTLayerType, &m_stDevList);
- if (MV_OK != nRet)
- {
- printf("error: EnumDevices fail [%x]\n", nRet);
- return;
- }
-
- int i = 0;
- if (m_stDevList.nDeviceNum == 0)
- {
- printf("no camera found!\n");
- return;
- }
-
- //选择查找到的第一台在线设备,创建设备句柄
- int nDeviceIndex = 0;
-
- MV_CC_DEVICE_INFO m_stDevInfo = {0};
- memcpy(&m_stDevInfo, m_stDevList.pDeviceInfo[nDeviceIndex], sizeof(MV_CC_DEVICE_INFO));
-
- nRet = MV_CC_CreateHandle(&m_handle, &m_stDevInfo);
-
- if (MV_OK != nRet)
- {
- printf("error: CreateHandle fail [%x]\n", nRet);
- return;
- }
-
- //连接设备
- nRet = MV_CC_OpenDevice(m_handle, nAccessMode, nSwitchoverKey);
- if (MV_OK != nRet)
- {
- printf("error: OpenDevice fail [%x]\n", nRet);
- return;
- }
- //...其他处理
-
- //开始采集图像
- nRet = MV_CC_StartGrabbing(m_handle);
- if (MV_OK != nRet)
- {
- printf("error: StartGrabbing fail [%x]\n", nRet);
- return;
- }
-
- //获取一帧数据的大小
- MVCC_INTVALUE stIntvalue = {0};
- nRet = MV_CC_GetIntValue(m_handle, "PayloadSize", &stIntvalue);
- if (nRet != MV_OK)
- {
- printf("Get PayloadSize failed! nRet [%x]\n", nRet);
- return;
- }
- int nBufSize = stIntvalue.nCurValue; //一帧数据大小
-
- unsigned int nTestFrameSize = 0;
- unsigned char* pFrameBuf = NULL;
- pFrameBuf = (unsigned char*)malloc(nBufSize);
-
- MV_FRAME_OUT_INFO_EX stInfo;
- memset(&stInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
-
- //上层应用程序需要根据帧率,控制好调用该接口的频率
- //此次代码仅供参考,实际应用建议另建线程进行图像帧采集和处理
- while(1)
- {
- if (nTestFrameSize > 99)
- {
- break;
- }
- nRet = MV_CC_GetOneFrameTimeout(m_handle, pFrameBuf, nBufSize, &stInfo, 1000);
- if (MV_OK != nRet)
- {
- Sleep(10);
- }
- else
- {
- //...图像数据处理
- nTestFrameSize++;
- }
- }
-
- //...其他处理
-
- //停止采集图像
- nRet = MV_CC_StopGrabbing(m_handle);
- if (MV_OK != nRet)
- {
- printf("error: StopGrabbing fail [%x]\n", nRet);
- return;
- }
-
- //关闭设备,释放资源
- nRet = MV_CC_CloseDevice(m_handle);
- if (MV_OK != nRet)
- {
- printf("error: CloseDevice fail [%x]\n", nRet);
- return;
- }
-
- //销毁句柄,释放资源
- nRet = MV_CC_DestroyHandle(m_handle);
- if (MV_OK != nRet)
- {
- printf("error: DestroyHandle fail [%x]\n", nRet);
- return;
- }
- }
- </code></span></span>
SDK还提供了回调出流的方法,这里我没研究,如果大佬采用的这种方式,也可以一起交流,方便我学习。
2.3 设置相机的一些参数
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">/********************************************************************//**
- * @~chinese
- * @brief 设置Enum型属性值
- * @param handle [IN] 设备句柄
- * @param strKey [IN] 属性键值,如获取像素格式信息则为"PixelFormat"
- * @param nValue [IN] 想要设置的设备的属性值
- * @return 成功,返回MV_OK,失败,返回错误码
- * @remarks 连接设备之后调用该接口可以设置Enum类型的指定节点的值。strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IEnumeration”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。
- ************************************************************************/
- MV_CAMCTRL_API int __stdcall MV_CC_SetEnumValue(IN void* handle,IN const char* strKey,IN unsigned int nValue);
-
- /********************************************************************//**
- * @~chinese
- * @brief 设置float型属性值
- * @param handle [IN] 设备句柄
- * @param strKey [IN] 属性键值
- * @param fValue [IN] 想要设置的设备的属性值
- * @return 成功,返回MV_OK,失败,返回错误码
- * @remarks 连接设备之后调用该接口可以设置float类型的指定节点的值。strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IFloat”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 ************************************************************************/
- MV_CAMCTRL_API int __stdcall MV_CC_SetFloatValue(IN void* handle,IN const char* strKey,IN float fValue);
- </code></span></span>
关于输入参数参考相机中的节点。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">int setCameraParametMode(const char* str_key, unsigned int val)
- {
- // TriggerMode 0: Off 1: On
- // TriggerSource 0:Line0 1:Line1 7:Software
- // GainAuto 0: Off 1: Once 2: Continuous
- int temp_val = MV_CC_SetEnumValue(m_handle, str_key, val);
- if (temp_val != 0) {
- return -1;
- }
- else {
- return 0;
- }
- }
-
- int setCameraParametValue(const char* str_type, float num_val)
- {
- // ExposureTime
- // Gain
- int temp_value = MV_CC_SetFloatValue(m_handle, str_type, num_val);
- if (temp_value != 0) {
- return -1;
- }
- else {
- return 0;
- }
- }
- </code></span></span>
使用:
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">camera_obj->setCameraParametMode("ExposureAuto", 0);
- camera_obj->setCameraParametValue("ExposureTime", exposure_time);
- camera_obj->setCameraParametMode("GainAuto", 0);
- camera_obj->setCameraParametValue("Gain", gain);
- </code></span></span>
3.将相机抓取到的图像转为Mat格式,方便后续使用
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">/************************************************************************
- * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetIntValue(IN void* handle,
- IN const char* strKey,
- OUT MVCC_INTVALUE *pIntValue);
- * @brief 获取Integer属性值(建议改用MV_CC_GetIntValueEx接口)
- * @param void* handle [IN] 相机句柄
- * @param char* strKey [IN] 属性键值,如获取宽度信息则为"Width"
- * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机属性结构体指针
- * @return 成功,返回MV_OK,失败,返回错误码
- ************************************************************************/
- MV_CAMCTRL_API int __stdcall MV_CC_GetIntValue(IN void* handle,IN const char* strKey,OUT MVCC_INTVALUE *pIntValue);
- </code></span></span>
- 相机采集到的图像格式是buffer,需要将其转化为Mat类型。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">int HikCamera::CameraOneFrameImageToMat(void* dev_handle,const std::string& dev_name, cv::Mat& image)
- {
- cv::Mat* getImage = new cv::Mat();
- unsigned int nRecvBufSize = 0;
- MVCC_INTVALUE stParam;
- memset(&stParam, 0, sizeof(MVCC_INTVALUE));
- int tempValue = MV_CC_GetIntValue(dev_handle, "PayloadSize", &stParam);
- if (tempValue != 0) {
- return -1;
- }
- nRecvBufSize = stParam.nCurValue;
- unsigned char* pDate;
- pDate = (unsigned char*)malloc(nRecvBufSize);
-
- MV_FRAME_OUT_INFO_EX stImageInfo = { 0 };
- tempValue = MV_CC_GetOneFrameTimeout(dev_handle, pDate, nRecvBufSize,
- &stImageInfo, 500);
- if (tempValue != 0) {
- return -1;
- }
- m_nBufSizeForSaveImage_ =
- stImageInfo.nWidth * stImageInfo.nHeight * 3 + 2048;
- unsigned char* m_pBufForSaveImage;
- m_pBufForSaveImage = (unsigned char*)malloc(m_nBufSizeForSaveImage_);
-
- bool isMono; //判断是否为黑白图像
- switch (stImageInfo.enPixelType) {
- case PixelType_Gvsp_Mono8:
- case PixelType_Gvsp_Mono10:
- case PixelType_Gvsp_Mono10_Packed:
- case PixelType_Gvsp_Mono12:
- case PixelType_Gvsp_Mono12_Packed:
- isMono = true;
- break;
- default:
- isMono = false;
- break;
- }
- if (isMono) {
- *getImage =
- cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC1, pDate);
- }
- else {
- // 转换图像格式为BGR8
- MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
- memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));
- stConvertParam.nWidth = stImageInfo.nWidth;
- stConvertParam.nHeight = stImageInfo.nHeight;
- stConvertParam.pSrcData = pDate;
- stConvertParam.nSrcDataLen = stImageInfo.nFrameLen; // 输入数据大小
- stConvertParam.enSrcPixelType = stImageInfo.enPixelType; // 输入像素格式
- stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed; // 输出像素格式
-
- stConvertParam.pDstBuffer = m_pBufForSaveImage; // 输出数据缓存
- stConvertParam.nDstBufferSize = m_nBufSizeForSaveImage_; // 输出缓存大小
- MV_CC_ConvertPixelType(dev_handle, &stConvertParam);
-
- *getImage = cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC3, m_pBufForSaveImage);
- }
- (*getImage).copyTo(image);
- (*getImage).release();
- free(pDate);
- free(m_pBufForSaveImage);
- return 0;
- }
- </code></span></span>
4.将Mat格式图像转为QImage格式,用于UI界面上控件的显示
转化为Mat后,通过display_myImage_L再将Mat转化为QImage类型,进行在控件上显示。软触发的话,就是采集到当前帧图像,通过display_myImage_L显示,连续采集的话,通过多线程,将线程对象myThread_camera_L_show发送信号display给主线程,主线程调用display_myImage_L将相机采集到的图像进行显示。
- <span style="color:#333333"><span style="background-color:#f9f5e9"><code class="language-cpp">void display_myImage_L(const cv::Mat* image_ptr)
- {
- cv::Mat rgb;
- // cv::cvtColor(*imagePrt, rgb, CV_BGR2RGB);
-
- //判断是黑白、彩色图像
- QImage QmyImage_L;
- if (lbl_camera_L_image->channels() > 1) {
- cv::cvtColor(*image_ptr, rgb, CV_BGR2RGB);
- QmyImage_L = QImage((const unsigned char*)(rgb.data), rgb.cols,
- rgb.rows, QImage::Format_RGB888);
- }
- else {
- cv::cvtColor(*image_ptr, rgb, CV_GRAY2RGB);
- QmyImage_L = QImage((const unsigned char*)(rgb.data), rgb.cols,
- rgb.rows, QImage::Format_RGB888);
- }
-
- QmyImage_L = (QmyImage_L).scaled(ui.lbl_camera_L->size(), Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
-
- ui.lbl_camera_L->setPixmap(QPixmap::fromImage(QmyImage_L));
- }
- </code></span></span>
参考博文:
hikvision SDK使用(转)-腾讯云开发者社区-腾讯云
rm记录第三阶段(二):海康威视工业相机的使用,cmake的复习_海康工业相机cmakelists-CSDN博客
关于相机SDK开发
海康相机SDK联合c++标定-腾讯云开发者社区-腾讯云