编写带界面的图像处理程序,选择opencv+mfc是一种很好的选择;在读取摄像头数据方面,网上的方法很多,其中shiqiyu的camerads的方法是较好的。
      基于现有资料,通过在实际项目中的积累,我总结出来一套结合opencv和mfc的摄像头采集框架。具有以下特点:
      1、基于directshow,兼容性好,速度快。到目前为止,无论是工业相机还是普通相机,没发现不兼容的;
      2、摄像头部分通过线程读取,保证界面的运行流畅;
      3、框架经过多次打磨,已经比较稳定,不会出现异常错误;代码简洁明了,方便复用。

 
一、代码解析
       框架为对话框模式代码生成,加入CameraDS类和CvvImage类。
      
CameraDS是shiqiyu编写的,主要完成directshow的引入,提供了以下函数。能够获得目前相机总数,读取相机名称,打开相机以及获得当前帧的数据等

//打开摄像头,nCamID指定打开哪个摄像头,取值可以为0,1,2,...
//bDisplayProperties指示是否自动弹出摄像头属性页
//nWidth和nHeight设置的摄像头的宽和高,如果摄像头不支持所设定的宽度和高度,则返回false
boolCCameraDS::OpenCamera(int nCamID,bool bDisplayProperties=true,int nWidth=,int nHeight=);
//关闭摄像头,析构函数会自动调用这个函数
voidCloseCamera();
//返回摄像头的数目
//可以不用创建CCameraDS实例,采用int c=CCameraDS::CameraCount();得到结果。
staticintCameraCount();
//根据摄像头的编号返回摄像头的名字
//nCamID: 摄像头编号
//sName: 用于存放摄像头名字的数组
//nBufferSize: sName的大小
//可以不用创建CCameraDS实例,采用CCameraDS::CameraName();得到结果。
staticintCCameraDS::CameraName(int nCamID,char* sName,int nBufferSize);
//返回图像宽度
intGetWidth(){return m_nWidth;}
//返回图像高度
intGetHeight(){return m_nHeight;}
//抓取一帧,返回的IplImage不可手动释放!
//返回图像数据的为RGB模式的Top-down(第一个字节为左上角像素),即IplImage::origin=0(IPL_ORIGIN_TL)
IplImage*QueryFrame();
voidDisplayPinProperties(void);
CvvImage类是Opencv自己提供的,这里使用它的主要目的是讲mat对象画到mfc的控件中去
CvvImage cimg;
IplImage cpy = dst;
cimg.CopyOf(&cpy );// 复制图片
cimg.DrawToHDC( hDC,&rect );// 将图片绘制到显示控件的指定区域内
在GOMfcTemplate2Dlg中是主要代码,分为以下几个部分。这块的东西主要是我自己总结的。
1、摄像头显示循环,是单独的线程
//摄像头显示循环
DWORD WINAPI CaptureThread(LPVOID lpParameter)
{
CGOMfcTemplate2Dlg* pDlg =(CGOMfcTemplate2Dlg*)lpParameter;
while(true)
{
IplImage* queryframe = pDlg->cameraDs.QueryFrame();
Mat matframe(queryframe);//iplimage到Mat转化
if(pDlg->b_closeCam)//退出循环
break;
if(pDlg->b_takeApic )
{
pDlg->b_takeApic =false;
pDlg->m_mainframe = matframe;
Sleep();
}
pDlg->showImage(matframe,IDC_CAM);
}
return0;
}
这个线程函数,在创建的时候读取主Dlg的指针为参数,这样能够进行线程间通信。它主要完成两项工作,一个是通过camerads的QueryFrame函数读取当前的图像并传递给主线程;一个是判断b_closeCam和b_taleApic两个控制变量是否为true并进行相关操作。
目前的线程间通信采用的变量共享的方式,由于在摄像头线程中是写变量,在主线程中是读变量,一般不会冲突。但是如果摄像头很多或者实时性非常高,还是应该采用postmessage的方式通信。
2、initdialog中,对界面控件进行初始化
m_nCamCount =CCameraDS::CameraCount();//摄像头总数
//获得摄像头数目
char camera_name[];
char istr[];
for(int i=; i < m_nCamCount; i++)
{
int retval =CCameraDS::CameraName(i, camera_name,sizeof(camera_name));
sprintf_s(istr," # %d", i);
strcat_s(camera_name,istr );
CString camstr = camera_name;
if(retval >)
m_CBNCamList.AddString(camstr);
else
AfxMessageBox(_T("不能获取摄像头的名称"));
}
//初始化显示控件
CRect rect;
GetDlgItem(IDC_CAM)->GetClientRect(&rect);
m_mainframe =Mat::zeros(rect.Height(),rect.Width(),CV_8UC3);
GetDlgItem(IDC_PIC)->GetClientRect(&rect);
m_takepic =Mat::zeros(rect.Height(),rect.Width(),CV_8UC3);
return TRUE;// 除非将焦点设置到控件,否则返回 TRUE
包括填写combolist控件,为两个用于显示的static控件生成对应大小的mat变量等。
3、打开摄像头,主要就是根据选择的摄像头名称,创建摄像头线程
voidCGOMfcTemplate2Dlg::OnBnClickedBtnOpencam()
{
if(m_nCamCount>=)//开视频捕获线程
{
HANDLE hThread = NULL;
DWORD dwThreadID =;
OnBnClickedBtnClosecam();//首先关闭现有摄像头
bool bret = cameraDs.OpenCamera(m_iCamNum,false,,);//尝试打开摄像头
if(bret)
{
b_closeCam =false;
hThread =CreateThread(NULL,,CaptureThread,this,,&dwThreadID);
}
}
else
{
AfxMessageBox(_T("请确认至少有摄像头连上了"));
}
}
稍作修改,可以用于多摄像头,这个是完全没有问题并且做过实际项目的。
4、关闭摄像头
voidCGOMfcTemplate2Dlg::OnBnClickedBtnClosecam()
{
//尝试关闭摄像头
b_closeCam =true;
Sleep();
cameraDs.CloseCamera();
}
传递控制变量到摄像头线程,并且调用camerads的closecamera函数关闭摄像头;
5、采集图片
voidCGOMfcTemplate2Dlg::OnBnClickedBtnTakepic()
{
b_takeApic =true;
Sleep();
if(m_mainframe.rows >)
{
showImage(m_mainframe,IDC_PIC);
}
}
传递控制变量到摄像头线程,并且显示图片到控件。
6、显示图像函数,为了方便地讲mat对象显示到mfc的控件上,编写图像实现函数
voidCGOMfcTemplate2Dlg::showImage(Mat& src, UINT ID)
{
if(src.empty())
return;
CRect rect;
Mat dst = src.clone();
GetDlgItem(ID)->GetClientRect(&rect );// 获取控件尺寸位置
if(dst.channels()==)
cvtColor(dst, dst, CV_GRAY2BGR);
CDC* pDC =GetDlgItem( ID )->GetDC();
HDC hDC = pDC ->GetSafeHdc();// 获取 HDC(设备句柄) 来进行绘图操作
CvvImage cimg;
IplImage cpy = dst;
cimg.CopyOf(&cpy );// 复制图片
cimg.DrawToHDC( hDC,&rect );// 将图片绘制到显示控件的指定区域内
ReleaseDC( pDC );
}
主要就是调用cvvimage的drawtohdc函数,并进行相关的错误控制。
二、存在问题
      由于directshow本身是com架构,学习曲线陡峭。目前这个框架还存在至少两个问题:
      1、摄像头熟悉配置问题,以及配置的保持问题。目前框架中的摄像头配置没有实现,就是下图的这种能够调整摄像头参数的对话框如何出来。并且上一次的配置数据要能够保存下来。
      
      2、视频数据采集问题。
      目前我采用的是xvid的摄像头数据采集方式,能够解决问题。但是我认为directshow应该本身就能够才是摄像头数据,并且保存为.avi。这个方面还需要继续研究。
      我会在下一步工作中继续总结这方面资料,同时非常希望能够得到高人的指点,感谢!
 
