赞
踩
本篇主要对海康威视的监控相机的SDK回调进行研究,并于opencv结合,保存图像,以供后续其他处理,开发语言为C++
海康SDK下载地址
根据自身编译环境,下载对应的SDK,需要注意的是,不要和工业相机SDK相混淆,工业相机好像是MVS是什么玩意儿,现在暂时没研究
SDK包的结构如下,包括需要的头文件和库目录,其中windows下还提供了Demo示例,开发文档是我们时常需要用到的,结构如下:
其中SDK编程指南是需要注意的一个地方,通常需要根据自己的相机型号选择对应的文件,有些相机型号可能不支持,比如我使用的DS-2CD7********,这款相机,就只支持IPC的,查看文档是否支持相机,可以打开文档,在SDK简介中查看:
SDK文件确定后可以开始后续的开发了
打开visual studio 2022,配置sdk环境,包括前面提到的库文件和头文件,其中用到了opencv,配置opencv的相关头文件和库文件:
配置方式不多说,也可以在VC++目录中添加
添加完成后,编写头文件,蒋需用到的库包含进来,包括opencv的和海康sdk的:
其中有一个PlayM4.h头文件,sdk里面是没有的,需要在示例文件中拷贝过来,opencv如果是编译成一个opencv_world的,直接配置上去就行了
海康相机配置比较容易,初始的IP地址是192.168.1.64,可以根据实际使用情况,修改相机的IP,登录的用户名和密码,sdk调用的时候需要用到这3个信息。
SDK取流在文档的第4章《函数调用示例》中,直接复制,粘贴,修改好自己相机的IP,用户名和密码就可以正常调用起来了,主要分为以下几个部分:
(1)初始化
初始化,包括设备注册,设置连接时间和重连时间,设置异常回调函数等,其中最重要的是设置登录相机设备的IP地址,用户名和密码。
(2)调用相机
调用相机有多种方式,可以直接使用rtsp取流,也可以直接使用海康sdk提供的回调取流函数,这一部分需要参考sdk开发文档,不只是代码示例,还有sdk提供的其他函数,可参考5《函数说明》,例如,我需要拿到图像后和opencv结合,那么需要拿到图像的数据,就需要用到以下接口了:
本篇主要解决的问题是,sdk拿到的图像通过opencv保存下来并且可以做其他操作,sdk中提供的参考示例使用了回调函数,这里给出两种保存方法:
使用预览的方式存图,需要定义一个全局的Mat变量,最终保存图像在回调函数中完成,整个工程实例如下:
#include <opencv2\opencv.hpp>
//#include "hkinterface.h"
#include <HCNetSDK.h>
#include <plaympeg4.h>
#include <PlayM4.h>
using namespace std;
using namespace cv;
Mat g_BGRImage;
LONG g_nPort;
//数据解码回调函数将YV_12格式的视频数据流转码为可供opencv处理的BGR类型的图片数据。
void CALLBACK DecCBFun(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, long nUser, long nReserved2)
{
if (pFrameInfo->nType == T_YV12)
{
std::cout << "the frame infomation is T_YV12" << std::endl;
if (g_BGRImage.empty())
{
g_BGRImage.create(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);
}
Mat YUVImage(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (unsigned char*)pBuf);
cvtColor(YUVImage, g_BGRImage, COLOR_YUV2BGR_YV12);
//保存图像
static int count = 0;
count += 1;
string name = to_string(count) + ".jpg";
cout << "g_BGRImage: " << g_BGRImage.empty() << "========" << name << endl;
if (!g_BGRImage.empty()) {
imwrite("D:\\桌面文件\\save_imgsdk\\" + name, g_BGRImage);
}
YUVImage.~Mat();
}
}
//获取播放库通道号
void getPort() {
if (PlayM4_GetPort(&g_nPort))
{
if (PlayM4_SetStreamOpenMode(g_nPort, STREAME_REALTIME)) //设置流模式
{
if (PlayM4_OpenStream(g_nPort, NULL, 0, 1440 * 2560)) //打开流
{
if (PlayM4_SetDecCallBackExMend(g_nPort, DecCBFun, NULL, 0, NULL))
{
if (PlayM4_Play(g_nPort, NULL))
{
std::cout << "success to set play mode" << std::endl;
}
else
{
std::cout << "fail to set play mode" << std::endl;
}
}
else
{
std::cout << "fail to set dec callback " << std::endl;
}
}
else
{
std::cout << "fail to open stream" << std::endl;
}
}
else
{
std::cout << "fail to set stream open mode" << std::endl;
}
}
else
{
std::cout << "fail to get port" << std::endl;
}
}
//实时视频码流数据获取 回调函数
void CALLBACK g_RealDataCallBack_V30(LONG lPlayHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* pUser)
{
if (dwDataType == NET_DVR_STREAMDATA)//码流数据
{
if (dwBufSize > 0 && g_nPort != -1)
{
if (!PlayM4_InputData(g_nPort, pBuffer, dwBufSize))
{
std::cout << "fail input data" << std::endl;
}
else
{
std::cout << "success input data" << std::endl;
//这里也可以存图
/*static int count = 0;
count += 1;
string name = to_string(count) + ".jpg";
cout << "g_BGRImage: " << g_BGRImage.empty() << "========" << name << endl;
if (!g_BGRImage.empty()) {
imwrite("D:\\桌面文件\\save_imgsdk\\" + name, g_BGRImage);
}*/
}
}
}
}
int main()
{
//------------------------------
// 初始化
NET_DVR_Init();
// 设置连接时间与重连时间
NET_DVR_SetConnectTime(2000, 1);
NET_DVR_SetReconnect(10000, true);
//------------------------------
// 登录
NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
string CameraIPAddress = "172.16.100.223";
string CameraUserName = "admin";
string CameraPassword = "123456";
//启动预览并设置回调数据流
NET_DVR_PREVIEWINFO struPlayInfo = {0};
struLoginInfo.bUseAsynLogin = 0; // 同步登录方式
struLoginInfo.wPort = 8000; //设备服务端口
strcpy_s(struLoginInfo.sDeviceAddress, CameraIPAddress.c_str()); //设备IP地址
strcpy_s(struLoginInfo.sUserName, CameraUserName.c_str()); //设备登录用户名
strcpy_s(struLoginInfo.sPassword, CameraPassword.c_str()); //设备登录密码
LONG lUserID;
//设备信息, 输出参数
NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = { 0 };
lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
if (lUserID < 0)
{
cout << "Login error: " << NET_DVR_GetLastError() << endl;;
NET_DVR_Cleanup();
return -1;
}
else {
cout << "Login success!" << endl;
}
getPort();
//启动预览并设置回调数据流
struPlayInfo.hPlayWnd = NULL; //需要 SDK 解码时句柄设为有效值,仅取流不解码时可设为空
struPlayInfo.lChannel = 1; //预览通道号
struPlayInfo.dwStreamType = 0; //0-主码流,1-子码流,2-码流 3,3-码流 4,以此类推
struPlayInfo.dwLinkMode = 0; //0- TCP 方式,1- UDP 方式,2- 多播方式,3- RTP 方式,4-RTP/RTSP,5-RSTP/HTTP
struPlayInfo.bBlocked = 1; //0- 非阻塞取流,1- 阻塞取流
LONG lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, g_RealDataCallBack_V30, NULL);
if (lRealPlayHandle < 0)
{
//printf(“NET_DVR_RealPlay_V40 error\n”);
NET_DVR_Logout(lUserID);
NET_DVR_Cleanup();
return -1;
}
//Sleep(1000); //测试1秒能存几张
system("pause");
return 0;
}
参考的sdk:
补充:最近使用时发现了一些报错问题,这种方式有一个缺点,SDK参考文档里面也指出了,使用的时候根据自身情况选择:
这部分代码已经打包上传,其中需要修改的部分
//使用海康的SDK抓取图像
HKSDK_LIB_API int __stdcall playCapture(void* user,int count, Mat &save_image_l,Mat &save_image_r)
{
//外部多线程使用
LPMyStruct stru = (LPMyStruct)user;
//启动预览并设置回调数据流
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = NULL; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
struPlayInfo.lChannel = stru->channel; //预览通道号
struPlayInfo.dwStreamType = 0; //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
struPlayInfo.bBlocked = 1; //0- 非阻塞取流,1- 阻塞取流
LPNET_DVR_JPEGPARA JpegPara = new NET_DVR_JPEGPARA;
JpegPara->wPicQuality = 0;
JpegPara->wPicSize = 70;
int w = 2560;
int h = 1440;
char* Jpeg = new char[w * h];
DWORD len = w * h;
LPDWORD Ret = 0;
if (!NET_DVR_SetCapturePictureMode(BMP_MODE))
{
cout << "Set Capture Picture Mode error!" << endl;
cout << "The error code is " << NET_DVR_GetLastError() << endl;
}
vector<char>data(w * h);
bool capture = NET_DVR_CaptureJPEGPicture_NEW(stru->lUserId, struPlayInfo.lChannel, JpegPara, Jpeg, len, Ret);
if (!capture)
{
printf("Error: NET_DVR_CaptureJPEGPicture_NEW = %d", NET_DVR_GetLastError());
return -1;
}
for (int i = 0; i < w * h; i++)
data[i] = Jpeg[i];
if(stru->channel==1){
save_image_l = imdecode(Mat(data), 1);
}
if (stru->channel == 2) {
save_image_r = imdecode(Mat(data), 1);
}
return 0;
}
此时save_image_r 表示的就是一个Mat了,主要修改包括:
(1)LPNET_DVR_JPEGPARA
从这一部分开始,和sdk提供的预览方式有所区别,主要用NET_DVR_CaptureJPEGPicture_NEW替换了NET_DVR_RealPlay_V40函数
其中LPNET_DVR_JPEGPARA类提供了一些配置,包括图像大小,图像质量等。
完整的代码工程点击头像获取资源,可以直接部署使用,我这里相机有两个通道图,所以拿到了两个图。
[1] 海康威视SDK文档 https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10
[2]https://blog.csdn.net/u013832707/article/details/103405278
[3]https://blog.csdn.net/xiaojunjun200211/article/details/122237091
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。