手上有客户送的几个海康的摄像头,本来是做测试用的,2年前利用海康的sdk调试过,并结合opencv做了一些视觉处理方面的应用,后面因为要兼容其他的ip摄像头和onvif协议的通用性,最后使用了libvlc库,把原来的代码压缩备份后,海康的sdk和源码就删除了。
后面有其他客户做一个七通道的交互采集,发现海康的延迟越来越大,电脑cpu占用也非常高,实际上海康的延迟网上一直有讨论,有人说可以做到几百ms之内,对于实时采集来说,几百ms的延迟也不低,好多方法也不能根本的解决,后面客户问有没有优化的空间,然后就想到重新把海康的sdk拉出来测试,才发现悲剧了,之前备份的源码找不到了,只有重新造车轮子或者参考(拷贝加整理)别人的代码了。
QT的环境配置与opencv的配置这里就不说了,毕竟很多看到这篇文章的都已经配置好了,主要是来参考海康sdk调用方面资源的。海康官网很久没有上去,发现官网改版了,sdk需要注册后才能下载,下载地址变更为下面的地址;https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10
环境是64位,就下载win 64位的sdk
解压到D盘根目录,并将文件夹改名成HCNetSDK,(为什么放D盘,因为我编程用的插件和库之类的都在D盘),然后把内部文件夹中文改成英文,比如头文件改成include,库文件夹改成lib(不知道厂家为什么用中文的目录),然后配置QT的pro文件。(简单粗暴的方式,不用像其他网友那样每一个文件和目录去输入,里面有opencv和vlc库的配置,最下面是海康sdk的配置,两行搞定)
开启调用采集用的h文件和cpp文件(如果是自己新建,可以在QT Creator项目右键添加新的文件,然后选择c++的class文件,生成一个h和cpp文件就可以)。首先在h头文件中添加海康sdk的头文件
#include
#include
然后添加两个变量作为全局使用,然后还有2个函数,处理海康摄像头的启动和关闭,放入private内和public内都可以,随你意。
LONG handle;
LONG userID;
bool InitialCameraMan(QString hkip, QString username,QString pwd);
void StopCamera();
打开cpp文件,输入如下的代码(拷贝粘贴,调整,查找QString转char*的方法,查找独占锁的应用以及数据的引用)
HANDLE MainVectorhMutex;
std::vector MainImageVector;
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);
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser);
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)
{
if (pFrameInfo->nType == T_YV12) //YV12:视频格式 PCM:音频
{
cv::Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);
cvtColor(src, src, CV_YUV2BGR_YV12);
cv::resize(src,src,cv::Size(640,480));
WaitForSingleObject(MainVectorhMutex,INFINITE);
MainImageVector.push_back(src);
if(MainImageVector.size()>5){
MainImageVector.erase(MainImageVector.begin());
}
ReleaseMutex(MainVectorhMutex);
//cv::imshow("yv12mat",src);
//cv::waitKey(1);
}
}
//异常触发
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
qDebug() << "exception callback," << lUserID << lHandle;
switch (dwType)
{
case EXCEPTION_RECONNECT: //预览时触发"重连"信号
qDebug("设备重新连接,当前时间为:%d\n", (int)time(NULL));
break;
default: //默认不作处理
break;
}
}
x1t_lib::x1t_lib(QObject *parent) : QObject(parent)
{
fourdian = false;
ptrMOG2 = cv::createBackgroundSubtractorMOG2();
structElement = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
}
bool x1t_lib::InitialCameraMan(QString hkip, QString username, QString pwd){
//初始化摄像头
NET_DVR_Init();
NET_DVR_SetConnectTime(2000, 1);
NET_DVR_SetReconnect(10000, true);
//定义登录相关参数
qDebug() << "initcam " << hkip << username << pwd;
NET_DVR_DEVICEINFO_V30 struDeviceInfo;
userID = NET_DVR_Login_V30(hkip.toLatin1().data(), 8000, username.toLatin1().data(), pwd.toLatin1().data(), &struDeviceInfo);
if (userID < 0)
{
qDebug("登录摄像头出错, 错误代码:%d\n", (int)NET_DVR_GetLastError());
NET_DVR_Cleanup();
return false;
}
NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);
//定义预览参数
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = NULL;
struPlayInfo.lChannel = 1;
struPlayInfo.dwStreamType = 0;
struPlayInfo.dwLinkMode = 0;//0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
//设置实时回调
handle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, fRealDataCallBack, NULL);
if (handle < 0)
{
qDebug("实时预览调用出错,错误代码:%d\n", (int)NET_DVR_GetLastError());
NET_DVR_Logout(userID);
NET_DVR_Cleanup();
return false;
}
return true;
}
void x1t_lib::StopCamera(){
//相关释放
NET_DVR_StopRealPlay(handle); //关闭预览
NET_DVR_Logout(userID); //注销用户
NET_DVR_Cleanup(); //释放SDK资源
}
到这个地方的时候,海康摄像头有关的代码就全部设置完成了,最后调用然后进行opencv处理就妥了。
调用的方式如下:
QString ipadr = "192.168.1.37";
QString ipad = "admin";
QString ipmm = "hk123456";
//qDebug() << "hksdk init " << ipadr << ipad << ipmm;
bool hkok = InitialCameraMan(ipadr,ipad,ipmm);
while(hkok){
while (MainImageVector.size()<=0) {
Sleep(20);
continue;
}
WaitForSingleObject(MainVectorhMutex,INFINITE);
cv::Mat getimg;
if(MainImageVector.size()>0){
getimg = MainImageVector.front();
//getimg = *(MainImageVector.begin());
}else{
continue;
}
MainImageVector.erase(MainImageVector.begin());
ReleaseMutex(MainVectorhMutex);
getImage(getimg);
}
其中的getImage(getimg)是我进行opencv处理图像的函数,结构为void getImage(cv::Mat src);至于用opencv找轮廓,还是帧差,还是YOLO,还是其他视觉处理,这个都是大家熟悉的,我就不展开了。
这个时候你也很兴奋,毕竟该参考的都参考完了,运行,报错,最比较郁闷的是NET_DVR_Login_V30调用报错,错误代码是29,查资料说是NET_DVR_DVROPRATEFAILED 29 设备操作失败,设备操作失败什么鬼,然后无意间看到官方sdk内的说明文件和百度贴吧里面一个求助帖子,说是dll文件缺失,补全就可以了运行,官网提示中涉及到的dll说明如下:
HCNetSDK.dll、HCCore.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll、hlog.dll、hpr.dll、zlib1.dll、HCNetSDKCom文件夹(以上文件在sdk里bin内都能找到)、ssleay32.dll、libeay32.dll(这两个文件要自己找)、log4cxx.properties(这个没有用到)等文件均要加载到程序里面,【HCNetSDKCom文件夹】(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。
所有dll文件补全,(这里注意ssleay32.dll、libeay32.dll的版本,我找了好几个都不能用,然后在qt安装文件夹里pyqt内搜到了,放入后才能用)然后运行ok,下面是最后运行的效果。
最早版本录制的视频,https://www.bilibili.com/video/BV1sR4y1E7RY?spm_id_from=333.999.0.0
这次的就不录了,如果这篇文章对大家有帮助,感觉有参考价值,可以给个关注。
悟空学堂张老师,更多创意,更多交互分享