海康工业相机的应用部署不是简简单单!?

news/2025/1/18 4:44:45/文章来源:https://www.cnblogs.com/SkyXZ/p/18677928

作者:SkyXZ

CSDN:SkyXZ~-CSDN博客

博客园:SkyXZ - 博客园

笔者使用的设备及环境:WSL2-Ubuntu22.04+MV-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版本的客户端

image-20250118041419982

        下载完成后我们会得到一个ZIP压缩包,我们解压后可以看到有很多不同CPU架构的版本,我们选择适合自己系统的版本使用如下命令即可完成安装,安装完成后的客户端将在/opt/MVS目录下

微信截图_20250117134813

sudo dpkg -i MVS-***********.deb #自行替换适合自己的系统版本安装包

        我们使用如下命令即可进入MVS安装环境并且启动我们的客户端

cd /opt/MVS/bin #进入软件目录
./MVS.sh #运行客户端

        运行客户端后我们可以看到我们的客户端主界面如下,左边主要为我们的设备区在这个区域我们可以看到当前连接到我们电脑的所有海康相机设备,最上方为菜单栏提供文件、视图、设置、工具和帮助的功能,下方为控制工具条可以为相机参数保存等操作提供快捷入口

image-20250118042210593

        接着我们选中我们的相机可以看到我们进入了如下界面,在这个界面中左下方为接口和设备信息获取窗口可以显示设备详细信息,中间为图像预览窗口上方左一按钮点击后即可开启相机实时取流查看图像,右边为属性设置窗口,可以对相机的基本参数进行设置,如:触发模式、增益、曝光时间、帧率、像素格式等等

image-20250118042654848

        由于属性设置客户端中有中文介绍,那么我们接着主要开始讲解我们的SDK-API的使用方法叭

二、海康相机开发实战上手

        在我们上一节安装的MVS目录下存在着海康相机驱动所需要的全部头文件以及链接库,我们首先在我们的工作空间下面新建一个文件夹,接着将MVS安装目录下的includelib文件全部复制进来

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文件

        我们首先来看一下海康相机组件包的项目结构叭,要驱动海康工业相机需要有两个主要的驱动包includelib,其具体的结构以及对应的作用请看下图:

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的主要接口文件

image-20250117141841904

  • 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_DEVICE;MV_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_SetEnumValueMV_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内触发模式,同时设置我们的像素格式为BayerRG8

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

        最后我们完整的代码及运行效果如下:(但是又有同学要问了,我们该如何显示图像呢?请继续翻到下面,我将在下面进行介绍)

image-20250117235331337

#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显示图像的完整的代码及显示效果如下:

image-20250118034644685

image-20250118034858385

#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)海康相机官方简易API+X11窗口实时取流部署教程

        但是我们会发现,如果我们摄像头采集的是BGR格式那么用OpenCV会比较方便,但是如果我们采集的不是BGR格式呢?那用OpenCV来显示图像便还需要进行图像的转换,非常的复杂!!!但是如果我们用海康官方的API再加Linux的OpenGL来显示图像那么我们便无需关心图像格式的转换问题啦!接下来我们便开始用海康官方显示API+Linux-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_Packed,PixelType_Gvsp_BGR8_Packed,PixelType_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的完整的代码及效果如下:

image-20250118034359365

image-20250118034341634

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/871102.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3D-NAND 计算(上)

3D-NAND 闪存工艺成熟并且存储密度极高, 基于 3D-NAND 的神经形态芯片受到许多研究者的关注. 然而由于该技术的专利性质, 少有基 于 3D-NAND 神经形态计算的硬件实现. 本文综述了用 3D-NAND 实现神经形态计算的工作, 介绍了其中前 向传播和反向传播的机制, 并提出了目前 3D NAN…

blender4.3.2-修改器

关于修改器的其他问题 1.在修改器执行应用前,无法与其他物体进行合并 阵列修改器 生成->阵列指定数量和间隔,生成克隆体,所有克隆体同步发生选中和修改 倒角修改器 生成->倒角使用倒角修改器而不直接使用编辑模式中的倒角,好处在于像立方体这种使用了倒角修改器而未应…

