最近做的一个项目需要调用一个海康威视的摄像头,获取每一帧的数据。刚拿到手的时候有些不知所措,花了两三个小时看了下官方文档,下面分享一下代码
opencv+摄像头依赖
摄像头依赖可在海康威视官网找到,内容如下:
#头文件
DataType.h
DecodeCardSdk.h
HCNetSDK.h
plaympeg4.h
#动态/静态链接库
AnalyzeData.dll
AudioIntercom.dll
AudioRender.dll
D3DCompiler_43.dll
d3dx9_43.dll
EagleEyeRender.dll
GdiPlus.dll
GdiPlus.lib
HCAlarm.dll
HCAlarm.lib
HCCore.dll
HCCore.lib
HCCoreDevCfg.dll
HCDisplay.dll
HCGeneralCfgMgr.dll
HCGeneralCfgMgr.lib
HCIndustry.dll
HCNetSDK.dll
HCNetSDK.lib
HCPlayBack.dll
HCPreview.dll
HCPreview.lib
HCVoiceTalk.dll
HWDecode.dll
libiconv2.dll
libmmd.dll
MP_Render.dll
OpenAL32.dll
PlayCtrl.dll
PlayCtrl.lib
StreamTransClient.dll
SuperRender.dll
SystemTransform.dll
YUVProcess.dll
方案一:通过“抓拍”接口抓取每一帧(实际基本不能实时,大概最快一秒一帧)
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
#include
#include "Windows.h"
#include "HCNetSDK.h"
#include "plaympeg4.h"
using namespace std;
int main_old()
{
// 初始化设备
NET_DVR_Init();
//设置连接时间与重连时间
NET_DVR_SetConnectTime(2000, 3); //等待时间,重连次数
NET_DVR_SetReconnect(10000, true); //重连间隔,是否重连
//注册设备
NET_DVR_DEVICEINFO_V30 struDeviceInfo; //定义设备参数的结构体,NET_DVR_Login_V30中初始化
char ip[] = "192.168.0.64";
int port = 8000;
char user[] = "account";
char password[] = "password";
LONG lUserID = NET_DVR_Login_V30(ip, port,user,password, &struDeviceInfo); //注册设备,参数存储在struDeviceInfo中,返回用户ID>=0号
//注册失败
if (lUserID < 0)
{
printf("Login error, %d\n", NET_DVR_GetLastError());
NET_DVR_Cleanup();
return 0;
}
NET_DVR_JPEGPARA JpegPara;
JpegPara.wPicSize = 19; //1920*1080
JpegPara.wPicQuality = 0; //最好质量
const DWORD maxBuffer = 1000000;
char *jpeg = (char*)calloc(sizeof(char), maxBuffer);
DWORD size = 0;
//计算时间
for (int i = 0; i < 100; i++)
{
clock_t start = clock();
if (!NET_DVR_CaptureJPEGPicture_NEW(lUserID, 1, &JpegPara, jpeg, maxBuffer, &size))
{
std::cout << "error:" << NET_DVR_GetLastError() << std::endl;
continue;
}
clock_t end = clock();
std::cout << "帧花费时间:" << ((double)(end - start)) / CLOCKS_PER_SEC << "s" << endl;
}
//图片写入磁盘
if (_access("Pictures", 0) == -1)
{
int i = _mkdir("Pictures");
}
for (int i = 0; i < 1000; i++)
{
if (!NET_DVR_CaptureJPEGPicture_NEW(lUserID, 1, &JpegPara, jpeg, maxBuffer, &size))
{
std::cout << "error:" << NET_DVR_GetLastError() << std::endl;
continue;
}
std::cout <<"size of "<
方案二:通过实时回调获取每一帧(基本能做到实时)
#include
#include
#include
#include
#include
#include
#include "Windows.h"
#include "HCNetSDK.h"
#include "plaympeg4.h"
#include
#include
using namespace std;
using namespace cv;
//回调函数声明
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2); // 二级回调
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser); // 数据触发。(V40句柄,数据类型,缓冲区指针,缓冲区大小,用户数据)
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser); // 异常时触发
//void CALLBACK CBFun(long nPort, char *pBuf, long nSize, long nWidth, long nHeight, long nStamp, long nType, long nReceved);
clock_t startTime, endTime;
int main()
{
//初始化摄像头
NET_DVR_Init();
NET_DVR_SetConnectTime(2000, 1); //连接等待2s,重连1次
NET_DVR_SetReconnect(10000, true); //10秒重连,启动重连
//图片写入磁盘,测试使用
if (_access("Pictures_in_T_YV12", 0) == -1)
{
int i = _mkdir("Pictures_in_T_YV12");
}
//定义登录相关参数
char ip[] = "192.168.0.64";
int port = 8000;
char user[] = "account";
char password[] = "password";
NET_DVR_DEVICEINFO_V30 struDeviceInfo; //存储设备相关参数
//启动登录
LONG userID = NET_DVR_Login_V30(ip, port, user, password, &struDeviceInfo); //注册设备,参数存储在struDeviceInfo中,返回用户ID>=0号
//登录结果判断
if (userID < 0)
{
printf("登录摄像头出错, 错误代码:%d\n", NET_DVR_GetLastError());
NET_DVR_Cleanup();
system("pause");
return 0;
}
NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL); //设置异常回调
//定义预览参数
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = NULL; //不使用预览界面
struPlayInfo.lChannel = 1; //预览通道号 = 1
struPlayInfo.dwStreamType = 0; //0:主码流
struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
//设置实时回调
LONG handle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, fRealDataCallBack, NULL); //启动实时预览
//预览判断
if (handle < 0)
{
printf("实时预览调用出错,错误代码:%d\n", NET_DVR_GetLastError());
NET_DVR_Logout(userID);
NET_DVR_Cleanup();
system("pause");
return 0;
}
//waitKey();
Sleep(-1);
//相关释放
NET_DVR_StopRealPlay(handle); //关闭预览
NET_DVR_Logout(userID); //注销用户
NET_DVR_Cleanup(); //释放SDK资源
system("pause");
return 0;
}
//实时数据触发调用 (V40句柄,数据类型,缓冲区指针,缓冲区大小,用户数据)
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
static LONG nPort = -1; //实时预览通道号
switch (dwDataType)
{
case NET_DVR_SYSHEAD: //头部数据
if (!PlayM4_GetPort(&nPort)) //申请播放port,存为全局变量,供后面使用
{
break;
}
if (dwBufSize > 0) //数据长度>0
{
if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME)) //设置流处理模式 STREAME_REALTIME:尽力实时,不阻塞 STREAME_FILE:按照时间戳
{
break;
}
if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 100000)) //打开流接口,缓冲区设置为最大
{
break;
}
if (!PlayM4_Play(nPort, NULL)) //开始处理,不设置窗口句柄
{
break;
}
if (!PlayM4_SetDecCallBack(nPort, DecCBFun)) //设置自定义的解码回调函数 DecCBFun
{
break;
}
}
break;
case NET_DVR_STREAMDATA: //流数据
if (dwBufSize > 0 && nPort != -1)
{
if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
{
cout << "error" << PlayM4_GetLastError(nPort) << endl;
break;
}
}
break;
default: //其他数据
if (dwBufSize > 0 && nPort != -1)
{
if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
{
break;
}
}
break;
}
}
//实时解码回调
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
static int number = 0;
if (pFrameInfo->nType == T_YV12) //YV12:视频格式 PCM:音频
{
Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);
cvtColor(src, src, CV_YUV2BGR_YV12);
char name[100];
sprintf_s(name, "Pictures_in_T_YV12\\%d.jpg", number); //保存在磁盘为jpg
imwrite(name, src);
number++;
}
}
//异常触发
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
switch (dwType)
{
case EXCEPTION_RECONNECT: //预览时触发"重连"信号
printf("设备重新连接,当前时间为:%d\n", (int)time(NULL));
break;
default: //默认不作处理
break;
}
}