您的位置:首页 > 其它

[置顶] 第六章 KinectV2结合MFC显示和处理图像数据(上)

2016-09-01 16:42 507 查看

第六章   KinectV2结合MFC显示和处理图像数据(上)

 

首先声明一下,本系统所使用的开发环境版本是计算机系统Windows 10、Visual Studio 2013、Opencv3.0和Kinect SDK v2.0。这些都可以在百度上找到,download下来安装一下即可。详情参考本系列第二篇KinectV2结合opencv入门开发以及一些相关的学习资料

其实只要用前面几章的知识就差不多可以把基本功能实现了,只不过实在控制台下,没有一个好用的交互界面,不方便使用,更不能发挥Kinect的体感方面的优势。想到前面的基础程序是在C++基础上写的,故选了一个好入门的平台--MFC,说好入门不是说MFC简单,只是因为MFC比较成熟,学习资料较多,如果前面是在C#基础上写的,那肯定首选WPF了。MFC那么多内容,这里不便说清楚,我也说不清楚,只能大概说一下本系统的简单界面。

 

一、用电脑的摄像头代替Kinect的程序

 

核心的代码如下:

 

VideoCapture capture;	//定义一个摄像头类型的对象
capture.open(0);	//打开摄像头
//AfxMessageBox("OK");
if (!capture.isOpened())	//判断是否打开摄像头
{
MessageBox(_T("无法打开摄像头"));
return;
}
//保存视频
writer = cvCreateVideoWriter("MyVideo.avi", CV_FOURCC('x', 'v', 'I', 'D'), 25, cvSize(640, 480));
// 测试
//IplImage* m_Frame;
Mat cframe;	//定义一个Mat类对象,用来存放摄像头的图像流
capture >> cframe;
IplImage* iframe;
iframe = &IplImage(cframe);	//Mat转IplImage
DrawPicToHDC(iframe, IDC_PIC_STATIC);	//显示摄像头获取的图像
//cvWaitKey(10);

// 设置计时器,每10ms触发一次事件
SetTimer(1, 10, NULL);


 

运行截图:

 


这是一个非简单的小demo,实现了普通相机的功能,就是录像和拍照啊之类的,基于MFC平台写的,非常简单,而且有详细注释,这里就不做过多的解释。这里给出整个过程的下载地址:

http://download.csdn.net/detail/baolinq/9618271

 

二、基于MFC平台的Kinect程序

 

有了上部分的基础,电脑摄像头和Kinect都是一种摄像头,只是Kinect相机更高端一点,是RGBD相机,很容易类比过去,把电脑摄像头换成kinect相机就差不多了。说起来是这么简单,一两句话的事,但这只是一种思想,如果真的用程序去实现还是有一定的难度。一起来看一下。

 

2.1 MFC 一个类访问另一个类成员对象的成员变量值 

 

电脑摄像头比较简单,只有几句话就可以表达出来,可以直接写在MFC自动生成的类和方法里面,然后单击按钮触发事件。但是Kinect的初始化和启动以及获取图像的程序那么长,如果也放在那下面的话,会显得程序结构很乱,可读性很差。一般我们会自己创建类和自己的方法,然后在MFC自带的类和方法里面调用自己创建的类,这样可以一目了然,程序干净整洁,也更加便于维护。这里就会遇到MFC里面一个类访问另一个类成员对象的成员变量值 。

 

 

MFC中一个类要访问另外一个类的的对象的成员变量值,这就需要获得原来那个类对象的指针,其实有好几种方法都可以实现。

比如维护一个单例模式、设置静态变量等等。我们这里举个列子,实现多个类之间的相互访问。

 

(1)示例:创建MFC对话框,实现对个对话框之间数据访问

1. 我们创建一个MFC对话框应用程序,命名为Visit工程.

2. 对话框本身有一个主界面(CVisitDlg对话框),那我们再添加一个新界面CXXXDlg.

(2)在主界面CVisitDlg类的头文件中,添加一个static CVisitDlg *s_pDialog;指针(注:添加为共有类型的)

由于这个指针式静态的,我们需要在类外初始化,那么我们在CVisitDlg .cpp文件中,写上一行初始化代码,直接初始化为空,如下:

CVisitDlg *CVisitDlg::s_pDialog = NULL; //(注意要写在类外,不要写在类实现函数里面)。

(3)然后需要在CVisitDlg 主对话框生成的时候,给这个指针赋值为主对话框指针。

那么,我们可以在CVisitDlg 类的构造函数或者OnInitDialog()函数里面写上如下一句代码:

s_pDialog = this;

好了,现在我们只要在其他的类里面获得这个静态指针,就可以访问这个类里面的所有数据了~~~~

(4)获得静态指针

假设,我们想在CXXXDlg类中访问CVisitDlg类的数据,如下代码轻轻松松搞定:

CVisitDlg *pDia = CVisitDlg::s_pDialog;

 

小插曲:这里推荐一个学习编程的网站   鸡啄米http://www.jizhuomi.com/software/257.html,