代码位置 https://github.com/jsxyhelu/GOMfcTemplate2

[p.s]2016年10月7日 经过对directshow的简单学习,主要参考了ampcap(vs2012+win7可运行版本 https://git.coding.net/jsxyhelu/AMCa_win7vs2012.git ),解决了摄像头的属性操作问题,同时也想办法解决了分辨率设置问题。在usb摄像头上和工业摄像头上测试都没有问题。

[p.s]我自己在新装的机器上测试框架,不会出现需要direct头文件的情况,但是可能由于操作系统版本不一样,有一些操作系统还是需要头文件的。链接:https://pan.baidu.com/s/1qXNgzOo   direct 为完整版本下载,如果连接失败了请及时联系我。感谢。

基于opencv和mfc的摄像头采集代码(GOMFCTemplate2)持续更新的更多相关文章

  1. 基于opencv和mfc的摄像头采集代码(GOMFCTemplate2)

            编写带界面的图像处理程序,选择opencv+mfc是一种很好的选择:在读取摄像头数据方面,网上的方法很多,其中shiqiyu的camerads的方法是较好的.       基于现有资料 ...

  2. 基于opencv和QT的摄像头采集代码( GoQTtemplate3持续更新)

    在Linux操作系统上,编写带界面的图像处理程序,选择opencv+QT是一种很好的选择.GoQTtemplate3是我为编写Linux下图像处理程序实现的框架,希望能够为大家解决Linux环境下桌面 ...

  3. 基于Opencv和Mfc的图像处理增强库GOCVHelper(索引)

    GOCVHelper(GreenOpen Computer Version Helper )是我在这几年编写图像处理程序的过程中积累下来的函数库.主要是对Opencv的适当扩展和在实现Mfc程序时候的 ...

  4. 【从0開始Tornado建站】0.9版本号python站点代码开源--持续更新中

            从5月份開始[从0開始Tornado建站]这个专栏,開始一点一点把这个分类兴趣站点弄起来,从无到有的过程也是令人兴奋的:-) 国庆的时候等待备案然后上线,如今站点域名为ustchack ...

  5. Phantomjs实用代码段(持续更新中……)

    一.下载 下载链接二.解压安装包 直接解压即可三.配置环境变量 找到高级系统设置,打开它,出现以下图.点击环境变量. 分别点击编辑按钮 分别新建添加当初的解压路径,到bin文件夹.点击确定. 这样,环 ...

  6. 编写高性能的javascript代码(持续更新)

    参考资料: Vanilla JS——世界上最轻量的JavaScript框架(没有之一) http://segmentfault.com/a/1190000000355277 探索高效jQuery的奥秘 ...

  7. 基于opencv网络摄像头在ubuntu下的视频获取

     基于opencv网络摄像头在ubuntu下的视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译运行步骤 安装编译opencv-2.3  参 ...

  8. 【4opencv】为基于OpenCV的图像处理程序编写界面—关于QT\MFC\CSharp的选择以及GOCW的介绍

            基于OpenCV编写图像处理项目,除了算法以外,比较重要一个问题就是界面设计问题.对于c++语系的程序员来说,一般来说有QT/MFC两种考虑.QT的确功能强大,特别是QML编写andr ...

  9. 基于opencv在摄像头ubuntu根据视频获取

     基于opencv在摄像头ubuntu根据视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译执行步骤 安装编译opencv-2.3  參考h ...

