Kinect2+opencv之Body篇:显示Kinect2的Body

Kinect2+opencv之Body篇:显示Kinect2的Body

目录



一、目的:

1、显示出Kinect2的Body

二、参考

1、

①总结:good:作者翻译了一个系列的Kinect2的文章,目前测试Color、Depth、BodyIndex篇,都能实现,下面是参考后直接实现的代码

2、Kinect2+opencv之Color篇:显示Kinect2的画面

①总结:good:这是我总结的Color,有直接实现的代码

3、Kinect2+opencv之Depth篇:显示Kinect2的深度图

①总结:good:这是我总结的Depth,有直接实现的代码

4、Kinect2+opencv之Depth篇:显示Kinect2的BodyIndex

①总结:good:这是我总结的BodyIndex,有直接实现的代码

三、步骤

1、创建MFC工程,配置Kinect2和Opencv的环境

这个再次就不赘述了,网上很多的教程

2、头文件

①导入Kinect、opencv所需的头文件

www.zeeklog.com  - Kinect2+opencv之Body篇:显示Kinect2的Body
#include "Kinect.h"
#include <iostream>
#include "cv.h"
#include "opencv2/opencv.hpp"

②测试Kinect代码

www.zeeklog.com  - Kinect2+opencv之Body篇:显示Kinect2的Body
//Kinect2	
public:
    int Test_kinect2();

3、cpp源文件文件

①安全释放函数:ColorBasics-D2D里面有的直接复制过来

www.zeeklog.com  - Kinect2+opencv之Body篇:显示Kinect2的Body
// Safe release for interfaces
template<class Interface>
inline void SafeRelease(Interface *& pInterfaceToRelease)
{
    if (pInterfaceToRelease != NULL)
    {
        pInterfaceToRelease->Release();
        pInterfaceToRelease = NULL;
    }
}

②OnInitDialog函数中使用到Test_kinect函数

www.zeeklog.com  - Kinect2+opencv之Body篇:显示Kinect2的Body

③Test_kinect:函数

