海康威视多摄像头视频实时采集——OpenCV显示

最近由于工程上要做多摄像头多目标跟踪,用到的摄像头是海康威视的DS-2CD3320D摄像头。

一、摄像头的配置

本人购买的摄像头的型号是DS-2CD3320D,采用以太网接口。为了实现多路采集视频,另外购置了4路以太网卡(淘宝购买)。

  将摄像头插入以太网口后,那么我们就可以通过官方提供的“设备网络搜索软件“——SADP工具,这个软件可以在海康威视的官方网站下载最新版本。本篇文章的很大程度上参照了lonelyrains的教程,在此基础上进行改进,同时在此表示感谢。下面主要补充一些lonelyrains没有提到的内容。

1、摄像头激活

  摄像头的激活需要用到官方的SADP工具,我第一次参照lonelyrains的教程去其他网站下载SADP工具并不是最新版本,因此没有激活功能。lonelyrains教程中的版本也确实没有激活功能,可能是海康后续提供的。

  

海康sdk java 抓图 海康威视抓图功能_#include


  

  摄像头第一次使用的时候,激活状态会显示未激活,这个时候在需要设置一个新密码以激活设备。这点主要是出于隐私和安全性考虑。详细的内容说明书会提到,这里就不赘述了。

2、摄像头配置

  前面步骤完成后就是配置摄像头的IP地址,我的本机网卡的IP地址配置保持不变。采集卡的IP设置见下图:

              

海康sdk java 抓图 海康威视抓图功能_IP_02


  IP地址和SADP配置的IP地址是一个子网即可。192.168.110.xxx

  

二、视频采集

  如果上述的步骤成功完成,那么下面就可以通过官方的DEMO采集摄像头的视频了。官方的SDK下载地址。官方的DEMO就在解压后的”库文件“文件夹中。采集效果见下图:

  

海康sdk java 抓图 海康威视抓图功能_IP_03

二、程序编写

  废话不多说,直接上程序。

HKCamDriver.cpp

/**************************************************************************************
 *  FileName   : HKCamDriver.cpp
 *  Discrption : HCNetSDK Camera Driver. You can simply using this class by the 
                 following way:

                 HKCamDriver m_HKCamDriver;
                 m_HKCamDriver.InitHKNetSDK();
                 m_HKCamDriver.InitCamera("xxx.xxx.xxx.xxx","usrname","psw");

                 This class support OpenCV. You can get IplImage form the CALLBACK
                 function DecCBFun.
                 But there remain have some problem need to be slove. 
                 1. Software decode the video steam need too much CPU resource.
                 2. When we convert the yv12 to RGB, There need too much CPU 
                    resource. If we use OpenCV, we can not avoid to convert the 
                    yv12 to RGB.
                 3. CALLBACK function will be call every time, and we also get the 
                    image data at the same time. There we have some multi-thread 
                    problem to be solve.

 *  Create     : 2017-7-10
 *  Author     : Li Zhan, UESTC(University of Electronic Science and Technology of 
                                China)
***************************************************************************************/

#include <windows.h>
#include "HKCamDriver.h"

HKCamDriver::HKCamDriver()
{
    /*  Create a mutex Lock when a object create */
}

HKCamDriver::~HKCamDriver(){
    ReleaseCamera();
}

int HKCamDriver::ReleaseCamera(void)
{
    if(!NET_DVR_StopRealPlay(lRealPlayHandle)){
        printf("NET_DVR_StopRealPlay error! Error number: %d\n",NET_DVR_GetLastError());
        return 0;
    }
    NET_DVR_Logout(lUserID);
    NET_DVR_Cleanup();
    return 1;
}

void HKCamDriver::InitHKNetSDK(void)
{
    /*  SDK Init                                 */
    NET_DVR_Init();
    /*  Set the Connect Time and Reconnect time  */
    NET_DVR_SetConnectTime(200, 1);
    NET_DVR_SetReconnect(10000, true);

    for(int i = 0; i < MaxCameraNum; i++){
        hMutex[i] = CreateMutex(NULL,FALSE,NULL);
        nPort[i] = -1;
        pImg[i] = NULL;
    }
    Scalefactor = 1.0f;

}

