海康威视sdk QT Opencv结合调试

手上有客户送的几个海康的摄像头,本来是做测试用的,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
海康威视sdk QT Opencv结合调试_第1张图片
解压到D盘根目录,并将文件夹改名成HCNetSDK,(为什么放D盘,因为我编程用的插件和库之类的都在D盘),然后把内部文件夹中文改成英文,比如头文件改成include,库文件夹改成lib(不知道厂家为什么用中文的目录),然后配置QT的pro文件。(简单粗暴的方式,不用像其他网友那样每一个文件和目录去输入,里面有opencv和vlc库的配置,最下面是海康sdk的配置,两行搞定)

海康威视sdk QT Opencv结合调试_第2张图片
开启调用采集用的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,下面是最后运行的效果。
海康威视sdk QT Opencv结合调试_第3张图片
最早版本录制的视频,https://www.bilibili.com/video/BV1sR4y1E7RY?spm_id_from=333.999.0.0
这次的就不录了,如果这篇文章对大家有帮助,感觉有参考价值,可以给个关注。

悟空学堂张老师,更多创意,更多交互分享

你可能感兴趣的:(opencv,qt,海康威视,qt,opencv,开发语言)