欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MFC kinect 实现骨骼识别

程序员文章站 2022-07-12 08:53:41
...

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

一.建立工程

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

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

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

MFC kinect 实现骨骼识别
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;            //命名空间
  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 <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,这里改好了那里又出问题了自己还要多多总结,还是需要积累经验。(中间男票帮我看了一下,分分钟他就搞定了,这就是差距,泪奔)。