CamHandle HKCamDriver::InitCamera(char *sIP,char *UsrName,char *PsW,int Port)
{
    NET_DVR_DEVICEINFO_V30 struDeviceInfo;

    lUserID = NET_DVR_Login_V30(sIP, Port,UsrName,PsW, &struDeviceInfo);

    if (lUserID < 0){
        printf("Login error, %d\n", NET_DVR_GetLastError());
        NET_DVR_Cleanup();
        return -1;
    }

    NET_DVR_SetExceptionCallBack_V30(0, NULL,ExceptionCallBack, NULL);

    NET_DVR_CLIENTINFO ClientInfo;
    ClientInfo.lChannel = 1;                    /* Channel number Device channel number. */
    ClientInfo.hPlayWnd = NULL;                 /* Window is NULL                        */
    ClientInfo.lLinkMode = 0;                   /* Main Stream                           */
    ClientInfo.sMultiCastIP = NULL;

    lRealPlayHandle = NET_DVR_RealPlay_V30(lUserID,&ClientInfo,fRealDataCallBack,NULL,TRUE);

    if (lRealPlayHandle<0)return 0;

    return lRealPlayHandle;
}

void CALLBACK  HKCamDriver::DecCBFun(long nPort,char * pBuf,long nSize,FRAME_INFO * pFrameInfo, long nReserved1,long nReserved2)
{
    long lFrameType = pFrameInfo->nType;  
    char WindowName[15];
    static IplImage* pImgYCrCb[MaxCameraNum];
    sprintf_s(WindowName,"Windows:%d",nPort);

    if(lFrameType ==T_YV12)
    {

        WaitForSingleObject(hMutex[nPort],INFINITE);

        /*    Single Camera decode 3.5%    */
        if(pImgYCrCb[nPort] == NULL){
            pImgYCrCb[nPort] = cvCreateImage(cvSize(pFrameInfo->nWidth,pFrameInfo->nHeight), 8, 3);        
        }
        if(pImg[nPort]==NULL){
            pImg[nPort] = cvCreateImage(cvSize((int)(pFrameInfo->nWidth*Scalefactor),(int)(pFrameInfo->nHeight*Scalefactor)), 8, 3);  
        }

        /*    CPU: 0.1%                    */
        yv12toYUV(pImgYCrCb[nPort]->imageData, pBuf, pFrameInfo->nWidth,
                    pFrameInfo->nHeight,pImgYCrCb[nPort]->widthStep);  

        cvResize(pImgYCrCb[nPort],pImg[nPort], CV_INTER_LINEAR);

        /*    CPU  3.4%                    */
        cvCvtColor(pImg[nPort],pImg[nPort],CV_YCrCb2RGB); 

        /*   1080p Video Display Need 3.5%
             per Cmaera                    */
        ReleaseMutex(hMutex[nPort]);
    }
}

void CALLBACK  HKCamDriver::ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
    char tempbuf[256] = {0};
    switch(dwType) 
    {
    case EXCEPTION_RECONNECT:    /* Reconnet when Error Happen */
        break;
    default:
        break;
    }
}

void HKCamDriver::yv12toYUV(char *outYuv, char *inYv12, int width, int height,int widthStep)
{
    int col,row;
    unsigned int Y,U,V;
    int tmp;
    int idx;
    for (row=0; row<height; row++){
        idx=row * widthStep;
        int rowptr=row*width;
        for (col=0; col<width; col++){
            tmp = (row/2)*(width/2)+(col/2);
            Y=(unsigned int) inYv12[row*width+col];
            U=(unsigned int) inYv12[width*height+width*height/4+tmp];
            V=(unsigned int) inYv12[width*height+tmp];
            outYuv[idx+col*3]   = Y;
            outYuv[idx+col*3+1] = U;
            outYuv[idx+col*3+2] = V;
        }
    }
}
/*     Realtime Steam Callback           */
void CALLBACK HKCamDriver::fRealDataCallBack(LONG lRealHandle,DWORD dwDataType,BYTE *pBuffer,DWORD dwBufSize,void *pUser)
{
    DWORD dRet;
    DWORD CameraIndex = 0;

    CameraIndex  = lRealHandle;
    printf("lRealHandle = %ld\n",CameraIndex);
    switch (dwDataType)
    {
        /*  System Head    */
        case NET_DVR_SYSHEAD:    
            if (!PlayM4_GetPort(&nPort[CameraIndex]))break;
            if(dwBufSize > 0){
                if (!PlayM4_OpenStream(nPort[CameraIndex],pBuffer,dwBufSize,1024*1024)){
                    dRet=PlayM4_GetLastError(nPort[CameraIndex]);
                    break;
                }
                /* Setting the Decode function*/
                if (!PlayM4_SetDecCallBack(nPort[CameraIndex],DecCBFun)){
                    dRet=PlayM4_GetLastError(nPort[CameraIndex]);
                    break;
                }
                if (!PlayM4_Play(nPort[CameraIndex],NULL)){
                    dRet=PlayM4_GetLastError(nPort[CameraIndex]);
                    break;
                }    
            }
            break;
        /* Code steam data    */
        case NET_DVR_STREAMDATA:   
            if (dwBufSize > 0 && nPort[CameraIndex] != -1) {
                BOOL inData=PlayM4_InputData(nPort[CameraIndex],pBuffer,dwBufSize);
                while (!inData){
                    Sleep(10);
                    inData=PlayM4_InputData(nPort[CameraIndex],pBuffer,dwBufSize);
                }
            }
            break;  
        }       
}

