海康威视工业相机SDK的开发使用笔记

环境说明: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.准备

  首先是安装海康威视的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.0libMVGigEVisionSDK.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 中找到设备的信息。
在这里插入图片描述

#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);
    }
}

  关于搜索到的所有相机设备信息在 MV_CC_DEVICE_INFO_LIST结构体中,

/// \~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;

  通过MV_CC_DEVICE_INFO结构体可以访问每个设备的详细信息,

/// \~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;

  这里以我使用的网口相机GigE设备为例,要获取到设备的详细内容:

// 我通过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);
    }

  有些参数我也没搞懂是什么信息。。。
在这里插入图片描述
  2.在打开指定设备之前,调用 MV_CC_IsDeviceAccessible() 检查指定设备是否可访问。 (这一步在实际使用中并未用到)
在这里插入图片描述
  3.调用 MV_CC_CreateHandle() 以创建设备句柄。
在这里插入图片描述

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

  4.调用 MV_CC_OpenDevice() 打开设备。
在这里插入图片描述
  去该API定义后发现,后面两个参数有默认值,所以实际使用中只需要输入第一个参数设备的句柄即可。

/********************************************************************//**
 *  @~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
int nRet = MV_CC_OpenDevice(m_handle);

  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的彩色图像。

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

SDK还提供了回调出流的方法,这里我没研究,如果大佬采用的这种方式,也可以一起交流,方便我学习。

2.3 设置相机的一些参数

/********************************************************************//**
 *  @~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);

  关于输入参数参考相机中的节点。

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;
    }
}

  使用:

camera_obj->setCameraParametMode("ExposureAuto", 0);
camera_obj->setCameraParametValue("ExposureTime", exposure_time);
camera_obj->setCameraParametMode("GainAuto", 0);
camera_obj->setCameraParametValue("Gain", gain);

3.将相机抓取到的图像转为Mat格式,方便后续使用

/************************************************************************
 *  @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);
  • 相机采集到的图像格式是buffer,需要将其转化为Mat类型。
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;
}

4.将Mat格式图像转为QImage格式,用于UI界面上控件的显示

  转化为Mat后,通过display_myImage_L再将Mat转化为QImage类型,进行在控件上显示。软触发的话,就是采集到当前帧图像,通过display_myImage_L显示,连续采集的话,通过多线程,将线程对象myThread_camera_L_show发送信号display给主线程,主线程调用display_myImage_L将相机采集到的图像进行显示。

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));
}

参考博文:
https://cloud.tencent.com/developer/article/1730933
https://blog.csdn.net/weixin_46421489/article/details/116381368
关于相机SDK开发
https://cloud.tencent.com/developer/article/1730940

MV_CC_RegisterImageCallBackEx函数是用于注册图像数据回调函数的函数,它可以实现实时取流。该函数需要配合MV_CC_StartGrabbing和MV_CC_StopGrabbing函数一起使用,具体实现过程如下: 1. 调用MV_CC_OpenDevice打开设备; 2. 调用MV_CC_SetEnumValue设置参数; 3. 调用MV_CC_RegisterImageCallBackEx注册回调函数; 4. 调用MV_CC_StartGrabbing开始取流; 5. 在回调函数中处理图像数据; 6. 调用MV_CC_StopGrabbing停止取流; 7. 调用MV_CC_CloseDevice关闭设备。 以下是一个简单的示例代码,可供参考: ```c #include "MvCameraControl.h" void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser) { // 处理图像数据 } int main() { MV_CC_DEVICE_INFO_LIST stDeviceList; memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList); if (MV_OK != nRet) { printf("MV_CC_EnumDevices failed! nRet [%x]\n", nRet); return -1; } MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[0]; // 打开设备 void* handle = NULL; nRet = MV_CC_OpenDevice(pDeviceInfo, &handle); if (MV_OK != nRet) { printf("MV_CC_OpenDevice failed! nRet [%x]\n", nRet); return -1; } // 设置参数 nRet = MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF); if (MV_OK != nRet) { printf("MV_CC_SetEnumValue TriggerMode failed! nRet [%x]\n", nRet); MV_CC_CloseDevice(handle); return -1; } // 注册回调函数 nRet = MV_CC_RegisterImageCallBackEx(handle, ImageCallBackEx, NULL); if (MV_OK != nRet) { printf("MV_CC_RegisterImageCallBackEx failed! nRet [%x]\n", nRet); MV_CC_CloseDevice(handle); return -1; } // 开始取流 nRet = MV_CC_StartGrabbing(handle); if (MV_OK != nRet) { printf("MV_CC_StartGrabbing failed! nRet [%x]\n", nRet); MV_CC_CloseDevice(handle); return -1; } // 等待图像数据回调 getchar(); // 停止取流 nRet = MV_CC_StopGrabbing(handle); if (MV_OK != nRet) { printf("MV_CC_StopGrabbing failed! nRet [%x]\n", nRet); MV_CC_CloseDevice(handle); return -1; } // 关闭设备 nRet = MV_CC_CloseDevice(handle); if (MV_OK != nRet) { printf("MV_CC_CloseDevice failed! nRet [%x]\n", nRet); return -1; } return 0; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

boss-dog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值