MFC kinect 实现骨骼识别
http://blog.csdn.net/baolinq/article/details/52373574
本文主要根据以上博主的文章,在其基础上将其改为用MFC界面实现,效果图如下
功能实现如下:点击打开深度相机按钮,可以启动kinect相机,然后在左边框图中进行显示,骨骼检测得到的图在右边框图实现。代码链接:这里写链接内容
一.建立工程
1.打开visual studio 新建MFC工程
2.点击下一步,然后选择单文档对话框,再点击下一步
3.点击下一步,去掉勾选粗框架和系统菜单,再点击创建完成就可以了
4.此时界面如图
5.删除TODD:在此放置对话框控件,同时删除取消按钮,保留确定按钮,并将确定按钮选择Caption 重新命名为close
6.找到工具箱,添加一个Button ,一个Caption命名为打开深度相机,然后在工具箱中找到picture Control控件,添加两个到我们的界面,一个ID号重新命名为 IDC_ColorImage ,另外一个命名为IDC_STATIC。 同时可以适当将界面拉大一点,然后在工具箱中找到Static Test 空间,添加两个到界面,一个为深度图像显示,一个为骨骼检测显示,此时界面如图:
7. 现在开始添加代码 ,首先添加opencv的头文件,我是放在bonetestDlg.h中,
#include "Kinect.h"
#include "afxwin.h"
#include<time.h>
bonetestDlg.cpp添加头文件如下
#include "stdafx.h"
#include "bonetest.h"
#include "bonetestDlg.h"
#include "afxdialogex.h"
#include <Kinect.h>
#include "cv.h"
#include <opencv2/opencv.hpp> //opencv头文件
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std; //命名空间
- 双击打开深度相机按钮,程序会转到***Dlg.cpp 的 void CmfcshowDlg::OnBnClickedOpenKinect() 。在这里添加代码在***Dlg.cpp实现与kinect相关的程序如下,并且采用定时器定时更新数据
//--------------------------与Kinect有关-----------------------//
void CbonetestDlg::OnBnClickedOpenKinect()
{
HRESULT hr = myKinect.InitializeDefaultSensor(); //初始化默认Kinect
if (SUCCEEDED(hr))
{
myKinect.Update(); //刷新骨骼和深度数据
}
SetTimer(35, 20, NULL); //定时器
}
void CbonetestDlg::OnTimer(UINT_PTR nIDEvent) //定时器处理
{
switch (nIDEvent)
{
case 35:
myKinect.Update();
default:
break;
}
CDialog::OnTimer(nIDEvent);
}
注意:必须在下面这段程序中添加定时器事件
BEGIN_MESSAGE_MAP(CbonetestDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_TIMER() //定时器事件
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_OPEN_KINECT, &CbonetestDlg::OnBnClickedOpenKinect)
ON_BN_CLICKED(IDOK, &CbonetestDlg::OnBnClickedOk)
ON_STN_CLICKED(IDC_ColorImage, &CbonetestDlg::OnStnClickedColorimage)
END_MESSAGE_MAP()
9.自己创建kinect.h和kinect.cpp程序
//kinect.h
#pragma once
#include <Kinect.h>
#include "cv.h"
#include <opencv2/opencv.hpp> //opencv头文件
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std; //命名空间
template<class Interface>
inline void SafeRelease(Interface *& pInterfaceToRelease)
{
if (pInterfaceToRelease != NULL)
{
pInterfaceToRelease->Release();
pInterfaceToRelease = NULL;
}
}
class CBodyBasics
{
public:
static const int cColorWidth = 1920;
static const int cColorHeight = 1080;
CBodyBasics();
~CBodyBasics();
friend class CSerialPortTestDlg;
void Update();//获得骨架、背景二值图和深度信息
HRESULT InitializeDefaultSensor();//用于初始化kinect
private:
IKinectSensor *mySensor;//kinect源
IColorFrameSource * myColorSource;
UINT16 *pBuffer;
IColorFrame * pColorFrame;
IColorFrameReader *pColorReader;//用于彩色数据读取
IFrameDescription * myDescription;
RGBQUAD* m_pColorRGBX;
IFrameDescription * pFrameDescription;
IBodyFrameSource * myBodySource;
IBodyFrameReader * myBodyReader;
IBodyFrame * myBodyFrame;
ICoordinateMapper * myMapper;
cv::Mat copy; // 彩色图
cv::Mat showImage;
cv::Mat ColorImage1;
//int colorHeight = 0, colorWidth = 0;
int myBodyCount;
int nWidth;
int nHeight;
public:
void DrawMat(cv::Mat & img, UINT nID);
void ProcessColor(RGBQUAD* pBuffer, int nWidth, int nHeight);
//void updataToSurface(Mat &img );
void draw(Mat & img, Joint & r_1, Joint & r_2, ICoordinateMapper * myMapper);//画线跟圆
void BodyUpdate();
};
//kinect.cpp
#include "stdafx.h"
#include "afxdialogex.h"
#include "bonetest.h"
#include "bonetestDlg.h"
#include <Windows.h>
#include"kinect.h"
#include"stdio.h"
using namespace cv;
/* ━━━━━━神兽出没━━━━━━
* ┏┓ ┏┓
* ┏┛┻━━━┛┻┓
* ┃ ┃
* ┃ ━ ┃
* ┃ ┳┛ ┗┳ ┃
* ┃ ┃
* ┃ ┻ ┃
* ┃ ┃
* ┗━┓ ┏━┛Code is far away from bug with the animal protecting
* ┃ ┃ 神兽保佑, 代码无bug
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
* ━━━━━━━━━━━━━━━━*/
///构造函数
CBodyBasics::CBodyBasics()
{
mySensor = nullptr;
myColorSource = nullptr;
pColorReader = nullptr;
myDescription = nullptr;
pBuffer = NULL;
pColorFrame = NULL;
m_pColorRGBX=new RGBQUAD[cColorWidth*cColorHeight];
//-----------------初始化骨骼------//
myBodyCount = 0;
myBodySource = nullptr;
myBodyReader = nullptr;
myBodyFrame = nullptr;
myMapper = nullptr;
}
/// 析构函数
CBodyBasics::~CBodyBasics()
{
if (m_pColorRGBX)
{
delete[] m_pColorRGBX;
m_pColorRGBX = NULL;
}
SafeRelease(pColorReader);
if (mySensor)
{
mySensor->Close();
}
SafeRelease(mySensor);
myDescription->Release();
myColorReader->Release();
myColorSource->Release();
myBodyReader->Release();
myBodySource->Release();
}
HRESULT CBodyBasics::InitializeDefaultSensor()
{
HRESULT hr;
hr=GetDefaultKinectSensor(&mySensor);
mySensor->Open();//打开kinect
mySensor->get_ColorFrameSource(&myColorSource);//打开彩色帧的源
myColorSource->OpenReader(&pColorReader);//打开彩色帧读取器
SafeRelease(myColorSource);
//**********************以上为ColorFrame的读取前准备**************************
mySensor->get_BodyFrameSource(&myBodySource);
myBodySource->OpenReader(&myBodyReader);
mySensor->get_CoordinateMapper(&myMapper);
return hr;
}
void CBodyBasics::Update()
{
if (!pColorReader)
{
return;
}
HRESULT hr = pColorReader->AcquireLatestFrame(&pColorFrame);
//更新彩色帧
if (SUCCEEDED(hr))
{
pFrameDescription = NULL;
nWidth = 0;
nHeight = 0;
UINT nBufferSize = 0;
ColorImageFormat imageFormat = ColorImageFormat_None;
RGBQUAD *pBuffer = NULL;
if (SUCCEEDED(hr))
{
hr = pColorFrame->get_FrameDescription(&pFrameDescription);
//一共六种数据源,彩色图像
}
if (SUCCEEDED(hr))
{
hr = pFrameDescription->get_Width(&nWidth);
}
if (SUCCEEDED(hr))
{
hr = pFrameDescription->get_Height(&nHeight);
}
if (SUCCEEDED(hr))
{
hr = pColorFrame->get_RawColorImageFormat(&imageFormat);
}
if (SUCCEEDED(hr))
{
if (imageFormat == ColorImageFormat_Bgra)
{
hr = pColorFrame->AccessRawUnderlyingBuffer(&nBufferSize, reinterpret_cast<BYTE**>(&pBuffer));
}
else if (m_pColorRGBX)
{
pBuffer = m_pColorRGBX;
nBufferSize = cColorWidth * cColorHeight * sizeof(RGBQUAD);
hr = pColorFrame->CopyConvertedFrameDataToArray(nBufferSize, reinterpret_cast<BYTE*>(pBuffer), ColorImageFormat_Bgra);
}
else
{
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
ProcessColor(pBuffer, nWidth, nHeight);
copy = ColorImage1.clone();
BodyUpdate();//骨骼检测
}
SafeRelease(pFrameDescription);
}
SafeRelease(pColorFrame);
}
void CBodyBasics::ProcessColor(RGBQUAD* pBuffer, int nWidth, int nHeight)
{
// Make sure we've received valid data
if (pBuffer && (nWidth == cColorWidth) && (nHeight == cColorHeight))
{
Mat ColorImage(nHeight, nWidth, CV_8UC4, pBuffer);
ColorImage1 = ColorImage;
resize(ColorImage, showImage, Size(nWidth / 2, nHeight / 2));
//读取彩色图像并输出到矩阵
DrawMat(showImage, IDC_ColorImage);
}
}
void CBodyBasics::BodyUpdate()
{
//while (myBodyReader->AcquireLatestFrame(&myBodyFrame) != S_OK); //读取身体图像
HRESULT hr = myBodyReader->AcquireLatestFrame(&myBodyFrame);
if (SUCCEEDED(hr))
{
myBodySource->get_BodyCount(&myBodyCount);
IBody ** myBodyArr = new IBody *[myBodyCount]; //为存身体数据的数组做准备
for (int i = 0; i < myBodyCount; i++)
myBodyArr[i] = nullptr;
if (myBodyFrame->GetAndRefreshBodyData(myBodyCount, myBodyArr) == S_OK) //把身体数据输入数组
for (int i = 0; i < myBodyCount; i++)
{
BOOLEAN result = false;
if (myBodyArr[i]->get_IsTracked(&result) == S_OK && result) //先判断是否侦测到
{
Joint myJointArr[JointType_Count];
if (myBodyArr[i]->GetJoints(JointType_Count, myJointArr) == S_OK) //如果侦测到就把关节数据输入到数组并画图
{
draw(copy, myJointArr[JointType_Head], myJointArr[JointType_Neck], myMapper);
draw(copy, myJointArr[JointType_Neck], myJointArr[JointType_SpineShoulder], myMapper);
draw(copy, myJointArr[JointType_SpineShoulder], myJointArr[JointType_ShoulderLeft], myMapper);
draw(copy, myJointArr[JointType_SpineShoulder], myJointArr[JointType_SpineMid], myMapper);
draw(copy, myJointArr[JointType_SpineShoulder], myJointArr[JointType_ShoulderRight], myMapper);
draw(copy, myJointArr[JointType_ShoulderLeft], myJointArr[JointType_ElbowLeft], myMapper);
draw(copy, myJointArr[JointType_SpineMid], myJointArr[JointType_SpineBase], myMapper);
draw(copy, myJointArr[JointType_ShoulderRight], myJointArr[JointType_ElbowRight], myMapper);
draw(copy, myJointArr[JointType_ElbowLeft], myJointArr[JointType_WristLeft], myMapper);
draw(copy, myJointArr[JointType_SpineBase], myJointArr[JointType_HipLeft], myMapper);
draw(copy, myJointArr[JointType_SpineBase], myJointArr[JointType_HipRight], myMapper);
draw(copy, myJointArr[JointType_ElbowRight], myJointArr[JointType_WristRight], myMapper);
draw(copy, myJointArr[JointType_WristLeft], myJointArr[JointType_ThumbLeft], myMapper);
draw(copy, myJointArr[JointType_WristLeft], myJointArr[JointType_HandLeft], myMapper);
draw(copy, myJointArr[JointType_HipLeft], myJointArr[JointType_KneeLeft], myMapper);
draw(copy, myJointArr[JointType_HipRight], myJointArr[JointType_KneeRight], myMapper);
draw(copy, myJointArr[JointType_WristRight], myJointArr[JointType_ThumbRight], myMapper);
draw(copy, myJointArr[JointType_WristRight], myJointArr[JointType_HandRight], myMapper);
draw(copy, myJointArr[JointType_HandLeft], myJointArr[JointType_HandTipLeft], myMapper);
draw(copy, myJointArr[JointType_KneeLeft], myJointArr[JointType_FootLeft], myMapper);
draw(copy, myJointArr[JointType_KneeRight], myJointArr[JointType_FootRight], myMapper);
draw(copy, myJointArr[JointType_HandRight], myJointArr[JointType_HandTipRight], myMapper);
}
}
}
DrawMat(copy, IDC_STATIC);
delete[]myBodyArr;
myBodyFrame->Release();
}
}
void CBodyBasics::draw(Mat & img, Joint & r_1, Joint & r_2, ICoordinateMapper * myMapper)
{
//用两个关节点来做线段的两端,并且进行状态过滤
if (r_1.TrackingState == TrackingState_Tracked && r_2.TrackingState == TrackingState_Tracked)
{
ColorSpacePoint t_point; //要把关节点用的摄像机坐标下的点转换成彩色空间的点
Point p_1, p_2;
myMapper->MapCameraPointToColorSpace(r_1.Position, &t_point);
p_1.x = t_point.X;
p_1.y = t_point.Y;
myMapper->MapCameraPointToColorSpace(r_2.Position, &t_point);
p_2.x = t_point.X;
p_2.y = t_point.Y;
/* line(img, p_1, p_2, Vec3b(0, 255, 0), 5);
circle(img, p_1, 10, Vec3b(255, 0, 0), -1);
circle(img, p_2, 10, Vec3b(255, 0, 0), -1);*/
line(img, p_1, p_2, Scalar(0, 255, 0), 5);
circle(img, p_1, 10, Scalar(255, 0, 0), -1);
circle(img, p_2, 10, Scalar(255, 0, 0), -1);
}
}
void CBodyBasics::DrawMat(cv::Mat & img, UINT nID)
{
cv::Mat imgTmp;
CRect rect;
CbonetestDlg::s_pDlg->GetDlgItem(nID)->GetClientRect(&rect); // 获取控件大小
cv::resize(img, imgTmp, cv::Size(rect.Width(), rect.Height()));// 缩小或放大Mat并备份
// 转一下格式 ,这段可以放外面,
switch (imgTmp.channels())
{
case 1:
cv::cvtColor(imgTmp, imgTmp, CV_GRAY2BGRA); // GRAY单通道
break;
case 3:
cv::cvtColor(imgTmp, imgTmp, CV_BGR2BGRA); // BGR三通道
break;
default:
break;
}
int pixelBytes = imgTmp.channels()*(imgTmp.depth() + 1); // 计算一个像素多少个字节
// 制作bitmapinfo(数据头)
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 8 * pixelBytes;
bitInfo.bmiHeader.biWidth = imgTmp.cols;
bitInfo.bmiHeader.biHeight = -imgTmp.rows;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
// Mat.data + bitmap数据头 -> MFC
CDC *pDC = CbonetestDlg::s_pDlg->GetDlgItem(nID)->GetDC();
::StretchDIBits(
pDC->GetSafeHdc(),
0, 0, rect.Width(), rect.Height(),
0, 0, rect.Width(), rect.Height(),
imgTmp.data,
&bitInfo,
DIB_RGB_COLORS,
SRCCOPY
);
CbonetestDlg::s_pDlg->ReleaseDC(pDC);
}
10,重要的一点,
在kinect.cpp中显示图像到MFC上,
在bonetestDlg.h中定义一个静态变量:
static CbonetestDlg *s_pDlg; //对象指针
在bonetestDlg.cpp初始化:
CbonetestDlg * CbonetestDlg::s_pDlg = nullptr;
在 CbonetestDlg 消息处理程序中加上这句话s_pDlg = this;
BOOL CbonetestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
s_pDlg = this; //很重要的一句话 , 对话框的指针 , 用于与Kinect 类交互
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
这样基本功能就实现啦,这算我学了c++之后入手的第一个程序,调了两三天,才一点点的明白一个程序,怎样照葫芦画瓢,一味的照搬过来的话实现不了,程序还一大堆的bug,这里改好了那里又出问题了自己还要多多总结,还是需要积累经验。(中间男票帮我看了一下,分分钟他就搞定了,这就是差距,泪奔)。
上一篇: [Blender]人物骨骼-笔记
下一篇: leetcode213.打家劫舍 II