很久没有做工程项目了,最近突然接单,要做多摄像头下运动物体入侵检测及拌线检测,本文测试用到的摄像头是海康威视的DS-2CD23**D摄像头。
1.下载SDK
2.vs2010开发环境
3..opencv2.4.8
具体配置自行百度吧。下面贴上我这边的主要配置,需要注意的是需要在x64环境下编译。
将摄像头插入以太网口后,那么我们就可以通过官方提供的“设备网络搜索软件“——SADP工具,这个软件可以在海康威视的官方网站下载最新版本。本篇文章的很大程度上参照了lonelyrains的教程,在此基础上进行改进,同时在此表示感谢。
1.先在官网下载到SDK开发包:
下载下来SDK后我们解压,就可以看到里面包含一些开发文档以及一些Demo示例:
2.VS2010下环境配置
首先,配置属性---常规
1)输出目录:$(SolutionDir)\bin\
2.)中间目录:$(SolutionDir)\Temp\Compile\$(ProjectName)\$(ConfigurationName)
接着,配置属性---调试
VC++目录
包含目录:
..\include
opencv目录也要包含进来,继承值里如果没有的话
库目录:
接着C/C++
常规--附加包含目录 ../include/
链接器--常规 --附加库目录 ../lib/
链接器--输入--附加依赖项
HCNetSDK.lib
ws2_32.lib
PlayCtrl.lib
winmm.lib
GdiPlus.lib
IPHlpApi.Lib
3.opencv配置,参见这里
下面进入重点,实时视频采集。
先看看SDK里的demo函数,参见这里
代码如下:
NesunCamDriver.h
添加了进程锁,由于考虑到项目保密性,下面开放部分源码。
.cpp源码开放出来:
#include "StdAfx.h"
#include "NesunCamDriver.h"
#include
IplImage * NesunCamDriver::m_pImg = NULL;
long NesunCamDriver::nPort = -1;
float NesunCamDriver::Scalefactor = 1.0f;
MTCMutex NesunCamDriver::m_hMutex;
HWND NesunCamDriver::m_hPlayWnd=NULL;
NesunCamDriver::NesunCamDriver(void)
: lUserID(0)
, lRealPlayHandle(-1)
{
/* Create a mutex Lock when a object create */
//m_pImg = NULL;
//hMutex = CreateMutex(NULL,FALSE,NULL);
}
NesunCamDriver::~NesunCamDriver(void)
{
ReleaseCamera();
}
int NesunCamDriver::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();
cvReleaseImage(&m_pImg);//gavin++
if (m_pImg)
{
m_pImg = NULL;
}
return 1;
}
void NesunCamDriver::InitHKNetSDK(void)
{
/* SDK Init */
NET_DVR_Init();
/* Set the Connect Time and Reconnect time */
NET_DVR_SetConnectTime(200, 1);
NET_DVR_SetReconnect(10000, true);
}
CamHandle NesunCamDriver::InitCamera(char *sIP,char *UsrName,char *PsW, HWND hPlayWnd,int Port)
{
m_hPlayWnd = hPlayWnd;
NET_DVR_DEVICEINFO_V30 struDeviceInfo;
memset(&struDeviceInfo,0,sizeof(NET_DVR_DEVICEINFO_V30));
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; //NULL; 窗口为空,设备SDK不解码只取流
ClientInfo.lLinkMode = 0; /* Main Stream */
ClientInfo.sMultiCastIP = NULL;
lRealPlayHandle = NET_DVR_RealPlay_V30(lUserID,&ClientInfo,fRealDataCallBack,NULL,TRUE);
if(-1 == lRealPlayHandle)
{
DWORD err=NET_DVR_GetLastError();
printf("NET_DVR_RealPlay_V30 failed! Error number: %d\n", err);
CString sErr;
sErr.Format("播放出错,错误代码:%d",err);
AfxMessageBox(sErr);
return 0;
}
return lRealPlayHandle;
}
void CALLBACK NesunCamDriver::DecCBFun(long nPort,char * pBuf,long nSize,FRAME_INFO * pFrameInfo, long nReserved1,long nReserved2)
{
long lFrameType = pFrameInfo->nType;
char WindowName[15];
static IplImage* pImgYCrCb =NULL;
sprintf_s(WindowName,"Windows:%d",nPort);
if(lFrameType ==T_YV12)
{
m_hMutex.Lock();
/* Single Camera decode 3.5% */
if(pImgYCrCb == NULL)
{
pImgYCrCb = cvCreateImage(cvSize(pFrameInfo->nWidth,pFrameInfo->nHeight), 8, 3);
}
if (m_pImg == NULL)
{
m_pImg = cvCreateImage(cvSize((int)(pFrameInfo->nWidth*Scalefactor),(int)(pFrameInfo->nHeight*Scalefactor)), 8, 3);
}
/* CPU: 0.1% */
yv12toYUV(pImgYCrCb->imageData, pBuf, pFrameInfo->nWidth,
pFrameInfo->nHeight,pImgYCrCb->widthStep);
cvResize(pImgYCrCb,m_pImg, CV_INTER_LINEAR);
/* CPU 3.4% */
cvCvtColor(m_pImg,m_pImg,CV_YCrCb2RGB);
/* 1080p Video Display Need 3.5%
per Cmaera */
m_hMutex.UnLock();
cvReleaseImage(&pImgYCrCb);//gavin++
}
}
void CALLBACK NesunCamDriver::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 NesunCamDriver::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 0)
{
if (!PlayM4_OpenStream(nPort,pBuffer,dwBufSize,1024*1024))
{
dRet=PlayM4_GetLastError(nPort);
break;
}
//fix by gavin
//设置解码回调函数 只解码不显示
// if (!PlayM4_SetDecCallBack(nPort,DecCBFun))
// {
// dRet=PlayM4_GetLastError(nPort);
// break;
// }
//设置解码回调函数 解码且显示
if (!PlayM4_SetDecCallBackEx(nPort,DecCBFun,NULL,NULL))
{
dRet=PlayM4_GetLastError(nPort);
break;
}
/*
if (!PlayM4_Play(nPort,NULL))
{
dRet=PlayM4_GetLastError(nPort);
break;
} */
//打开视频解码
if (!PlayM4_Play(nPort,m_hPlayWnd))
{
dRet=PlayM4_GetLastError(nPort);
break;
}
//打开音频解码, 需要码流是复合流
if (!PlayM4_PlaySound(nPort))
{
dRet=PlayM4_GetLastError(nPort);
break;
}
break;
}
break;
/* Code steam data */
case NET_DVR_STREAMDATA:
if (dwBufSize > 0 && nPort != -1)
{
BOOL inData=PlayM4_InputData(nPort,pBuffer,dwBufSize);
while (!inData)
{
Sleep(10);
inData=PlayM4_InputData(nPort,pBuffer,dwBufSize);
}
}
break;
}
}
int NesunCamDriver::GetCamMat(Mat &Img,CamHandle handle,float factor)
{
/* Get the Port using handle */
int iPort = nPort;
/* Check the iPort is vaild */
if(iPort != -1)
{
//WaitForSingleObject(hMutex[iPort],INFINITE);
m_hMutex.Lock();
Mat(m_pImg).copyTo(Img);
//ReleaseMutex(hMutex[iPort]);
cvReleaseImage(&m_pImg);//gavin++
m_pImg = NULL;//gavin++
m_hMutex.UnLock();
resize(Img,Img,cv::Size(),factor,factor);
return 1;
}
/* If iPort is invaild, return
empty */
return 0;
}
void NesunCamDriver::SetScaleFactor(float factor)
{
Scalefactor = factor;
}
// 抓图保存到指定路径
int NesunCamDriver::CaptureImg(char* szPath,char* szError)
{
FILE *file=NULL;
static long iPicNum = 0;
NET_DVR_JPEGPARA JpegPara;
JpegPara.wPicQuality=0;
JpegPara.wPicSize=0xff;
char *JpegPicBuffer= new char[352*288*2];
//这里的缓冲区大小需要根据抓图的分辨率大小自己调节,建议设置成2*图片的分辨率宽*图片的分辨率高
LONG iPChannel = 1;//Channel number 设备通道号
DWORD SizeReturned=0;
BOOL bRet= NET_DVR_CaptureJPEGPicture_NEW(lUserID, iPChannel,&JpegPara,JpegPicBuffer,352*288*2,&SizeReturned);
if (!bRet)
{
sprintf(szError, "NET_DVR_CaptureJPEGPicture_NEW failed! Error number: %d\n", NET_DVR_GetLastError());
return 0;
}
if (file==NULL)
{
sprintf(szPath,"%s\\JpegCAP%d_%d.jpg",szPath,lUserID,iPicNum);
file = fopen(szPath,"wb");
}
fwrite(JpegPicBuffer,SizeReturned,1,file);
iPicNum++;
delete JpegPicBuffer;
fclose(file);
file=NULL;
return 1;
}
调用:
CString strMode;
GetDlgItemText(IDC_BT_PLAY, strMode);
if (_T("播放") == strMode)
{
m_hkCamDriver[0].InitHKNetSDK();
m_hkCamDriver[0].SetScaleFactor(0.5f);
Sleep(500);
if (m_hkHandle[0] >= 0)
{
NET_DVR_StopRealPlay(m_hkHandle[0]);
m_hkHandle[0] = -1;
}
//m_lPlayHandle = NET_DVR_RealPlay_V30(m_lUserID, &struPlayInfo, NULL, NULL, TRUE);
m_hkHandle[0] = m_hkCamDriver[0].InitCamera("192.168.1.222","admin","12345",m_hPlayWnd[0]);
if (m_hkHandle[0] < 0)
{
char szTemp[128] = {0};
sprintf(szTemp, "视频播放出错, %s", NET_DVR_GetErrorMsg());
AfxMessageBox(szTemp);
}
GetDlgItem(IDC_BT_PLAY)->SetWindowText("停止");
}
else if (_T("停止") == strMode)
{
m_hkCamDriver[0].ReleaseCamera();
GetDlgItem(IDC_BT_PLAY)->SetWindowText("播放");
}
注意对应窗口显示区域,HWND m_hPlayWnd[12];
m_hPlayWnd[0] = GetDlgItem(IDC_STATIC_PLAY)->GetSafeHwnd();
其他参考:
1.https://blog.csdn.net/log_zhan/article/details/75041352#commentsedit
2.https://blog.csdn.net/qq_15029743/article/details/79733960
3.OpenCV笔记(2)打开海康威视摄像头
4.海康摄像头实时显示与字符叠加详解
5.海康威视IP摄像头基于OPENCV的二次开发
6.ROS:海康威视+opencv运动检测
7.捕获海康威视IPCamera图像,转成OpenCV可以处理的图像(二)