int HKCamDriver::GetCamMat(Mat &Img,CamHandle handle,float factor)
{
    /*  Get the Port using handle    */
    int iPort = nPort[lRealPlayHandle];
    /*  Check the iPort is vaild     */
    if(iPort != -1){
        WaitForSingleObject(hMutex[iPort],INFINITE);
        Mat(pImg[iPort]).copyTo(Img);
        ReleaseMutex(hMutex[iPort]);
        resize(Img,Img,cv::Size(),factor,factor);
        return 1;
    }
    /* If iPort is invaild, return 
       empty                         */
    return 0;
}

void HKCamDriver::SetScaleFactor(float factor)
{
    Scalefactor = factor;
}

HKCamDriver.h

#ifndef _HKCAMDRIVER_H_
#define _HKCAMDRIVER_H_

#include "opencv2\opencv.hpp"                                  /*  OpenCV header                           */
#include "plaympeg4.h"                                         /*  Software decoder header                 */
#include "HCNetSDK.h"                                          /*  HCNet Camera SDK header                 */

using namespace cv;                                            /*  Need the OpenCV support                 */                

#define MaxCameraNum 20                                        /*  Support the max number of camera is 20  */

typedef long CamHandle;                                        /*  Camera Handle is long int               */

/*   Multi-Thread Lock                                     */
static HANDLE hMutex[MaxCameraNum];

static long nPort[MaxCameraNum];

static IplImage* pImg[MaxCameraNum];

static float Scalefactor;

class HKCamDriver{
public:

    /*    Constructed function                                 */
    HKCamDriver();

    ~HKCamDriver();

    /*   Init the HKNetSDK, and the function only be using by 
         once                                                  */
    void InitHKNetSDK(void);

    /*    Supply IP Address,UserName and Password,return the   *
          camera hanlde                                        */
    CamHandle InitCamera(char *sIP,char *UsrName,char *PsW,int Port = 8000);

    int ReleaseCamera(void);

    /*    Supply Camera handle, and function return the Mat    */
    int GetCamMat(Mat &Img,CamHandle handle = NULL,float factor = 1.0f);

    /*    Supply camera handle, and function return  IPlImage  */
    IplImage* GetCamImage(CamHandle handle,float factor = 1.0f);

    /*    Exception Callback function                          */
    static void CALLBACK ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser);  

    /*    Decode function, which convert yv12 to rgb           */
    static void CALLBACK DecCBFun(long nPort,char * pBuf,long nSize,FRAME_INFO * pFrameInfo, long nReserved1,long nReserved2);

    /*   Realtime decode function,which call mpeg4 to decode   */
    static void CALLBACK fRealDataCallBack(LONG lRealHandle,DWORD dwDataType,BYTE *pBuffer,DWORD dwBufSize,void *pUser);

    static void SetScaleFactor(float factor);

private:
    /*   Convert the video format yv12 to YUV format           */
    static void yv12toYUV(char *outYuv, char *inYv12, int width, int height,int widthStep);
    /*   Realtime Play handle                                  */
    LONG lRealPlayHandle;
    /*   Camera User ID                                        */
    LONG lUserID;
};
#endif

main.cpp

clude <opencv\cv.h>
#include <opencv\highgui.h>
#include <opencv2\opencv.hpp>
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>    
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/ml/ml.hpp>  
#include <windows.h>
#include "HKCamDriver.h"
#include <string>    

using namespace std;    
using namespace cv;    

HKCamDriver m_CamDriver[2];

int main()
{   
    m_CamDriver[0].InitHKNetSDK();
    m_CamDriver[0].SetScaleFactor(0.5f);

    Sleep(500);
    m_CamDriver[0].InitCamera("192.168.110.65","admin","DS-2CD3320D");

    Sleep(200);  

    m_CamDriver[1].InitCamera("192.168.110.64","admin","DS-2CD3320D");

    Mat video[2];

    /* Wait */
    Sleep(1000); 

    while(1)
    {
        if(m_CamDriver[0].GetCamMat(video[0],NULL,1.0f)){
            imshow("windows::im0",video[0]);
        if(m_CamDriver[1].GetCamMat(video[1],NULL,1.0f)){
            imshow("windows::im1",video[1]);
        }
        waitKey(30);
    }

    return 0;
}

运行效果

海康sdk java 抓图 海康威视抓图功能_OpenCV_04