MFC kinect 实现骨骼识别

http://blog.csdn.net/baolinq/article/details/52373574
本文主要根据以上博主的文章,在其基础上将其改为用MFC界面实现,效果图如下
MFC kinect 实现骨骼识别_第1张图片
功能实现如下:点击打开深度相机按钮,可以启动kinect相机,然后在左边框图中进行显示,骨骼检测得到的图在右边框图实现。代码链接:这里写链接内容

一.建立工程

1.打开visual studio 新建MFC工程
MFC kinect 实现骨骼识别_第2张图片
2.点击下一步,然后选择单文档对话框,再点击下一步

MFC kinect 实现骨骼识别_第3张图片
3.点击下一步,去掉勾选粗框架和系统菜单,再点击创建完成就可以了
MFC kinect 实现骨骼识别_第4张图片
4.此时界面如图
MFC kinect 实现骨骼识别_第5张图片
5.删除TODD:在此放置对话框控件,同时删除取消按钮,保留确定按钮,并将确定按钮选择Caption 重新命名为close

MFC kinect 实现骨骼识别_第6张图片
6.找到工具箱,添加一个Button ,一个Caption命名为打开深度相机,然后在工具箱中找到picture Control控件,添加两个到我们的界面,一个ID号重新命名为 IDC_ColorImage ,另外一个命名为IDC_STATIC。 同时可以适当将界面拉大一点,然后在工具箱中找到Static Test 空间,添加两个到界面,一个为深度图像显示,一个为骨骼检测显示,此时界面如图:

MFC kinect 实现骨骼识别_第7张图片
7. 现在开始添加代码 ,首先添加opencv的头文件,我是放在bonetestDlg.h中,

#include "Kinect.h"
#include "afxwin.h"
#include

bonetestDlg.cpp添加头文件如下

#include "stdafx.h"
#include "bonetest.h"
#include "bonetestDlg.h"
#include "afxdialogex.h"
#include 
#include "cv.h"
#include    //opencv头文件
#include   
#include  

using namespace cv;
using namespace std;            //命名空间
  1. 双击打开深度相机按钮,程序会转到***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 
#include "cv.h"
#include    //opencv头文件
#include   
#include  

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 

#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(&pBuffer));

            }
            else if (m_pColorRGBX)
            {
                pBuffer = m_pColorRGBX;

                nBufferSize = cColorWidth * cColorHeight * sizeof(RGBQUAD);
                hr = pColorFrame->CopyConvertedFrameDataToArray(nBufferSize, reinterpret_cast(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,这里改好了那里又出问题了自己还要多多总结,还是需要积累经验。(中间男票帮我看了一下,分分钟他就搞定了,这就是差距,泪奔)。

你可能感兴趣的:(随记)