最近玩了一下海康的IPcamera,与普通的WEBcamera输出RGB图像不同,其解码后输出的图像格式为YV12,这使得我们不能用熟悉的两行代码

VideoCapture cap(0); cap << frame;

就获得可以用opencv直接处理的源图像了,这一度造成了不少麻烦。

所以贴出部分代码,希望对正在使用或者也想玩下海康摄像头的朋友一点参考。

能力有限,若有误,勿喷!

转YV12到YCrCb的函数实现有很多,代码不知谁写的,我是从雪水的这篇博客(超链接)看到的,亲测可以实现。

  1. void yv12toYUV(char *outYuv, char *inYv12, int width, int height,int widthStep)
  2. {
  3. int col,row;
  4. unsigned int Y,U,V;
  5. int tmp;
  6. int idx;
  7. for (row=0; row<height; row++)
  8. {
  9. idx=row * widthStep;
  10. int rowptr=row*width;
  11. for (col=0; col<width; col++)
  12. {
  13. tmp = (row/2)*(width/2)+(col/2);
  14. Y=(unsigned int) inYv12[row*width+col];
  15. U=(unsigned int) inYv12[width*height+width*height/4+tmp];
  16. V=(unsigned int) inYv12[width*height+tmp];
  17. if((idx+col*3+2)> (1200 * widthStep))
  18. {
  19. //printf("row * widthStep=%d,idx+col*3+2=%d.\n",1200 * widthStep,idx+col*3+2);
  20. }
  21. outYuv[idx+col*3]   = Y;
  22. outYuv[idx+col*3+1] = U;
  23. outYuv[idx+col*3+2] = V;
  24. }
  25. }
  26. }

本文推荐用cvtColor()函数,opencv2.4.8以上版本自定义了宏CV_YUV2BGR_YV12,可以实现这一转换;