//功能:显示Kinect2的Body
//参考网站: https://mp.csdn.net/console/editor/html/105449249    https://www.cnblogs.com/TracePlus/p/4138615.html    
int CMyKinect2Dlg::Test_kinect2()
{
    //第一步:取得「Sensor」
    IKinectSensor* pSensor;//Kinect v2预览版的Sensor接口。
    HRESULT hResult = S_OK;
    hResult = GetDefaultKinectSensor(&pSensor);//取得默认的Sensor。
    if (FAILED(hResult))
    {
        std::cerr << "Error : GetDefaultKinectSensor" << std::endl;
        return -1;
    }
    hResult = pSensor->Open();// 打开Sensor。
    if (FAILED(hResult))
    {
        std::cerr << "Error : IKinectSensor::Open()" << std::endl;
        return -1;
    }

    //第二步:Body部分:从「Sensor」取得「Source」。
    IBodyFrameSource* pBodySource;//Body Frame的Source接口。
    hResult = pSensor->get_BodyFrameSource(&pBodySource);//从Sensor取得Source。
    if (FAILED(hResult))
    {
        std::cerr << "Error : IKinectSensor::get_BodyFrameSource()" << std::endl;
        return -1;
    }

    //第三步:Body部分:「Source」从打开「Reader」。
    IBodyFrameReader* pBodyReader;//Body  Frame的Reader接口。
    hResult = pBodySource->OpenReader(&pBodyReader);//从Source打开Reader。
    if (FAILED(hResult))
    {
        std::cerr << "Error : IBodyFrameSource::OpenReader()" << std::endl;
        return -1;
    }


    //第四步:Body部分:从「Sensor」取得「Coordinate Mapper」。  
    //在这之前,为了可以和从传感器取得的坐标匹配,取得ICoordinateMapper的接口(列表1.4),由于Color照相机和Depth传感器的位置是分开的,因此需要把body数据和Color图像的位置进行匹配。
    ICoordinateMapper* pCoordinateMapper;//Frame之间的坐标匹配的接口。
    hResult = pSensor->get_CoordinateMapper(&pCoordinateMapper);//从Sensor获取坐标匹配的接口。
    if (FAILED(hResult))
    {
        std::cerr << "Error : IKinectSensor::get_CoordinateMapper()" << std::endl;
        return -1;
    }

    //第五步:Color部分:从「Sensor」取得「Source」。
    // 这里只是关于取得Body的源代码解说,不过,为了案例程序的显示,也同时取得了Color。
    IColorFrameSource* pColorSource;
    hResult = pSensor->get_ColorFrameSource(&pColorSource);
    if (FAILED(hResult))
    {
        std::cerr << "Error : IKinectSensor::get_ColorFrameSource()" << std::endl;
        return -1;
    }

    //第六步:Color部分:从「Source」取得「Reader」。
    IColorFrameReader* pColorReader;
    hResult = pColorSource->OpenReader(&pColorReader);
    if (FAILED(hResult))
    {
        std::cerr << "Error : IColorFrameSource::OpenReader()" << std::endl;
        return -1;
    }

    //第七步:Color部分:从「Reader」取得最新的「Frame」
    int width = 1920;//Color图像的尺寸(1920×1080)。 这里为了简化说明,画像尺寸用硬编码来设定,示例程序可以从Source取得着Frame信息
    int height = 1080;
    unsigned int bufferSize = width * height * 4 * sizeof(unsigned char);//Color图像的数据尺寸。
    cv::Mat bufferMat(height, width, CV_8UC4);//为了处理Color图像,准备OpenCV的cv::Mat。「bufferMat」是原始的图像数据,「colorMat」是Resize图像数据的处理。
    cv::Mat bodyMat(height / 2, width / 2, CV_8UC4);//「CV_8UC4」,是无符号8bit整数(8U),4个channel(C4)并列来表现1个像素的数据格式。
    cv::namedWindow("Body");
    // Color Table
    cv::Vec3b color[6];
    color[0] = cv::Vec3b(255, 0, 0);
    color[1] = cv::Vec3b(0, 255, 0);
    color[2] = cv::Vec3b(0, 0, 255);
    color[3] = cv::Vec3b(255, 255, 0);
    color[4] = cv::Vec3b(255, 0, 255);
    color[5] = cv::Vec3b(0, 255, 255);
    while (1)
    {
        // Color Frame  
        IColorFrame* pColorFrame = nullptr;//取得Color图像的Frame接口。
        hResult = pColorReader->AcquireLatestFrame(&pColorFrame);//从Reader取得最新的Frame。
        if (SUCCEEDED(hResult))
        {
            //从Frame取得Color图像。 默认的格式是YUY2(亮度与色差表现的格式),不过可以经过简单处理变换为BGRA。
            hResult = pColorFrame->CopyConvertedFrameDataToArray(bufferSize, reinterpret_cast<BYTE*>(bufferMat.data), ColorImageFormat_Bgra);
            if (SUCCEEDED(hResult))
            {
                //缩小为长宽各一半的尺寸(960×540)。 从「Frame」取得Color图像的数据时,Kinect SDK v1是预先指定图像尺寸和格式,Kinect SDK v2不能指定图像尺寸。因此,取得数据后可以按任意形状来整理。
                //取出的Color图像直接显示的话尺寸太大(1920×1080),在这里采用了OpenCV的resize命令(cv::resize()),缩小为长宽各一半的尺寸(960×540)。
                //(注:阅读理解时不要受这一段缩放代码的干扰,仅仅就是为了考虑在显示器上显示的方便观察,没别的意义。)
                cv::resize(bufferMat, bodyMat, cv::Size(), 0.5, 0.5);
            }
        }
        SafeRelease(pColorFrame);

        //第八步:Body部分:Body Frame
        IBodyFrame* pBodyFrame = nullptr;//Body的Frame接口。
        hResult = pBodyReader->AcquireLatestFrame(&pBodyFrame);//从Reader里取得最新的Frame。
        if (SUCCEEDED(hResult))
        {
            IBody* pBody[BODY_COUNT] = { 0 };
            hResult = pBodyFrame->GetAndRefreshBodyData(BODY_COUNT, pBody);//从Frame取得Body。  后面,是从人体取得数据。
            if (SUCCEEDED(hResult))
            {
                for (int count = 0; count < BODY_COUNT; count++)
                {
                    BOOLEAN bTracked = false;//确认能着追踪到人体
                    hResult = pBody[count]->get_IsTracked(&bTracked);
                    if (SUCCEEDED(hResult) && bTracked)
                    {
                        Joint joint[JointType::JointType_Count];
                        hResult = pBody[count]->GetJoints(JointType::JointType_Count, joint);//取得人体Joint(关节)。
                        if (SUCCEEDED(hResult))
                        {
                            // Left Hand State
                            HandState leftHandState = HandState::HandState_Unknown;//取得Hand State。
                            hResult = pBody[count]->get_HandLeftState(&leftHandState);
                            if (SUCCEEDED(hResult))
                            {
                                //为了绘制,把Body座標向Color座標的坐标匹配。  匹配的坐标是否超出绘制范围(这里Color图像的尺寸是1920×1080)的检查。
                                //(注:因为两个Camera位置、FOV和分辨率的不同,在匹配时不可能完全一一对应,所以必须检查坐标的有效性)
                                ColorSpacePoint colorSpacePoint = { 0 };
                                hResult = pCoordinateMapper->MapCameraPointToColorSpace(joint[JointType::JointType_HandLeft].Position, &colorSpacePoint);
                                if (SUCCEEDED(hResult))
                                {
                                    int x = static_cast<int>(colorSpacePoint.X);
                                    int y = static_cast<int>(colorSpacePoint.Y);
                                    if ((x >= 0) && (x < width) && (y >= 0) && (y < height))
                                    {
                                        // 对应状态绘制相应颜色的○(圆型)做Hand State的可视化。用各自对应Open(打开:布),Closed(关闭:拳),Lasso(套索:剪)的颜色绘制。如果检查不出状态就不绘制。
                                        if (leftHandState == HandState::HandState_Open)
                                        {
                                            cv::circle(bufferMat, cv::Point(x, y), 75, cv::Scalar(0, 128, 0), 5, CV_AA);
                                        }
                                        else if (leftHandState == HandState::HandState_Closed)
                                        {
                                            cv::circle(bufferMat, cv::Point(x, y), 75, cv::Scalar(0, 0, 128), 5, CV_AA);
                                        }
                                        else if (leftHandState == HandState::HandState_Lasso)
                                        {
                                            cv::circle(bufferMat, cv::Point(x, y), 75, cv::Scalar(128, 128, 0), 5, CV_AA);
                                        }
                                    }
                                }
                            }

                            // Right Hand State
                            /* 和左手一样,获取右手Hand State绘制状态。 */				

                            // Joint  对应人体的Joint参照color table的颜色做绘制。 和Hand State一样,Body坐标向Color坐标坐标匹配来绘制●(圆点)。
                            for (int type = 0; type < JointType::JointType_Count; type++)
                            {
                                ColorSpacePoint colorSpacePoint = { 0 };
                                pCoordinateMapper->MapCameraPointToColorSpace(joint[type].Position, &colorSpacePoint);
                                int x = static_cast<int>(colorSpacePoint.X);
                                int y = static_cast<int>(colorSpacePoint.Y);
                                if ((x >= 0) && (x < width) && (y >= 0) && (y < height))
                                {
                                    cv::circle(bufferMat, cv::Point(x, y), 5, static_cast<cv::Scalar>(color[count]), -1, CV_AA);
                                }
                            }
                        }
                    }
                }
                cv::resize(bufferMat, bodyMat, cv::Size(), 0.5, 0.5);
            }
        }
        SafeRelease(pBodyFrame);


        // Show Window
        cv::imshow("Body", bodyMat);
        if (cv::waitKey(10) == VK_ESCAPE)
        {
            break;
        }
    }



    return 0;
}

4、运行效果:

①左手能识别张开、握拳、效果,模拟了右手但是没有绘画出来

www.zeeklog.com  - Kinect2+opencv之Body篇:显示Kinect2的Body