随机推荐

  1. 启用oracle 11g自己主动收集统计信息

    今天接到朋友数据库一个case,在DBCA建库时,把自己主动收集统计信息的选项去掉了,数据库执行半年没有收集过统计信息.如今要启用方法例如以下: exec DBMS_AUTO_TASK_ADMIN.E ...

  2. 前端MVC Vue2学习总结(一)——MVC与vue2概要、模板、数据绑定与综合示例

    一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.Vue是框架而jQuery则是库. 1.2.AMD与CM ...

  3. Office 365 机器人(Bot)开发入门

    作者:陈希章 发表于 2017年7月29日 前言 作为人工智能技术的一个主要的表现形式,这些年机器人(bot)的应用越来越广泛.不管是有实物的,还是纯软件的,现在的机器人技术应该说已经走入寻常百姓家了 ...

  4. Elasticsearch过滤器——filter

    Elasticsearch中的所有的查询都会触发相关度得分的计算.对于那些我们不需要相关度得分的场景下,Elasticsearch以过滤器的形式提供了另一种查询功能.过滤器在概念上类似于查询,但是它们 ...

  5. 调用接口http封装

    public static String httpHandler(String URL,String xml){ try { URL url=new URL(URL); URLConnection c ...

  6. .Net Ajax跨域请求实现

    下一阵子要做一个网站Web储备一下知识,AJAX 实现跨域请求,估计会用到,以前在学  WebServer  时候老师整理的一个文档,现在便于查阅和使用现在放到我的博客中. 一般平时我写web页面的时 ...

  7. mybatis框架(5)---动态sql

    那么,问题来了: 什么是动态SQL? 动态SQL有什么作用? 传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误.Mybatis的动态S ...

  8. Android(Java) 字符串的常用操作,获取指定字符出现的次数,根据指定字符截取字符串

    /*这是第100000份数据,要截取出100000*/ String s="这是第100000份数据"; String s1 = s.substring(s.indexOf(&qu ...

  9. 每周.NET前沿技术文章摘要(2017-05-24)

    汇总国外.NET社区相关文章,覆盖.NET ,ASP.NET等内容: .NET Free eBook/Guide on '.NET Microservices – Architecture for C ...

  10. tensorflow ckpt文件转caffemodel时遇到的坑

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC"; color: #454545 } p.p2 ...