里面有MFC的入门学习资料,很基础,可以快速入门,想提高的话,可以看孙鑫老师的VC++和深入浅出MFC之类的。

 

2.2  MFC创建文件夹

就是在指定目录下创建文件夹,然后把一些处理结果和图片保存到该目录下。

CString m_strFolderPath=_T("E:\\path");
if(!PathIsDirectory(m_strFolderPath))
{
CString strMsg;
strMsg.Format (_T("指定路径\"%s\"不存在,是否创建?"), m_strFolderPath);
if (AfxMessageBox(strMsg, MB_YESNO) == IDYES)
{
if (!CreateDirectory(m_strFolderPath, NULL ) )
{
strMsg.Format(_T("创建路径\"%s\"失败!是否继续?"), m_strFolderPath);
if (AfxMessageBox(strMsg, MB_YESNO) == IDYES)
return;
}
}
}


但是此处CreateDirectory有局限性,只能创建单层文件夹,如果需要创建多层,还需要自己写函数

 

2.3  MFC窗口背景颜色和控件颜色的设置

如果只是像前面那个例子那样,整个界面全是灰色的,那界面就很没意思了,太难看了,没人愿意去使用这样的东西。既然做了界面,就应该做的酷炫一点,美观一点,这样用起来才会比较愉悦。这里给大家讲一些最简单的装X利器,给背景和控件上点颜色,这样简单的步骤会让你的界面看起来比较高端~~

对话框背景色的设置

参考:http://www.tuicool.com/articles/miai6f

(1)重载OnPaint()函数,即WM_PAINT消息,代码如下所示:

void CtestDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
//CDialogEx::OnPaint();

//设置为绿色背景
CRect   rect;
CPaintDC   dc(this);
GetClientRect(rect);
dc.FillSolidRect(rect,RGB(0,255,0));
}
}



2、重载OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor),即WM_CTLCOLOR消息:

 a)在对话框的.h文件中添加CBrush类的成员m_brush

  b)在对话框的.cpp文件中的OnInitDialog()中添加以下代码(背景红色):

<span style="white-space: pre;">	</span>m_brush.CreateSolidBrush(RGB(255,0,0));
  
c)重载OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor),代码如下:


HBRUSH CtestDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO:  在此更改 DC 的任何特性

if(nCtlColor == CTLCOLOR_DLG)
return   m_brush;   //返加红色刷子

// TODO:  如果默认的不是所需画笔,则返回另一个画笔
return hbr;<pre name="code" class="cpp">




控件颜色的设置

重载OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor),即WM_CTLCOLOR消息:

HBRUSH CtestDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO:  在此更改 DC 的任何特性

// 设置显示字体
CFont * font=new CFont;
font->CreateFont(16,0,0,0,FW_SEMIBOLD,FALSE,FALSE,0,
ANSI_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
DEFAULT_PITCH&FF_SWISS,_T("Arial"));

switch(nCtlColor)
{
case CTLCOLOR_STATIC: //对所有静态文本框的设置        {
pDC->SetBkMode(TRANSPARENT);
//设置背景为透明
pDC->SetTextColor(RGB(255,255,0)); //设置字体颜色
pWnd->SetFont(font); //设置字体
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
//创建画刷
return (HBRUSH) B; //返回画刷句柄        }
case CTLCOLOR_EDIT: //对所有编辑框的设置        {
if(IDC_EDIT2 == pWnd->GetDlgCtrlID())
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,255,0));
pWnd->SetFont(font);
HBRUSH B = CreateSolidBrush(RGB(125,125,125));
return (HBRUSH) B;
}
if(IDC_EDIT3 == pWnd->GetDlgCtrlID())
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,0, 0));
pWnd->SetFont(font);
HBRUSH B = CreateSolidBrush(RGB(125,125,0));
return (HBRUSH) B;
}
}
default:
return CDialog::OnCtlColor(pDC,pWnd, nCtlColor);
}

// TODO:  如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}


注:

nCtlColor的类别有以下几种:

CTLCOLOR_BTN  按钮控件

CTLCOLOR_DLG  对话框

CTLCOLOR_EDIT  编辑框

CTLCOLOR_LISTBOX  列表框

CTLCOLOR_MSGBOX   消息框

CTLCOLOR_SCROLLBAR  滚动条

CTLCOLOR_STATIC  静态文本

 

这部分主要给大家讲了一下MFC做界面遇到的部分困难,不会的时候他们是困难,学会了之后就是技术了。还有一些其他常见的问题比如界面的放大缩小、最大化和还原、界面的多线程问题等等,还有好多的。通过这些就可以很容易做出类似这样的界面

 


 

类比第一个程序,把Kinect代替电脑的摄像头,然后把Kinect 获取的图像显示到MFC界面上,这个问题的主要难度都在文章的第二部分讲到了,并给出了解决方案,具体的实现将在下一章中讲解。敬请期待~

 

超跑开起来,要先制定一个小目标,比如先买辆兰博基尼限量款~哈哈哈






 

 


 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: