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所需的头文件
#include "Kinect.h"
#include <iostream>
#include "cv.h"
#include "opencv2/opencv.hpp"
②测试Kinect代码
//Kinect2
public:
int Test_kinect2();
3、cpp源文件文件
①安全释放函数:ColorBasics-D2D里面有的直接复制过来
// Safe release for interfaces
template<class Interface>
inline void SafeRelease(Interface *& pInterfaceToRelease)
{
if (pInterfaceToRelease != NULL)
{
pInterfaceToRelease->Release();
pInterfaceToRelease = NULL;
}
}
②OnInitDialog函数中使用到Test_kinect函数
③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;
}