推荐书籍《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》4本,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

2025 最佳免费商用文本转语音模型: Kokoro TTS

在文本转语音(TTS)技术领域,一项突破性的进展引起了广泛关注——Kokoro TTS 模型凭借其卓越性能和完全免费的商用许可,成为目前最出色的 TTS 解决方案之一。基于广受欢迎的开源框架 StyleTTS,Kokoro TTS 在灵活性和功能性上都表现出色,可广泛应用于多种场景。接下来,我们…

2025春秋杯部分wpDAY1

2025春秋杯 DAY1 WEB easy_flask 直接fenjing一把梭file_copy 下载github上的脚本MISC 简单算术 题目提示了异或简单镜像提取formost提取到镜像文件然后用autopsy打开flag{E7A10C15E26AA5750070EF756AAA1F7C} CRYPTO 通往哈希的旅程 import hashlib# 目标哈希值 target_hash = …

【教育行业】2024中国网络安全产业势能榜优能企业「教育行业」典型案例展示

教育行业的数字化转型不断加速,线上教学、学籍管理、科研数据等方面的安全问题日益突出。随着教育信息化的不断推进,如何保护学生和教师的个人信息、确保教学平台的安全成为亟待解决的重要课题。我们将通过一些典型案例,展示教育行业在提升信息安全方面的最新成果和应对之策…

去攀登更高的山,渡过更长的河--软件工程个人总结

轻舟已过万重山——2024秋软工实践个人总结博客一、学期回顾 1.1 回顾你对于软件工程课程的想象 初次接触软件工程这门课程时,我内心充满了忐忑与不安 😰。作为一门实践性极强的课程,它不仅要求我们掌握各种开发技术,还需要我们具备团队协作、项目管理等综合能力。我记得第…

Avalonia系列文章之小试牛刀

最近有朋友反馈,能否分享一下Avalonia相关的文章,于是就抽空学习了一下,发现Avalonia真的是一款非常不错的UI框架,值得花时间认真学习一下,于是边学习边记录,整理成文,分享给大家,希望可以一起学习,共同进步。最近有朋友反馈,能否分享一下Avalonia相关的文章,于是就…

器件选型基础知识

器件选型基础知识器件选型基础知识 1. PCB结构与工艺 PCB主要由五部分组成,分别是介电层、孔、防焊墨油、丝印和线路组成。介电层:用来保持线路及各层之间的绝缘性,俗称为基材,最常见的材料是玻璃纤维; 孔:导通孔可以使两层次以上的线路彼此导通; 防焊墨油:对于整个电路…

【CodeForces训练记录】Codeforces Round 997 (Div. 2)

训练情况赛后反思 A题犯蠢了,题目看成面积了,C题应该可以更快的搓出来,只能说结论猜了几次不对 A题 我们把周长移动一下,补成一个长方形,左下角的坐标为 \((x_1,y_1)\),右上角坐标为 \((\sum{x_i}+m,\sum{y_i}+m)\),已知两点直接求周长 #include <bits/stdc++.h> …

引用 EAGLE/EAGLE2 的文章汇总

引用 EAGLE/EAGLE2 的文章汇总这张图是 eagle 和 previous approaches 的加速效果对比。 引用 eagle 的文章也可以被分成很多类,它们引用 eagle/eagle2 的侧重点也很多样。有不少文章是把 eagle 和 eagle2 作为 speculative decoding 的示例,这种不再在这里列出了。有些文章我…

【Kali工具】一款聚合暴力破解工具 Kraken

本文为大家分析一款聚合密码破解工具。集成了常见的破解类型,上手也比较简单,一起来看看吧。 在Kali中安装在Kali或者在其他Linux系统中,我们执行下面命令进行安装。 git clone https://github.com/jasonxtn/kraken.git cd Kraken pip install -r requirements.txt #运行 py…