网站怎么发布到iis上,珠海建网站价格,ih5做自适应网站,wordpress主题vantage作者#xff1a;SkyXZ CSDN#xff1a;SkyXZ#xff5e;-CSDN博客 博客园#xff1a;SkyXZ - 博客园 笔者使用的设备及环境#xff1a;WSL2-Ubuntu22.04MV-CS016-10UC 不会吧#xff1f;不会吧#xff1f;不会还有人拿到海康工业相机还是一脸懵叭#xff1f;不会还有人… 作者SkyXZ CSDNSkyXZ-CSDN博客 博客园SkyXZ - 博客园 笔者使用的设备及环境WSL2-Ubuntu22.04MV-CS016-10UC 不会吧不会吧不会还有人拿到海康工业相机还是一脸懵叭不会还有人觉得海康相机的API使用很难叭不用慌这篇文章从官方文档涵盖了海康相机官方MVS软件的使用以及Linux海康SDK-API包的理解上手**一篇文章带你走出海康相机使用新手村**让你一文读完之后便可自信的对朋友说“海康相机简简单单”
参考资料
海康工业相机客户端MVS下载地址海康机器人-机器视觉-下载中心海康工业相机镜头选型平台地址海康机器人-机器视觉-工具海康机器人USB3.0工业面阵相机用户手册海康机器人USB3.0工业面阵相机用户手册V3.0.1_9724.pdf海康机器人千兆网口工业面阵相机用户手册海康机器人千兆网口工业面阵相机用户手册V4.1.2_7307.pdf
一、海康官方MVS客户端使用教程 我们首先根据自己的系统在官网下载我们的MVS客户端我的系统为Linux因此我选择下载第二个Linux版本的客户端 下载完成后我们会得到一个ZIP压缩包我们解压后可以看到有很多不同CPU架构的版本我们选择适合自己系统的版本使用如下命令即可完成安装安装完成后的客户端将在/opt/MVS目录下 sudo dpkg -i MVS-***********.deb #自行替换适合自己的系统版本安装包我们使用如下命令即可进入MVS安装环境并且启动我们的客户端
cd /opt/MVS/bin #进入软件目录
./MVS.sh #运行客户端运行客户端后我们可以看到我们的客户端主界面如下左边主要为我们的设备区在这个区域我们可以看到当前连接到我们电脑的所有海康相机设备最上方为菜单栏提供文件、视图、设置、工具和帮助的功能下方为控制工具条可以为相机参数保存等操作提供快捷入口 接着我们选中我们的相机可以看到我们进入了如下界面在这个界面中左下方为接口和设备信息获取窗口可以显示设备详细信息中间为图像预览窗口上方左一按钮点击后即可开启相机实时取流查看图像右边为属性设置窗口可以对相机的基本参数进行设置如触发模式、增益、曝光时间、帧率、像素格式等等 由于属性设置客户端中有中文介绍那么我们接着主要开始讲解我们的SDK-API的使用方法叭
二、海康相机开发实战上手 在我们上一节安装的MVS目录下存在着海康相机驱动所需要的全部头文件以及链接库我们首先在我们的工作空间下面新建一个文件夹接着将MVS安装目录下的include和lib文件全部复制进来
mkdir -P HK_Camera/src #创建工作目录及源码文件夹
cd HK_Camera/ #进入工作目录
cp -r /opt/MVS/include/ HK_Camera/ #复制头文件
cp -r /opt/MVS/lib/ HK_Camera/ #复制链接库
touch CmakeLists.txt #创建Cmake文件我们首先来看一下海康相机组件包的项目结构叭要驱动海康工业相机需要有两个主要的驱动包include和lib其具体的结构以及对应的作用请看下图 HK_Camera/ ├── CmakeLists.txt ├── include/ │ ├── CameraParams.h │ ├── MvCameraControl.h │ ├── MvErrorDefine.h │ ├── MvISPErrorDefine.h │ ├── MvObsoleteInterfaces.h │ ├── ObsoleteCamParams.h │ └── PixelType.h ├── lib/ │ ├── 32/ │ ├── 64/ │ └── CLProtocol/ └── src/ CameraParams.h 定义了相机相关的参数结构体和枚举类型包含了相机的基本信息结构(如GigE相机信息、USB相机信息等)定义了图像采集相关的参数(如图像大小、像素格式等)定义了相机工作模式(如单帧模式、连续采集模式等)定义了网络传输相关的参数 MvCameraControl.h 定义了相机控制的主要API接口包含相机的初始化、连接、断开等基本操作函数包含图像采集相关的函数(开始采集、停止采集、获取图像等)包含参数设置和获取的函数包含文件操作相关的函数是SDK的主要接口文件 MvErrorDefine.h 定义了SDK所有的错误码包含通用错误码(0x80000000-0x800000FF)、GenICam错误码(0x80000100-0x800001FF)、GigE相关错误码(0x80000200-0x800002FF)、USB相关错误码(0x80000300-0x800003FF)、固件升级相关错误码(0x80000400-0x800004FF)MvISPErrorDefine.h 定义了图像处理(ISP)相关的错误码包含通用错误码、内存相关错误码、图像格式相关错误码、降噪处理相关错误码、去污点相关错误码PixelType.h 定义了相机支持的所有像素格式类型(enum MvGvspPixelType)主要包含以下几类像素格式Mono(单色): Mono8/10/12/16等、Bayer: BayerGR8/10/12, BayerRG8/10/12等、RGB: RGB8_Packed, BGR8_Packed等、YUV: YUV411/422/444等、3D点云相关格式ObsoleteCamParams.h 定义了一些已过时但仍支持的相机参数结构体MvObsoleteInterfaces.h 定义了一些已过时但仍支持的接口函数 由于海康的这些头文件里的API有着非常非常详细的双语介绍如下图因此我们将以海康工业相机MV-CS016-10UC来主要介绍使用海康相机的API使用及相机的使用流程类似于OpenCV调用免驱摄像头一般海康摄像头的使用也主要分为四步分别是初始化相机——设置相机参数——开始取图——停止采集和清理资源接下来我们将一边介绍主要使用的API一边手把手带着大家使能我们的海康相机首先我们创建我们的main.cc
touch src/main.cc #创建文件接着我们开始编写我们的Cmake文件由于Cmake比较简单我们便不详细的展开说明了
#step 1 设置我们的项目以及版本最小需求
cmake_minimum_required(VERSION 3.10)
project(HK_Camera)
#step 2 设置C标准
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#step 3 设置编译类型
if(NOT CMAKE_BUILD_TYPE)set(CMAKE_BUILD_TYPE Release)
endif()
#step 4 添加海康相机SDK路径
set(MVCAM_SDK_PATH ${PROJECT_SOURCE_DIR}/lib/64) # SDK库文件路径
set(MVCAM_INCLUDE_PATH ${PROJECT_SOURCE_DIR}/include) # SDK头文件路径
#step 5 添加头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include${MVCAM_INCLUDE_PATH}
)
#step 6 添加库文件路径
link_directories(${MVCAM_SDK_PATH}
)
#step 7 添加源文件
add_executable(${PROJECT_NAME} src/main.cc
)
#step 8 链接海康相机库
target_link_libraries(${PROJECT_NAME}MvCameraControl # 海康相机主库pthread # 线程库
)
#step 9 设置编译选项
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -O2)
#step 10 安装目标
install(TARGETS ${PROJECT_NAME}RUNTIME DESTINATION bin
) 1海康相机使用基本部署流程 Cmake编写完成后我们开始编写我们的代码首先导入一些我们需要的头文件
#include iostream
#include string
#include cstring // for memset
#include MvCameraControl.h
#include CameraParams.h // for MVCC_INTVALUE
#include PixelType.h // for PixelType_Gvsp_BGR8_Packed接着为了便于我们的使用我们首先创建一个HKCamera类其中包含有五个主要的函数分别是InitCamera()、SetParameters()、StartGrabbing()、GetOneFrame()、StopGrabbing()分别用来初始化我们的相机、设置相机参数、开始取流、获取一帧图像以及停止采集释放资源
class HKCamera{public:bool InitCamera(); // 初始化相机bool SetParameters(); // 设置相机参数bool StartGrabbing(); // 开始取图bool GetOneFrame(); // 获取一帧图像void StopGrabbing() // 停止采集
}我们开始首先完成初始化相机的函数要使用我们的海康相机那么首先肯定就需要先知道当前设备链接了哪些相机因此我们便需要先使用MV_CC_EnumDevices函数来枚举当前链接上主机的网口orUSB海康相机海康官方的API以及MV_CC_DEVICE_INFO_LIST结构体解析如下
typedef struct _MV_CC_DEVICE_INFO_LIST_
{unsigned int nDeviceNum; /// [OUT] \~chinese 在线设备数量 MV_CC_DEVICE_INFO* pDeviceInfo[MV_MAX_DEVICE_NUM];/// [OUT] \~chinese 支持最多256个设备
}MV_CC_DEVICE_INFO_LIST;
/********************************************************************//*** ~chinese* brief 枚举设备* param nTLayerType [IN] 枚举传输层, 参数定义参见CameraParams.h定义, 如: #define MV_GIGE_DEVICE 0x00000001 GigE设备* param pstDevList [IN][OUT] 设备列表* return 成功返回MV_OK错误返回错误码 * remarks 设备列表的内存是在SDK内部分配的多线程调用该接口时会进行设备列表内存的释放和申请,建议尽量避免多线程枚举操作。* remarks 参数枚举传输层适配传入MV_GIGE_DEVICE、MV_1394_DEVICE、MV_USB_DEVICE、MV_CAMERALINK_DEVICEMV_GIGE_DEVICE该参数传出所有GiGE相关的设备信息包含虚拟GiGE和GenTL下的GiGE设备MV_USB_DEVICE该参数传出所有USB设备包含虚拟USB设备。************************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_EnumDevices(IN unsigned int nTLayerType, IN OUT MV_CC_DEVICE_INFO_LIST* pstDevList);因此根据API的描述我们首先需要创建一个MV_CC_DEVICE_INFO_LIST类型的结构体同时我们还需要定义一个nRet变量来获取调用API后返回的值来判断我们是否成功使用了API因此我们的代码应该为下述形式
MV_CC_DEVICE_INFO_LIST stDeviceList;//定义MV_CC_DEVICE_INFO_LIST类型的结构体stDeviceList
memset(stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));//将结构体stDeviceList内存清零防止垃圾值影响
int nRet MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, stDeviceList);// 枚举设备
if (MV_OK ! nRet) { //判断返回值为MV_OK即正常运行std::cout 枚举设备失败! nRet [ nRet ] std::endl;return false;
}在枚举了相机之后如果相机数量不为零我们便可以选择开启我们的海康相机啦我们首先查看一下打开相机的API介绍
/********************************************************************//*** ~chinese* brief 打开设备* param handle [IN] 设备句柄* param nAccessMode [IN] 访问权限, 参数定义参见CameraParams.h定义, 如:#define MV_ACCESS_Exclusive 1 仅对 MV_GIGE_DEVICE/MV_GENTL_GIGE_DEVICE 类型的设备有效* param nSwitchoverKey[IN] 切换访问权限时的密钥仅对 MV_GIGE_DEVICE 类型的设备有效* return 成功返回MV_OK错误返回错误码* remarks 根据设置的设备参数找到对应的设备连接设备, 调用接口时可不传入nAccessMode和nSwitchoverKey此时默认设备访问模式为独占权限。MV_GIGE_DEVICE 类型设备目前相机固件暂不支持MV_ACCESS_ExclusiveWithSwitch、MV_ACCESS_ControlWithSwitch、MV_ACCESS_ControlSwitchEnable、MV_ACCESS_ControlSwitchEnableWithKey这四种抢占模式, SDK接口支持设置MV_GENTL_GIGE_DEVICE 设备只支持 nAccessMode 是 MV_ACCESS_Exclusive 、MV_ACCESS_Control 、MV_ACCESS_Monitor权限对于U3V设备CXP,Cameralink(MV_CAMERALINK_DEVICE、MV_GENTL_CAMERALINK_DEVICE), Xof设备, 虚拟GEV, 虚拟U3V设备nAccessMode、nSwitchoverKey这两个参数无效 默认以控制权限打开设备;该接口支持网口设备不枚举直接打开不支持U口和GenTL设备不枚举打开设备************************************************************************/
#ifndef __cplusplus //用于区分C和C编译环境
// C语言版本的函数声明
MV_CAMCTRL_API int __stdcall MV_CC_OpenDevice(IN void* handle, IN unsigned int nAccessMode, IN unsigned short nSwitchoverKey);
#else
// C语言版本的函数声明带默认参数
MV_CAMCTRL_API int __stdcall MV_CC_OpenDevice(IN void* handle, IN unsigned int nAccessMode MV_ACCESS_Exclusive, IN unsigned short nSwitchoverKey 0);
#endif根据这个API的说明我们可以知道我们在使用这个函数前还需要利用MV_CC_CreateHandle这个API创建一个句柄同时一个设备同时只能被一个进程以独占方式打开并且关闭设备时需要调用MV_CC_CloseDevice函数同时我们了解到这个API还有一个可选参数nAccessMode可以用来设置访问权限分别有MV_ACCESS_Exclusive独占权限、MV_ACCESS_Control控制权限、MV_ACCESS_Monitor监控权限、为了打开我们的摄像头我们还需再次查看MV_CC_CreateHandleAPI介绍
/********************************************************************//*** ~chinese* brief 创建设备句柄* param handle [IN][OUT] 设备句柄* param pstDevInfo [IN] 设备信息结构体* return 成功返回MV_OK错误返回错误码 * remarks 根据输入的设备信息创建库内部必须的资源和初始化内部模块通过该接口创建句柄调用SDK接口会默认生成SDK日志文件如果不需要生成日志文件可以将日志配置文件中的日志等级改成off************************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_CreateHandle(IN OUT void ** handle, IN const MV_CC_DEVICE_INFO* pstDevInfo);根据这个API的说明我们需要使用之前创建的MV_CC_DEVICE_INFO_LIST类型结构体stDeviceList中的成员变量pDeviceInfo于是我们的代码便应该如下
if (stDeviceList.nDeviceNum 0) {nRet MV_CC_CreateHandle(handle, stDeviceList.pDeviceInfo[0]); // 创建句柄if (MV_OK ! nRet) {std::cout 创建句柄失败! nRet [ nRet ] std::endl;return false;}nRet MV_CC_OpenDevice(handle); // 打开设备if (MV_OK ! nRet) {std::cout 打开设备失败! nRet [ nRet ] std::endl;return false;}
} else {std::cout 未找到设备! std::endl;return false;
}至此我们的InitCamera()函数便搭建完成啦完整代码如下
bool InitCamera() {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) {std::cout 枚举设备失败! nRet [ nRet ] std::endl;return false;}if (stDeviceList.nDeviceNum 0) {// 创建句柄nRet MV_CC_CreateHandle(handle, stDeviceList.pDeviceInfo[0]);if (MV_OK ! nRet) {std::cout 创建句柄失败! nRet [ nRet ] std::endl;return false;}// 打开设备nRet MV_CC_OpenDevice(handle);if (MV_OK ! nRet) {std::cout 打开设备失败! nRet [ nRet ] std::endl;return false;}} else {std::cout 未找到设备! std::endl;return false;}return true;
}完成了初始化相机的函数接下来我们便来完成我们SetParameters()设置相机基本参数的函数首先我们根据海康的用户手册可以知道我们有几个基本的参数可以进行设置分别是相机的触发模式、像素格式、曝光时间、增益Buff、相机帧率等我们在这里只对这四个常见参数进行设定通过查阅API手册我们可以知道这四个参数设置分别对应以下两个APIMV_CC_SetEnumValue、MV_CC_SetFloatValue同时我们可以通过MV_CC_GetFloatValueAPI来获取每一个属性键值的可调节参数范围这些函数的API以及参数列表中涉及到的结构体介绍如下
/********************************************************************//*** ~chinese* brief 设置Enum型属性值* param handle [IN] 设备句柄/采集卡句柄* param strKey [IN] 属性键值如获取像素格式信息则为PixelFormat* param nValue [IN] 想要设置的设备的属性值* return 成功,返回MV_OK,失败,返回错误码* remarks 连接设备之后调用该接口可以设置Enum类型的指定节点的值。************************************************************************/
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类型的指定节点的值。************************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_SetFloatValue(IN void* handle,IN const char* strKey,IN float fValue);/********************************************************************//*** ~chinese* brief 获取Float属性值* param handle [IN] 设备句柄/采集卡句柄* param strKey [IN] 属性键值* param pstFloatValue [IN][OUT] 返回给调用者有关设备属性结构体指针* return 成功,返回MV_OK,失败,返回错误码* remarks 连接设备之后调用该接口可以获取float类型的指定节点的值。************************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_GetFloatValue(IN void* handle,IN const char* strKey,IN OUT MVCC_FLOATVALUE *pstFloatValue);/// \~chinese Float类型值 \~english Float Value
typedef struct _MVCC_FLOATVALUE_T
{float fCurValue; /// [OUT] \~chinese 当前值 float fMax; /// [OUT] \~chinese 最大值 float fMin; /// [OUT] \~chinese 最小值 unsigned int nReserved[4]; /// \~chinese 预留
}MVCC_FLOATVALUE;我们通过查阅用户手册可以知道海康工业相机的属性键值遵循GenICam标准因此我们能调整和获取的常用属性键值有如下几个
AcquisitionFrameRate // 采集帧率
AcquisitionFrameRateEnable // 帧率控制使能
ExposureTime // 曝光时间
ExposureAuto // 自动曝光
Gain // 增益
GainAuto // 自动增益
TriggerMode // 触发模式
TriggerSource // 触发源
Width // 图像宽度
Height // 图像高度因此根据上述的分析我们首先设置我们相机的触发模式为OFF内触发模式同时设置我们的像素格式为BGR8
int nRet;
nRet MV_CC_SetEnumValue(handle, TriggerMode, 0);// 设置触发模式为off
if (MV_OK ! nRet) {std::cout 设置触发模式失败! nRet [ nRet ] std::endl;return false;
}
// 设置像素格式为BGR8
nRet MV_CC_SetEnumValue(handle, PixelFormat, PixelType_Gvsp_BGR8_Packed);
if (MV_OK ! nRet) {std::cout 设置像素格式失败! nRet [ nRet ] std::endl;return false;
}
std::cout 设置像素格式为BGR8_Packed std::endl;接着为了避免我们的参数设置有问题以及为了便于自检我们首先利用MV_CC_GetFloatValue来获取当前我们使用的相机的每一个属性参数的可调节范围再根据获取到的可调节范围来检查我们设置参数的可用性并进一步修改我们的参数因此我们以曝光时间的设置为例子代码应该为如下形式我们首先创建一个MVCC_FLOATVALUE形式的结构体stExposureTime用来获取曝光时间的可调节信息接着判定我们设置的exposureTime是否超过了stExposureTime.fMin和stExposureTime.fMax的范围如果没有那么我们便将exposureTime使用MV_CC_SetFloatValueAPI进行设置
// 获取和设置曝光时间
MVCC_FLOATVALUE stExposureTime {0};
nRet MV_CC_GetFloatValue(handle, ExposureTime, stExposureTime);
if (MV_OK nRet) {float exposureTime 10000.0f; // 默认10msif (exposureTime stExposureTime.fMin) exposureTime stExposureTime.fMin;if (exposureTime stExposureTime.fMax) exposureTime stExposureTime.fMax;std::cout 曝光时间范围: [ stExposureTime.fMin , stExposureTime.fMax ], 当前设置: exposureTime std::endl;nRet MV_CC_SetFloatValue(handle, ExposureTime, exposureTime);if (MV_OK ! nRet) {std::cout 设置曝光时间失败! nRet [ nRet ] std::endl;return false;}
}至此我们的InitCamera()函数便搭建完成啦完整代码如下
bool SetParameters() {// 设置相机参数int nRet;nRet MV_CC_SetEnumValue(handle, TriggerMode, 0);// 设置触发模式为offif (MV_OK ! nRet) {std::cout 设置触发模式失败! nRet [ nRet ] std::endl;return false;}// 设置像素格式为BGR8nRet MV_CC_SetEnumValue(handle, PixelFormat, PixelType_Gvsp_BGR8_Packed);if (MV_OK ! nRet) {std::cout 设置像素格式失败! nRet [ nRet ] std::endl;return false;}std::cout 设置像素格式为BGR8_Packed std::endl;// 获取和设置曝光时间MVCC_FLOATVALUE stExposureTime {0};nRet MV_CC_GetFloatValue(handle, ExposureTime, stExposureTime);if (MV_OK nRet) {float exposureTime 10000.0f; // 默认10msif (exposureTime stExposureTime.fMin) exposureTime stExposureTime.fMin;if (exposureTime stExposureTime.fMax) exposureTime stExposureTime.fMax;std::cout 曝光时间范围: [ stExposureTime.fMin , stExposureTime.fMax ], 当前设置: exposureTime std::endl;nRet MV_CC_SetFloatValue(handle, ExposureTime, exposureTime);if (MV_OK ! nRet) {std::cout 设置曝光时间失败! nRet [ nRet ] std::endl;return false;}}// 获取和设置增益MVCC_FLOATVALUE stGain {0};nRet MV_CC_GetFloatValue(handle, Gain, stGain);if (MV_OK nRet) {std::cout 增益范围: [ stGain.fMin , stGain.fMax ], 当前值: stGain.fCurValue std::endl;if (stGain.fCurValue stGain.fMin || stGain.fCurValue stGain.fMax) {float gain stGain.fMin; // 使用最小值nRet MV_CC_SetFloatValue(handle, Gain, gain);if (MV_OK ! nRet) {std::cout 设置增益失败! nRet [ nRet ] std::endl;return false;}}}// 获取和设置帧率MVCC_FLOATVALUE stFrameRate {0};nRet MV_CC_GetFloatValue(handle, AcquisitionFrameRate, stFrameRate);if (MV_OK nRet) {float frameRate 30.0f; // 默认30fpsif (frameRate stFrameRate.fMin) frameRate stFrameRate.fMin;if (frameRate stFrameRate.fMax) frameRate stFrameRate.fMax;std::cout 帧率范围: [ stFrameRate.fMin , stFrameRate.fMax ], 当前设置: frameRate std::endl;nRet MV_CC_SetFloatValue(handle, AcquisitionFrameRate, frameRate);if (MV_OK ! nRet) {std::cout 设置帧率失败! nRet [ nRet ] std::endl;// 这个错误不影响主要功能可以继续}}return true;
}完成了相机基本参数的设置我们便可以开始取流啦这部分有同学就会问了欸我们前面就已经打开摄像头了为什么这里还会有取流和取帧两个函数呢这就是海康摄像头和OpenCV使用免驱摄像头逻辑上的区别了我们可以把这个过程类比于我们电动抽水机前面打开摄像头的函数可以理解为给抽水机上电而我们这里的开始取流StartGrabbing()便是让图像暂存在相机内部的缓存中可以理解为让抽水机开始工作一直出水而我们的取帧函数GetOneFrame()便是从相机的缓存中读取一帧图像并将图像数据复制到我们准备好的内存中可以理解为拿一个水桶来接水这样我们便可以随接随用 理解了为什么我们便开始讲解取流函数StartGrabbing()在海康的API里面有一个专门的函数便是用来取流的他的API介绍如下
/********************************************************************//*** ~chinese* brief 开始取流* param handle [IN] 设备句柄* return 成功返回MV_OK错误返回错误码* remarks 该接口不支持MV_CAMERALINK_DEVICE 类型的设备。***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_StartGrabbing(IN void* handle);可以看到使用这个函数非常的简单和我们上面的操作非常类似只要将我们创建的句柄传入其中即可按照如下代码调用了之后我们的相机便会开始取流并将图像存储在相机内部的缓存里面临时存储
int nRet MV_CC_StartGrabbing(handle);// 开始取流
if (MV_OK ! nRet) {std::cout 开始取流失败! nRet [ nRet ] std::endl;return false;
}接着我们便可以获取当前属性参数下的图像数据包的大小由于这个属性参数不变那么每帧的图像数据大小的范围便也不会变因此我们只需要获取一次数据包的大小便可以所以我们将数据包大小获取放在开始取流的函数里面我们看到他的API介绍如下
/************************************************************************* 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);其中还涉及到了一个数据包结构体MVCC_INTVALUE求定义如下
typedef struct _MVCC_INTVALUE_T
{unsigned int nCurValue; /// [OUT] \~chinese 当前值 unsigned int nMax; /// [OUT] \~chinese 最大值 unsigned int nMin; /// [OUT] \~chinese 最小值 unsigned int nInc; /// [OUT] \~chinese unsigned int nReserved[4];/// \~chinese 预留
}MVCC_INTVALUE;了解了其定义后我们便可以使用这个API来获取数据包的大小并用malloc函数来分配我们主机的内存
// 获取数据包大小
MVCC_INTVALUE stParam;
nRet MV_CC_GetIntValue(handle, PayloadSize, stParam);
if (MV_OK ! nRet) {std::cout 获取数据包大小失败! nRet [ nRet ] std::endl;return false;
}
// 分配资源
nDataSize stParam.nCurValue;
pData (unsigned char*)malloc(nDataSize);
if (pData nullptr) {std::cout 内存分配失败! std::endl;return false;
}至此我们的StartGrabbing()函数便搭建完成啦完整代码如下
bool StartGrabbing() {// 开始取流int nRet MV_CC_StartGrabbing(handle);if (MV_OK ! nRet) {std::cout 开始取流失败! nRet [ nRet ] std::endl;return false;}// 获取数据包大小MVCC_INTVALUE stParam;nRet MV_CC_GetIntValue(handle, PayloadSize, stParam);if (MV_OK ! nRet) {std::cout 获取数据包大小失败! nRet [ nRet ] std::endl;return false;}// 分配资源nDataSize stParam.nCurValue;pData (unsigned char*)malloc(nDataSize);if (pData nullptr) {std::cout 内存分配失败! std::endl;return false;}return true;
}接着我们再来编写我们的取帧函数GetOneFrame()海康给了我们一个专门的获取一帧图像的API具体其介绍如下我们可以看到这个API采用的是超时机制因此SDK内部会一直等待直到有数据时才会返回一帧图像
/********************************************************************//*** ~chinese* brief 采用超时机制获取一帧图片SDK内部等待直到有数据时返回* param handle [IN] 设备句柄* param pData [IN][OUT] 图像数据接收指针* param nDataSize [IN] 接收缓存大小* param pstFrameInfo [IN][OUT] 图像信息结构体* param nMsec [IN] 等待超时时间* return 成功返回MV_OK错误返回错误码* remarks 调用该接口获取图像数据帧之前需要先调用MV_CC_StartGrabbing启动图像采集该接口为主动式获取帧数据上层应用程序需要根据帧率控制好调用该接口的频率该接口支持设置超时时间SDK内部等待直到有数据时返回可以增加取流平稳性适合用于对平稳性要求较高的场合该接口对于U3V、GIGE设备均可支持该接口不支持CameraLink设备。***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_GetOneFrameTimeout(IN void* handle, IN OUT unsigned char* pData , IN unsigned int nDataSize, IN OUT MV_FRAME_OUT_INFO_EX* pstFrameInfo, IN unsigned int nMsec);因此我们便可以用如下的方式来使用这个API同时我们在这里再加上一次错误判定以防止句柄或者分配的内存地址指针为空
if (handle nullptr || pData nullptr) {return false;
}
int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);
if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;
}
std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] FrameNum[ stImageInfo.nFrameNum ] std::endl;
return true;至此我们的StartGrabbing()函数便搭建完成啦完整代码如下
// 获取一帧图像
bool GetOneFrame() {if (handle nullptr || pData nullptr) {return false;}int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;}std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] FrameNum[ stImageInfo.nFrameNum ] std::endl;return true;
}最后我们只需要编写释放资源的函数StopGrabbing()便大功告成啦释放资源比较简单我们只需要按照我们开始的顺序倒叙关闭即可即停止取流、关闭相机、摧毁句柄最后释放我们主机的内存即可他们对应的API介绍如下
/********************************************************************//*** ~chinese* brief 停止取流* param handle [IN] 设备句柄* return 成功返回MV_OK错误返回错误码* remarks 该接口不支持MV_CAMERALINK_DEVICE 类型的设备。***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_StopGrabbing(IN void* handle);
/********************************************************************//*** ~chinese* brief 关闭设备* param handle [IN] 设备句柄* return 成功返回MV_OK错误返回错误码* remarks 通过MV_CC_OpenDevice连接设备后可以通过该接口断开设备连接释放资源***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_CloseDevice(IN void* handle);
/********************************************************************//*** ~chinese* brief 销毁设备句柄* param handle [IN] 设备句柄* return 成功返回MV_OK错误返回错误码 * remarks MV_CC_DestroyHandle 如果传入采集卡句柄其效果和 MV_CC_DestroyInterface 相同;************************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_DestroyHandle(IN void * handle);因此我们最后的函数的代码长这样
// 停止采集
void StopGrabbing() {if (handle ! nullptr) {MV_CC_StopGrabbing(handle);MV_CC_CloseDevice(handle);MV_CC_DestroyHandle(handle);handle nullptr;}if (pData ! nullptr) {free(pData);pData nullptr;}
}最后我们只需要在主函数依次进行调用即可这部分代码比较简单我相信来了解海康工业相机的同学这部分不需要再展开细讲啦
int main() {HKCamera camera;// 初始化相机if (!camera.InitCamera()) {std::cout 相机初始化失败! std::endl;return -1;}std::cout 相机初始化成功! std::endl;// 设置参数if (!camera.SetParameters()) {std::cout 设置相机参数失败! std::endl;return -1;}std::cout 设置相机参数成功! std::endl;// 开始取图if (!camera.StartGrabbing()) {std::cout 开始取图失败! std::endl;return -1;}std::cout 开始取图成功! std::endl;// 获取10帧图像for (int i 0; i 10; i) {if (!camera.GetOneFrame()) {break;}}// 停止采集camera.StopGrabbing();std::cout 停止采集完成! std::endl;return 0;
}最后我们完整的代码及运行效果如下(但是又有同学要问了我们该如何显示图像呢请继续翻到下面我将在下面进行介绍) #include iostream
#include string
#include cstring // for memset
#include MvCameraControl.h
#include CameraParams.h // for MVCC_INTVALUE
#include PixelType.h // for PixelType_Gvsp_BGR8_Packed
class HKCamera {
private:void* handle nullptr;MVCC_INTVALUE stParam;MV_FRAME_OUT_INFO_EX stImageInfo {0};unsigned char* pData nullptr;unsigned int nDataSize 0;
public:bool InitCamera() {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) {std::cout 枚举设备失败! nRet [ nRet ] std::endl;return false;}if (stDeviceList.nDeviceNum 0) {// 创建句柄nRet MV_CC_CreateHandle(handle, stDeviceList.pDeviceInfo[0]);if (MV_OK ! nRet) {std::cout 创建句柄失败! nRet [ nRet ] std::endl;return false;}// 打开设备nRet MV_CC_OpenDevice(handle);if (MV_OK ! nRet) {std::cout 打开设备失败! nRet [ nRet ] std::endl;return false;}} else {std::cout 未找到设备! std::endl;return false;}return true;}// 设置相机参数bool SetParameters() {int nRet;// 设置触发模式为offnRet MV_CC_SetEnumValue(handle, TriggerMode, 0);if (MV_OK ! nRet) {std::cout 设置触发模式失败! nRet [ nRet ] std::endl;return false;}// 设置像素格式为BGR8nRet MV_CC_SetEnumValue(handle, PixelFormat, PixelType_Gvsp_BGR8_Packed);if (MV_OK ! nRet) {std::cout 设置像素格式失败! nRet [ nRet ] std::endl;return false;}std::cout 设置像素格式为BGR8_Packed std::endl;// 获取和设置曝光时间MVCC_FLOATVALUE stExposureTime {0};nRet MV_CC_GetFloatValue(handle, ExposureTime, stExposureTime);if (MV_OK nRet) {float exposureTime 10000.0f; // 默认10msif (exposureTime stExposureTime.fMin) exposureTime stExposureTime.fMin;if (exposureTime stExposureTime.fMax) exposureTime stExposureTime.fMax;std::cout 曝光时间范围: [ stExposureTime.fMin , stExposureTime.fMax ], 当前设置: exposureTime std::endl;nRet MV_CC_SetFloatValue(handle, ExposureTime, exposureTime);if (MV_OK ! nRet) {std::cout 设置曝光时间失败! nRet [ nRet ] std::endl;return false;}}// 获取和设置增益MVCC_FLOATVALUE stGain {0};nRet MV_CC_GetFloatValue(handle, Gain, stGain);if (MV_OK nRet) {std::cout 增益范围: [ stGain.fMin , stGain.fMax ], 当前值: stGain.fCurValue std::endl;if (stGain.fCurValue stGain.fMin || stGain.fCurValue stGain.fMax) {float gain stGain.fMin; // 使用最小值nRet MV_CC_SetFloatValue(handle, Gain, gain);if (MV_OK ! nRet) {std::cout 设置增益失败! nRet [ nRet ] std::endl;return false;}}}// 获取和设置帧率MVCC_FLOATVALUE stFrameRate {0};nRet MV_CC_GetFloatValue(handle, AcquisitionFrameRate, stFrameRate);if (MV_OK nRet) {float frameRate 30.0f; // 默认30fpsif (frameRate stFrameRate.fMin) frameRate stFrameRate.fMin;if (frameRate stFrameRate.fMax) frameRate stFrameRate.fMax;std::cout 帧率范围: [ stFrameRate.fMin , stFrameRate.fMax ], 当前设置: frameRate std::endl;nRet MV_CC_SetFloatValue(handle, AcquisitionFrameRate, frameRate);if (MV_OK ! nRet) {std::cout 设置帧率失败! nRet [ nRet ] std::endl;// 这个错误不影响主要功能可以继续}}return true;}// 开始取图bool StartGrabbing() {// 开始取流int nRet MV_CC_StartGrabbing(handle);if (MV_OK ! nRet) {std::cout 开始取流失败! nRet [ nRet ] std::endl;return false;}// 获取数据包大小MVCC_INTVALUE stParam;nRet MV_CC_GetIntValue(handle, PayloadSize, stParam);if (MV_OK ! nRet) {std::cout 获取数据包大小失败! nRet [ nRet ] std::endl;return false;}// 分配资源nDataSize stParam.nCurValue;pData (unsigned char*)malloc(nDataSize);if (pData nullptr) {std::cout 内存分配失败! std::endl;return false;}return true;}// 获取一帧图像bool GetOneFrame() {if (handle nullptr || pData nullptr) {return false;}int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;}std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] FrameNum[ stImageInfo.nFrameNum ] std::endl;return true;}// 停止采集void StopGrabbing() {if (handle ! nullptr) {MV_CC_StopGrabbing(handle);MV_CC_CloseDevice(handle);MV_CC_DestroyHandle(handle);handle nullptr;}if (pData ! nullptr) {free(pData);pData nullptr;}}~HKCamera() {StopGrabbing();}
};
int main() {HKCamera camera;// 初始化相机if (!camera.InitCamera()) {std::cout 相机初始化失败! std::endl;return -1;}std::cout 相机初始化成功! std::endl;// 设置参数if (!camera.SetParameters()) {std::cout 设置相机参数失败! std::endl;return -1;}std::cout 设置相机参数成功! std::endl;// 开始取图if (!camera.StartGrabbing()) {std::cout 开始取图失败! std::endl;return -1;}std::cout 开始取图成功! std::endl;// 获取10帧图像for (int i 0; i 10; i) {if (!camera.GetOneFrame()) {break;}}// 停止采集camera.StopGrabbing();std::cout 停止采集完成! std::endl;return 0;
}2海康相机OpenCV实时取流部署教程 如果我们要显示采集的图像的话我们有两个方法首先便是我们的OpenCV再然后便是海康提供给我们的图像显示API我们首先介绍大家都会的OpenCV的方案我们先新引入OpenCV的头文件
#include opencv2/opencv.hpp接着我们在构造函数部分新增CV图像显示的Frame以及我们在构造函数中初始化我们的输出帧和数据包大小结构体
class HKCamera {private:cv::Mat frame; // OpenCV图像public:HKCamera() {// 在构造函数中初始化结构体memset(stParam, 0, sizeof(MVCC_INTVALUE));memset(stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));}然后我们把GetOneFrame()函数变为GetOneFrameAndShow()然后在函数里面新增OpenCV获取帧的代码将海康与OpenCV进行桥接由于我们前面设置的采集格式为BGR因此我们可以直接使用BGR数据创建Mat
bool GetOneFrameAndShow() {if (handle nullptr || pData nullptr) {return false;}int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;}std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] FrameNum[ stImageInfo.nFrameNum ] std::endl;
/*---------------------添加以下图像转换及显示代码-----------------------*/// 转换为OpenCV格式并显示if (stImageInfo.enPixelType PixelType_Gvsp_BGR8_Packed) {// 直接使用BGR数据创建Matframe cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC3, pData);// 简单的图像增强cv::Mat temp;// 轻微提升对比度frame.convertTo(temp, -1, 1.1, 0);frame temp;} else {std::cout 不支持的像素格式: 0x std::hex stImageInfo.enPixelType std::dec std::endl;return false;}if (!frame.empty()) {cv::imshow(Camera, frame);cv::waitKey(1);}
/*---------------------添加以上图像转换及显示代码-----------------------*/return true;
}然后我们在主函数里把原先的获取10帧图像修改为while循环持续获得图像这样便可以实时显示我们摄像头获取的图像
// 持续获取并显示图像直到按下ESC键
while (true) {if (!camera.GetOneFrameAndShow()) {break;}// 检查ESC键是否按下char key cv::waitKey(1);if (key 27) { // ESC键的ASCII码break;}最后我们在CmakeLists.txt中添加OpenCV的依赖即可
# 查找OpenCV包
find_package(OpenCV REQUIRED)
include_directories(${PROJECT_SOURCE_DIR}/include${MVCAM_INCLUDE_PATH}${OpenCV_INCLUDE_DIRS}
)
# 链接海康相机库和OpenCV库
target_link_libraries(${PROJECT_NAME}MvCameraControl # 海康相机主库pthread # 线程库${OpenCV_LIBS} # OpenCV库
)最后我们OpenCV显示图像的完整的代码及显示效果如下 #include iostream
#include string
#include cstring // for memset
#include opencv2/opencv.hpp
#include MvCameraControl.h
#include CameraParams.h // for MVCC_INTVALUE
#include PixelType.h // for PixelType_Gvsp_BGR8_Packedclass HKCamera {
private:void* handle nullptr;MVCC_INTVALUE stParam;MV_FRAME_OUT_INFO_EX stImageInfo {0};unsigned char* pData nullptr;unsigned int nDataSize 0;cv::Mat frame; // OpenCV图像
public:HKCamera() {// 在构造函数中初始化结构体memset(stParam, 0, sizeof(MVCC_INTVALUE));memset(stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));}bool InitCamera() {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) {std::cout 枚举设备失败! nRet [ nRet ] std::endl;return false;}if (stDeviceList.nDeviceNum 0) {// 创建句柄nRet MV_CC_CreateHandle(handle, stDeviceList.pDeviceInfo[0]);if (MV_OK ! nRet) {std::cout 创建句柄失败! nRet [ nRet ] std::endl;return false;}// 打开设备nRet MV_CC_OpenDevice(handle);if (MV_OK ! nRet) {std::cout 打开设备失败! nRet [ nRet ] std::endl;return false;}} else {std::cout 未找到设备! std::endl;return false;}return true;}// 设置相机参数bool SetParameters() {int nRet;// 设置触发模式为offnRet MV_CC_SetEnumValue(handle, TriggerMode, 0);if (MV_OK ! nRet) {std::cout 设置触发模式失败! nRet [ nRet ] std::endl;return false;}// 设置像素格式为BGR8nRet MV_CC_SetEnumValue(handle, PixelFormat, PixelType_Gvsp_BGR8_Packed);if (MV_OK ! nRet) {std::cout 设置像素格式失败! nRet [ nRet ] std::endl;return false;}std::cout 设置像素格式为BGR8_Packed std::endl;// 获取和设置曝光时间MVCC_FLOATVALUE stExposureTime {0};nRet MV_CC_GetFloatValue(handle, ExposureTime, stExposureTime);if (MV_OK nRet) {float exposureTime 10000.0f; // 默认10msif (exposureTime stExposureTime.fMin) exposureTime stExposureTime.fMin;if (exposureTime stExposureTime.fMax) exposureTime stExposureTime.fMax;std::cout 曝光时间范围: [ stExposureTime.fMin , stExposureTime.fMax ], 当前设置: exposureTime std::endl;nRet MV_CC_SetFloatValue(handle, ExposureTime, exposureTime);if (MV_OK ! nRet) {std::cout 设置曝光时间失败! nRet [ nRet ] std::endl;return false;}}// 获取和设置增益MVCC_FLOATVALUE stGain {0};nRet MV_CC_GetFloatValue(handle, Gain, stGain);if (MV_OK nRet) {std::cout 增益范围: [ stGain.fMin , stGain.fMax ], 当前值: stGain.fCurValue std::endl;if (stGain.fCurValue stGain.fMin || stGain.fCurValue stGain.fMax) {float gain stGain.fMin; // 使用最小值nRet MV_CC_SetFloatValue(handle, Gain, gain);if (MV_OK ! nRet) {std::cout 设置增益失败! nRet [ nRet ] std::endl;return false;}}}// 获取和设置帧率MVCC_FLOATVALUE stFrameRate {0};nRet MV_CC_GetFloatValue(handle, AcquisitionFrameRate, stFrameRate);if (MV_OK nRet) {float frameRate 30.0f; // 默认30fpsif (frameRate stFrameRate.fMin) frameRate stFrameRate.fMin;if (frameRate stFrameRate.fMax) frameRate stFrameRate.fMax;std::cout 帧率范围: [ stFrameRate.fMin , stFrameRate.fMax ], 当前设置: frameRate std::endl;nRet MV_CC_SetFloatValue(handle, AcquisitionFrameRate, frameRate);if (MV_OK ! nRet) {std::cout 设置帧率失败! nRet [ nRet ] std::endl;// 这个错误不影响主要功能可以继续}}return true;}// 开始取图bool StartGrabbing() {// 开始取流int nRet MV_CC_StartGrabbing(handle);if (MV_OK ! nRet) {std::cout 开始取流失败! nRet [ nRet ] std::endl;return false;}// 获取数据包大小MVCC_INTVALUE stParam;nRet MV_CC_GetIntValue(handle, PayloadSize, stParam);if (MV_OK ! nRet) {std::cout 获取数据包大小失败! nRet [ nRet ] std::endl;return false;}// 分配资源nDataSize stParam.nCurValue;pData (unsigned char*)malloc(nDataSize);if (pData nullptr) {std::cout 内存分配失败! std::endl;return false;}return true;}// 获取一帧图像并显示bool GetOneFrameAndShow() {if (handle nullptr || pData nullptr) {return false;}int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;}std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] PixelType[0x std::hex stImageInfo.enPixelType std::dec ] FrameNum[ stImageInfo.nFrameNum ] std::endl;// 转换为OpenCV格式并显示if (stImageInfo.enPixelType PixelType_Gvsp_BGR8_Packed) {// 直接使用BGR数据创建Matframe cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC3, pData);// 简单的图像增强cv::Mat temp;// 轻微提升对比度frame.convertTo(temp, -1, 1.1, 0);frame temp;} else {std::cout 不支持的像素格式: 0x std::hex stImageInfo.enPixelType std::dec std::endl;return false;}if (!frame.empty()) {cv::imshow(Camera, frame);cv::waitKey(1);}return true;}// 停止采集void StopGrabbing() {if (handle ! nullptr) {MV_CC_StopGrabbing(handle);MV_CC_CloseDevice(handle);MV_CC_DestroyHandle(handle);handle nullptr;}if (pData ! nullptr) {free(pData);pData nullptr;}}~HKCamera() {StopGrabbing();}
};
int main() {HKCamera camera;// 初始化相机if (!camera.InitCamera()) {std::cout 相机初始化失败! std::endl;return -1;}std::cout 相机初始化成功! std::endl;// 设置参数if (!camera.SetParameters()) {std::cout 设置相机参数失败! std::endl;return -1;}std::cout 设置相机参数成功! std::endl;// 开始取图if (!camera.StartGrabbing()) {std::cout 开始取图失败! std::endl;return -1;}std::cout 开始取图成功! std::endl;// 持续获取并显示图像直到按下ESC键while (true) {if (!camera.GetOneFrameAndShow()) {break;}// 检查ESC键是否按下char key cv::waitKey(1);if (key 27) { // ESC键的ASCII码break;}}// 停止采集camera.StopGrabbing();std::cout 停止采集完成! std::endl;return 0;
}3海康相机官方简易APIX11窗口实时取流部署教程 但是我们会发现如果我们摄像头采集的是BGR格式那么用OpenCV会比较方便但是如果我们采集的不是BGR格式呢那用OpenCV来显示图像便还需要进行图像的转换非常的复杂但是如果我们用海康官方的API再加Linux的OpenGL来显示图像那么我们便无需关心图像格式的转换问题啦接下来我们便开始用海康官方显示APILinux-OpenGL-X11来实时显示我们的图像海康API中有关显示的函数有三个其中一个即将被废除具体介绍如下
/********************************************************************//*** ~chinese* brief 显示一帧图像* param handle [IN] 设备句柄* param pstDisplayInfo [IN] 图像信息* return 成功返回MV_OK错误返回错误码 * remarks 与设备类型无关渲染模式为D3D时支持的最大分辨率为16384 * 163840***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_DisplayOneFrame(IN void* handle, IN MV_DISPLAY_FRAME_INFO* pstDisplayInfo);
/********************************************************************//*** ~chinese* brief 显示一帧图像* param handle [IN] 设备句柄* param hWnd [IN] 窗口句柄* param pstDisplayInfo [IN] 图像信息* return 成功返回MV_OK错误返回错误码* remarks 该接口支持渲染宽高大小至int类型* 渲染模式为D3D时支持的最大分辨率为16384 * 163840***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_DisplayOneFrameEx(IN void* handle, IN void* hWnd, IN MV_DISPLAY_FRAME_INFO_EX* pstDisplayInfo);/********************************************************************//*** ~chinese* brief 显示一帧图像* param handle [IN] 设备句柄* param hWnd [IN] 窗口句柄* param pstImage [IN] 图像信息* param enRenderMode [IN] 渲染方式Windows:0-GDI 1-D3D 2-OpenGL Linux:0-OpenGL * return 成功返回MV_OK错误返回错误码* remarks 可选择OpenGL渲染模式支持PixelType_Gvsp_RGB8_PackedPixelType_Gvsp_BGR8_PackedPixelType_Gvsp_Mono8三种像素格式图像大小超过4GB的渲染其他渲染模式不支持。若图像大小未超过4GB支持宽高大小至int类型调用时需要输入MV_CC_IMAGE结构体中nImageLen的值渲染模式为D3D时支持的最大分辨率为16384 * 163840***********************************************************************/
MV_CAMCTRL_API int __stdcall MV_CC_DisplayOneFrameEx2(IN void* handle, IN void* hWnd, IN MV_CC_IMAGE* pstImage, unsigned int enRenderMode);MV_CC_DisplayOneFrame()函数为基础函数他的功能最简单但分辨率限制较大并且即将被废除、MV_CC_DisplayOneFrameEx()函数为扩展函数他支持更大分辨率也支持Windows上的D3D渲染但是不支持超大图像而MV_CC_DisplayOneFrameEx2()函数支持选择渲染模式支持超大图像也支持更多像素格式且性能最好 接下来我们将以最简单的MV_CC_DisplayOneFrame()函数来完成示例**我们使用X11窗口来显示图像**首先我们导入X11包接着我们在类中新增创建窗口句柄函数和显示函数并且修改我们的取帧函数
#include X11/Xlib.h
class HKCamera {
private:void* displayHandle nullptr; // 显示窗口句柄
public:HKCamera() {// 在构造函数中正确初始化结构体memset(stParam, 0, sizeof(MVCC_INTVALUE));memset(stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));}bool SetDisplayWindow(void* windowHandle) //创建显示窗口句柄bool DisplayOneFrame() //新建显示bool GetOneFrameAndShow()//修改GetOneFrame()函数接着我们需要利用我们的SetDisplayWindow(void* windowHandle)函数来创建一个X11窗口的句柄
// 设置显示窗口
bool SetDisplayWindow(void* windowHandle) {displayHandle windowHandle;return true;
}然后我们来创建我们的DisplayOneFrame()函数根据我们上面的函数介绍我们需要在使用MV_CC_DisplayOneFrame()函数前先定义一个MV_DISPLAY_FRAME_INFO类型的结构体
typedef struct _MV_DISPLAY_FRAME_INFO_
{void* hWnd; /// [IN] \~chinese 窗口句柄 \~english HWNDunsigned char* pData; /// [IN] \~chinese 显示的数据 \~english Data Bufferunsigned int nDataLen; /// [IN] \~chinese 数据长度 \~english Data Sizeunsigned short nWidth; /// [IN] \~chinese 图像宽 \~english Widthunsigned short nHeight; /// [IN] \~chinese 图像高 \~english Heightenum MvGvspPixelType enPixelType; /// [IN] \~chinese 像素格式 \~english Pixel formatunsigned int enRenderMode; /// [IN] \~chinese 图像渲染方式Windows:0-GDI(默认), 1-D3D, 2-OPENGL Linux: 0-OPENGL(默认) unsigned int nRes[3]; /// \~chinese 保留 \~english Reserved
}MV_DISPLAY_FRAME_INFO;根据上述结构体的定义我们先行配置我们的stDisplayInfo参数
MV_DISPLAY_FRAME_INFO stDisplayInfo {0};
stDisplayInfo.hWnd displayHandle;
stDisplayInfo.pData pData;
stDisplayInfo.nDataLen stImageInfo.nFrameLen;
stDisplayInfo.nWidth stImageInfo.nWidth;
stDisplayInfo.nHeight stImageInfo.nHeight;
stDisplayInfo.enPixelType stImageInfo.enPixelType;在定义完这个结构体之后我们便可以使用显示函数API啦具体的使用代码如下
// 显示一帧图像
bool DisplayOneFrame() {if (handle nullptr || pData nullptr || displayHandle nullptr) {return false;}// 准备显示信息MV_DISPLAY_FRAME_INFO stDisplayInfo {0};stDisplayInfo.hWnd displayHandle;stDisplayInfo.pData pData;stDisplayInfo.nDataLen stImageInfo.nFrameLen;stDisplayInfo.nWidth stImageInfo.nWidth;stDisplayInfo.nHeight stImageInfo.nHeight;stDisplayInfo.enPixelType stImageInfo.enPixelType;// 显示图像int nRet MV_CC_DisplayOneFrame(handle, stDisplayInfo);if (MV_OK ! nRet) {std::cout 显示图像失败! nRet [ nRet ] std::endl;return false;}return true;
}接着我们将原来的GetOneFrame()函数中间添加图像显示函数DisplayOneFrame()即可
// 获取一帧图像并显示
bool GetOneFrameAndShow() {if (handle nullptr || pData nullptr) {return false;}int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;}std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] FrameNum[ stImageInfo.nFrameNum ] std::endl;/* --------------显示图像 -----------------------*/if (displayHandle ! nullptr) {return DisplayOneFrame();}/* --------------显示图像 -----------------------*/return true;
}之后便开始修改我们的主函数啦首先在主函数中创建X11窗口并且捕获屏幕信息
// 创建X11窗口
Display* display XOpenDisplay(NULL);
if (!display) {std::cout 无法连接到X服务器! std::endl;return -1;
}
// 获取屏幕信息
int screen DefaultScreen(display);
Window root DefaultRootWindow(display);接着我们设置窗口的基本参数并设置窗口标题和窗口关闭事件
// 创建窗口
Window window XCreateSimpleWindow(display, // Displayroot, // 父窗口0, 0, // 位置1440, 1080, // 大小使用相机分辨率1, // 边框宽度BlackPixel(display, screen), // 边框颜色WhitePixel(display, screen) // 背景颜色
);
// 设置窗口标题
XStoreName(display, window, Camera Display);
// 设置窗口接收关闭事件
Atom wmDeleteMessage XInternAtom(display, WM_DELETE_WINDOW, False);
XSetWMProtocols(display, window, wmDeleteMessage, 1);最后我们显示窗口并持续捕获图像并使用X11事件显示即可
// 显示窗口
XMapWindow(display, window);
XFlush(display);
// 设置显示窗口
camera.SetDisplayWindow((void*)window);
// 开始取图
if (!camera.StartGrabbing()) {std::cout 开始取图失败! std::endl;XCloseDisplay(display);return -1;
}
std::cout 开始取图成功! std::endl;
// 持续获取并显示图像
bool running true;
while (running) {if (!camera.GetOneFrameAndShow()) {break;}// 处理X11事件while (XPending(display)) {XEvent event;XNextEvent(display, event);// 处理窗口关闭事件if (event.type ClientMessage) {if (event.xclient.data.l[0] wmDeleteMessage) {running false;}}}
}接着我们修改一下我们的Cmake即可
# 查找X11包
find_package(X11 REQUIRED)
include_directories(${PROJECT_SOURCE_DIR}/include${MVCAM_INCLUDE_PATH}${OpenCV_INCLUDE_DIRS}
)
# 链接海康相机库和X11库
target_link_libraries(${PROJECT_NAME}MvCameraControl # 海康相机主库pthread # 线程库${X11_LIBRARIES} # X11库
)最后我们X11串口海康的显示API的完整的代码及效果如下 #include iostream
#include string
#include cstring // for memset
#include X11/Xlib.h
#include MvCameraControl.h
#include CameraParams.h // for MVCC_INTVALUE
#include PixelType.h // for PixelType_Gvsp_BGR8_Packed
class HKCamera {
private:void* handle nullptr;MVCC_INTVALUE stParam;MV_FRAME_OUT_INFO_EX stImageInfo {0};unsigned char* pData nullptr;unsigned int nDataSize 0;void* displayHandle nullptr; // 显示窗口句柄
public:HKCamera() {// 在构造函数中正确初始化结构体memset(stParam, 0, sizeof(MVCC_INTVALUE));memset(stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));}bool InitCamera() {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) {std::cout 枚举设备失败! nRet [ nRet ] std::endl;return false;}if (stDeviceList.nDeviceNum 0) {// 创建句柄nRet MV_CC_CreateHandle(handle, stDeviceList.pDeviceInfo[0]);if (MV_OK ! nRet) {std::cout 创建句柄失败! nRet [ nRet ] std::endl;return false;}// 打开设备nRet MV_CC_OpenDevice(handle);if (MV_OK ! nRet) {std::cout 打开设备失败! nRet [ nRet ] std::endl;return false;}} else {std::cout 未找到设备! std::endl;return false;}return true;}// 设置相机参数bool SetParameters() {int nRet;// 设置触发模式为offnRet MV_CC_SetEnumValue(handle, TriggerMode, 0);if (MV_OK ! nRet) {std::cout 设置触发模式失败! nRet [ nRet ] std::endl;return false;}// 设置像素格式为BGR8nRet MV_CC_SetEnumValue(handle, PixelFormat, PixelType_Gvsp_BGR8_Packed);if (MV_OK ! nRet) {std::cout 设置像素格式失败! nRet [ nRet ] std::endl;return false;}std::cout 设置像素格式为BGR8_Packed std::endl;// 获取和设置曝光时间MVCC_FLOATVALUE stExposureTime {0};nRet MV_CC_GetFloatValue(handle, ExposureTime, stExposureTime);if (MV_OK nRet) {float exposureTime 10000.0f; // 默认10msif (exposureTime stExposureTime.fMin) exposureTime stExposureTime.fMin;if (exposureTime stExposureTime.fMax) exposureTime stExposureTime.fMax;std::cout 曝光时间范围: [ stExposureTime.fMin , stExposureTime.fMax ], 当前设置: exposureTime std::endl;nRet MV_CC_SetFloatValue(handle, ExposureTime, exposureTime);if (MV_OK ! nRet) {std::cout 设置曝光时间失败! nRet [ nRet ] std::endl;return false;}}// 获取和设置增益MVCC_FLOATVALUE stGain {0};nRet MV_CC_GetFloatValue(handle, Gain, stGain);if (MV_OK nRet) {std::cout 增益范围: [ stGain.fMin , stGain.fMax ], 当前值: stGain.fCurValue std::endl;if (stGain.fCurValue stGain.fMin || stGain.fCurValue stGain.fMax) {float gain stGain.fMin; // 使用最小值nRet MV_CC_SetFloatValue(handle, Gain, gain);if (MV_OK ! nRet) {std::cout 设置增益失败! nRet [ nRet ] std::endl;return false;}}}// 获取和设置帧率MVCC_FLOATVALUE stFrameRate {0};nRet MV_CC_GetFloatValue(handle, AcquisitionFrameRate, stFrameRate);if (MV_OK nRet) {float frameRate 30.0f; // 默认30fpsif (frameRate stFrameRate.fMin) frameRate stFrameRate.fMin;if (frameRate stFrameRate.fMax) frameRate stFrameRate.fMax;std::cout 帧率范围: [ stFrameRate.fMin , stFrameRate.fMax ], 当前设置: frameRate std::endl;nRet MV_CC_SetFloatValue(handle, AcquisitionFrameRate, frameRate);if (MV_OK ! nRet) {std::cout 设置帧率失败! nRet [ nRet ] std::endl;// 这个错误不影响主要功能可以继续}}return true;}// 开始取图bool StartGrabbing() {// 开始取流int nRet MV_CC_StartGrabbing(handle);if (MV_OK ! nRet) {std::cout 开始取流失败! nRet [ nRet ] std::endl;return false;}// 获取数据包大小MVCC_INTVALUE stParam;nRet MV_CC_GetIntValue(handle, PayloadSize, stParam);if (MV_OK ! nRet) {std::cout 获取数据包大小失败! nRet [ nRet ] std::endl;return false;}// 分配资源nDataSize stParam.nCurValue;pData (unsigned char*)malloc(nDataSize);if (pData nullptr) {std::cout 内存分配失败! std::endl;return false;}return true;}// 设置显示窗口bool SetDisplayWindow(void* windowHandle) {displayHandle windowHandle;return true;}// 显示一帧图像bool DisplayOneFrame() {if (handle nullptr || pData nullptr || displayHandle nullptr) {return false;}// 准备显示信息MV_DISPLAY_FRAME_INFO stDisplayInfo {0};stDisplayInfo.hWnd displayHandle;stDisplayInfo.pData pData;stDisplayInfo.nDataLen stImageInfo.nFrameLen;stDisplayInfo.nWidth stImageInfo.nWidth;stDisplayInfo.nHeight stImageInfo.nHeight;stDisplayInfo.enPixelType stImageInfo.enPixelType;// 显示图像int nRet MV_CC_DisplayOneFrame(handle, stDisplayInfo);if (MV_OK ! nRet) {std::cout 显示图像失败! nRet [ nRet ] std::endl;return false;}return true;}// 获取一帧图像并显示bool GetOneFrameAndShow() {if (handle nullptr || pData nullptr) {return false;}int nRet MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, stImageInfo, 1000);if (MV_OK ! nRet) {std::cout 获取一帧图像失败! nRet [ nRet ] std::endl;return false;}std::cout 获取一帧图像成功: Width[ stImageInfo.nWidth ] Height[ stImageInfo.nHeight ] FrameNum[ stImageInfo.nFrameNum ] std::endl;// 显示图像if (displayHandle ! nullptr) {return DisplayOneFrame();}return true;}// 停止采集void StopGrabbing() {if (handle ! nullptr) {MV_CC_StopGrabbing(handle);MV_CC_CloseDevice(handle);MV_CC_DestroyHandle(handle);handle nullptr;}if (pData ! nullptr) {free(pData);pData nullptr;}}~HKCamera() {StopGrabbing();}
};
int main() {HKCamera camera;// 初始化相机if (!camera.InitCamera()) {std::cout 相机初始化失败! std::endl;return -1;}std::cout 相机初始化成功! std::endl;// 设置参数if (!camera.SetParameters()) {std::cout 设置相机参数失败! std::endl;return -1;}std::cout 设置相机参数成功! std::endl;// 创建X11窗口Display* display XOpenDisplay(NULL);if (!display) {std::cout 无法连接到X服务器! std::endl;return -1;}// 获取屏幕信息int screen DefaultScreen(display);Window root DefaultRootWindow(display);// 创建窗口Window window XCreateSimpleWindow(display, // Displayroot, // 父窗口0, 0, // 位置1440, 1080, // 大小使用相机分辨率1, // 边框宽度BlackPixel(display, screen), // 边框颜色WhitePixel(display, screen) // 背景颜色);// 设置窗口标题XStoreName(display, window, Camera Display);// 设置窗口接收关闭事件Atom wmDeleteMessage XInternAtom(display, WM_DELETE_WINDOW, False);XSetWMProtocols(display, window, wmDeleteMessage, 1);// 显示窗口XMapWindow(display, window);XFlush(display);// 设置显示窗口camera.SetDisplayWindow((void*)window);// 开始取图if (!camera.StartGrabbing()) {std::cout 开始取图失败! std::endl;XCloseDisplay(display);return -1;}std::cout 开始取图成功! std::endl;// 持续获取并显示图像bool running true;while (running) {if (!camera.GetOneFrameAndShow()) {break;}// 处理X11事件while (XPending(display)) {XEvent event;XNextEvent(display, event);// 处理窗口关闭事件if (event.type ClientMessage) {if (event.xclient.data.l[0] wmDeleteMessage) {running false;}}}}// 停止采集camera.StopGrabbing();std::cout 停止采集完成! std::endl;// 关闭X11连接XCloseDisplay(display);return 0;
}有任何问题大家都可以在评论区询问哦我看到了便会及时回复哒!!!