以下是主函数: /*----------------------------异常消息回调函数----------------------------*/ void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser) { char tempbuf[256] = {0}; switch(dwType) { case EXCEPTION_RECONNECT: //预览时重连 printf("----------reconnect--------%d\n", time(NULL)); break; default: break; } } void main() { //--------------------------------------- // 初始化 NET_DVR_Init(); //设置连接时间与重连时间 NET_DVR_SetConnectTime(2000, 1); NET_DVR_SetReconnect(10000, true); //--------------------------------------- // 获取控制台窗口句柄 //HMODULE hKernel32 = GetModuleHandle((LPCWSTR)"kernel32"); //GetConsoleWindow = (PROCGETCONSOLEWINDOW)GetProcAddress(hKernel32,"GetConsoleWindow"); //--------------------------------------- // 注册设备 LONG lUserID; NET_DVR_DEVICEINFO_V30 struDeviceInfo; lUserID = NET_DVR_Login_V30("10.102.7.88", 8000, "admin", "12345", &struDeviceInfo); if (lUserID < 0) { printf("Login error, %d\n", NET_DVR_GetLastError()); NET_DVR_Cleanup(); return; } //--------------------------------------- //设置异常消息回调函数 NET_DVR_SetExceptionCallBack_V30(0, NULL,g_ExceptionCallBack, NULL); /*NET_DVR_RealPlay_V30参数设置NET_DVR_CLIENTINFO ClientInfo;ClientInfo.hPlayWnd = NULL;//改为“= GetDlgItem(IDC_STATIC_PLAY)->m_hWnd” ClientInfo.lChannel = 1;ClientInfo.lLinkMode = 0;ClientInfo.sMultiCastIP = NULL;TRACE("Channel number:%d\n",ClientInfo.lChannel);*///NET_DVR_RealPlay_V40参数设置NET_DVR_PREVIEWINFO struPlayInfo = {0};struPlayInfo.hPlayWnd = NULL; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空struPlayInfo.lChannel = 1; //预览通道号struPlayInfo.dwStreamType = 0; //0-主码流,1-子码流,2-码流3,3-码流4,以此类推struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTPLONG lRealPlayHandle; //lRealPlayHandle = NET_DVR_RealPlay_V30(lLoginID,&ClientInfo,fRealDataCallBack,NULL,false);lRealPlayHandle = NET_DVR_RealPlay_V40(lLoginID,&struPlayInfo,fRealDataCallBack,NULL);if (lRealPlayHandle<0) { printf("NET_DVR_RealPlay_V30 failed! Error number: %d\n",NET_DVR_GetLastError()); return; } //cvWaitKey(0); Sleep(-1); //fclose(fp); //--------------------------------------- //关闭预览 if(!NET_DVR_StopRealPlay(lRealPlayHandle)) { printf("NET_DVR_StopRealPlay error! Error number: %d\n",NET_DVR_GetLastError()); return; } //注销用户 NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return; } 以下是实时流回调函数fRealDataCallBack:/********************************************实时流回调注:获得的是数据流,仍需在DecCBFun中解码**********************************************/ void CALLBACK fRealDataCallBack(LONG lRealHandle,DWORD dwDataType,BYTE *pBuffer,DWORD dwBufSize,void *pUser) { //TRACE("fRealDataCallBack 函数被调用\n");//TRACE("dwDataType: %ld\n",dwDataType);DWORD dRet; switch (dwDataType) { case NET_DVR_SYSHEAD: //系统头//TRACE("系统头被执行\n");if (!PlayM4_GetPort(&nPort)) //获取播放库未使用的通道号 { break; } if(dwBufSize > 0) { if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME)) //设置实时流播放模式{break;}if (!PlayM4_OpenStream(nPort,pBuffer,dwBufSize,10*1024*1024)) { dRet=PlayM4_GetLastError(nPort); break; }PlayM4_SetDecCBStream(nPort,1);//只解码视频流,不解音频流//设置解码回调函数 只解码不显示 if (!PlayM4_SetDecCallBack(nPort,DecCBFun)) { dRet=PlayM4_GetLastError(nPort); break; } //打开视频解码 if (!PlayM4_Play(nPort,NULL)) { dRet=PlayM4_GetLastError(nPort); break; } //打开音频解码, 需要码流是复合流 //if (!PlayM4_PlaySound(nPort)) //{ // dRet=PlayM4_GetLastError(nPort); // break; //} } break; case NET_DVR_STREAMDATA: //码流数据//TRACE("码流数据被执行\n");if (dwBufSize > 0 && nPort != -1) { BOOL inData = PlayM4_InputData(nPort,pBuffer,dwBufSize); while (!inData) { Sleep(10); inData = PlayM4_InputData(nPort,pBuffer,dwBufSize);} } break; } } 以下是解码回调函数,在解码回调之前需声明一个List容器,用以存放帧数据,该List最好定义为全局变量,便于在线程里opencv处理。 /*---------------------------------解码回调函数,帧数据格式转化,存储---------------------------------*/ void CALLBACK DecCBFun(long nPort,char * pBuf,long nSize,FRAME_INFO * pFrameInfo, long nReserved1,long nReserved2) { //TRACE("DecCBFun 函数被调用\n");long lFrameType = pFrameInfo->nType;//TRACE(" lFrameType: %ld\n", lFrameType);if (lFrameType == T_YV12){Mat pImg(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);Mat pImg_YUV(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);Mat pImg_YCrCb(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);cvtColor(pImg_YUV, pImg, CV_YUV2BGR_YV12);cvtColor(pImg,pImg_YCrCb,CV_BGR2YCrCb);// Sleep(-1);imshow("IPCamera",pImg);//waitKey(1);//IplImage *pImg1 = &IplImage(pImg);if(!IsTracking){hEvent = CreateEvent(NULL,false,true,NULL);//InitializeCriticalSection(&cs_frameQueue);}//HANDLE hThread = CreateThread(NULL, 0, dealFun, NULL, 0, NULL);//CloseHandle(hThread); //图片存储//*--------回调函数当做存储视频帧线程-----------//ResetEvent(hEvent);//EnterCriticalSection(&cs_frameQueue);realframe_count++;//TRACE("实时帧数: %d\n",realframe_count);if(0 == realframe_count%10){ WaitForSingleObject(hEvent,INFINITE);frameQueue.push_back(pImg_YCrCb);if(!IsTracking){frameQueue.clear();}SetEvent(hEvent);}//LeaveCriticalSection(&cs_frameQueue);} } 此处利用解码回调函数做视频格式转换操作,并将RGB图像存入frameQueue。利用多线程编程,在另一个线程里用opencv处理图像或其他操作,我这里是进行跟踪的,其中,实时帧数realframe_count是用来控制取帧间隔时间的,因为opencv图像处理线程会运行相应复杂算法或者别的操作,时间消耗较大,不可能逐一处理每一帧。线程通过一个按钮开启,响应函数如下,由于贴的使部分代码,请自动忽略无关内容! void CRealPlayDlg::OnBnClickedButtonTracking() {// TODO: 在此添加控件通知处理程序代码cvNamedWindow("frame",0);cvResizeWindow("frame",400,300);cvMoveWindow("frame",420,0);cvNamedWindow("result",0);cvResizeWindow("result",400,300);cvMoveWindow("result",840,0);IsTracking = true;setMouseCallback( "frame", onMouse, 0 );//消息响应机制if(IsTracking){ // HANDLE hThread1; // HANDLE hThread2; // hEvent = CreateEvent(NULL,false,true,NULL); // hThread1 = CreateThread(NULL, 0, storeFun, NULL, 0, NULL);//InitializeCriticalSection(&cs_frameQueue);hThread = CreateThread(NULL, 0, dealFun, NULL, 0, NULL);// hMutex = CreateMutex(NULL, FALSE, "imgData"); // CloseHandle(hThread1);CloseHandle(hThread);} 以下为取帧线程函数: /*----------------------------------图像处理 目标跟踪 线程---------------------------------*/ DWORD WINAPI dealFun(LPVOID lpParamter) {while (1){if (!frameQueue.empty()){WaitForSingleObject(hEvent, INFINITE);//src = (Mat)(*(frameQueue.begin()));//frameQueue.front();src_YCrCb = (Mat)(*(frameQueue.begin()));cvtColor(src_YCrCb, src, CV_YCrCb2BGR);frameQueue.pop_front();SetEvent(hEvent);//your code............}}return 0; } 到这一步就可以了,另外,在函数中是不需要waitkey来控制帧率的,海康的SDK好像是自动回调的!

海康威视IP摄像头基于OPENCV的二次开发相关推荐

  1. 海康威视多摄像头实时视频预览(基于SDK的二次开发)

    项目背景 很久没有做工程项目了,最近突然接单,要做多摄像头下运动物体入侵检测及拌线检测,本文测试用到的摄像头是海康威视的DS-2CD23**D摄像头. 环境配置: 1.下载SDK 2.vs2010开发 ...

  2. 基于OpenCV实现二维码发现与定位

    基于OpenCV实现二维码发现与定位 在如今流行扫描的年代,应用程序实现二维码扫描检测与识别已经是应用程序的标配.特别是在移动端.如果你的应用程序不能自动发现检测二维码,自动定位二维码你都不好意思跟别 ...

  3. 如何使用方位X210来查看海康威视IP摄像头

    文章来源:https://3x.58voip.com/fanvil-x210-hikvision/ 介绍: X210是一款高端商务IP话机,适用于需要灵活性和高级功能的用户,以便在工作中轻松应对繁 X ...

  4. 海康大华天地伟业网络摄像头chrome浏览器web二次开发

    海康大华天地伟业网络摄像头chrome浏览器二次开发 海康大华天地伟业网络摄像头chrome浏览器web二次开发 由于工作的原因需要开发海康和大华,还有天地伟业的摄像头,而且必须是本地部署开发,每个厂 ...

  5. office 文件在线协作编辑——解决方案1(基于sharepoint的二次开发)

    概述 office 文件在线协作编辑主要是指word.excel.ppt的多人在线协作编辑,实时同步的功能: 这里主要介绍解决方案之一--基于sharepoint的二次开发(还可以基于wopi实现)的 ...

  6. 据说这是熟练掌握python的爷们_dongbei 是一门基于 Python 3 二次开发的东北方言编程语言...

    dongbei - 东北方言编程语言 学编程,就整东北浪! 体格咋地 扫码关注原作者微信公众号"老万故事会": 引言 dongbei是啥?它是一门以东北方言词汇为基本关键字的以人为 ...

  7. java二次开发浏览器内核_深入理解基于Selenium的二次开发

    对于做web端自动化测试的人来说,可能接触selenium比QTP还要多,但是我们在做基于selenium的二次开发的时候,经常会说到二次开发是 为了易于维护,很多人可能不懂得维护的价值是什么,和到底 ...

  8. Vue+Vant 基于DatetimePicker进行二次开发,实现yyyyMMdd hh:mm:ss时间选择

    Vue+Vant 基于DatetimePicker进行二次开发,实现yyyyMMdd hh:mm:ss时间选择 1.效果图 2.前提 3.项目结构 4.index.vue 5.timeSelectio ...

  9. 基于Visio的二次开发

    基于Visio的二次开发 前一段时间,由于项目的需要:学习了一些关于Visio二次开发的知识:现在工具基本成形了,也算告一段落了:因此想总结一下关于Visio的二次开发的一些基本知识: 对于基于Vis ...

最新文章

  1. idea中链接mysql查询_在Idea中编写Java程序连接查询Sqlite数据库
  2. SSM框架的搭建学习(1)---MyBatis的环境搭建
  3. mongodb和mysql的对比_MongoDB和MySQL的区别
  4. 若依单体版本代码生成模块使用教程
  5. JS 匿名函数 自执行
  6. php rc5,ThinkPHP 6.0 RC5 发布,多应用模式独立及中间件机制调整
  7. Atiitt 管理方面的误区总结 attilax总结
  8. java计算机毕业设计大数据在线考试系统在线阅卷系统及大数据统计分析源码+mysql数据库+系统+lw文档+部署
  9. 小程序图片上传,存储,获取,显示
  10. opencontrail 2.20
  11. pythonurllib新浪微博_利用python实现新浪微博爬虫_python新浪微博爬虫
  12. QWebEngineView崩溃及替代方案
  13. MySQL基本架构示意图
  14. mysql cbrt函数_ES6 数值的扩展
  15. Ryujinx - 基于 C# 开发的任天堂 Switch 模拟器
  16. nginx官网下载百度云分享
  17. 大学计算机实验教程实验6,《大学计算机基础实验教程》参考答案.(6页)-原创力文档...
  18. 华为慧通真相--关联企业迷局
  19. 度小满9.20测开笔试第二题——相似字符串
  20. SAP 面试题 训练

热门文章

  1. [leetcode] 342. Power of Four
  2. android snackbar源码,android Snackbar
  3. 使用nginx设置代理服务器
  4. 【python】喜欢XJJ?这不得来一波大采集?
  5. 如何让多个div横向排列(html+css)
  6. 网络营销:获取客户的有效方法
  7. windows批量创建用户脚本
  8. PHP+Mysql—白酒管理系统(前端+后端)
  9. Xshell 执行python脚本
  10. python图像/视频处理函数