天天看點

海康威視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結合調試

解壓到D盤根目錄,并将檔案夾改名成HCNetSDK,(為什麼放D盤,因為我程式設計用的插件和庫之類的都在D盤),然後把内部檔案夾中文改成英文,比如頭檔案改成include,庫檔案夾改成lib(不知道廠家為什麼用中文的目錄),然後配置QT的pro檔案。(簡單粗暴的方式,不用像其他網友那樣每一個檔案和目錄去輸入,裡面有opencv和vlc庫的配置,最下面是海康sdk的配置,兩行搞定)

海康威視sdk QT Opencv結合調試

開啟調用采集用的h檔案和cpp檔案(如果是自己建立,可以在QT Creator項目右鍵添加新的檔案,然後選擇c++的class檔案,生成一個h和cpp檔案就可以)。首先在h頭檔案中添加海康sdk的頭檔案

#include <HCNetSDK.h>
#include <plaympeg4.h>
           

然後添加兩個變量作為全局使用,然後還有2個函數,處理海康攝像頭的啟動和關閉,放入private内和public内都可以,随你意。

LONG handle;
LONG userID;
           
bool InitialCameraMan(QString hkip, QString username,QString pwd);
void StopCamera();
           

打開cpp檔案,輸入如下的代碼(拷貝粘貼,調整,查找QString轉char*的方法,查找獨占鎖的應用以及資料的引用)

HANDLE MainVectorhMutex;
std::vector<cv::Mat> 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結合調試

最早版本錄制的視訊,https://www.bilibili.com/video/BV1sR4y1E7RY?spm_id_from=333.999.0.0

這次的就不錄了,如果這篇文章對大家有幫助,感覺有參考價值,可以給個關注。

悟空學堂張老師,更多創意,更多互動分享