Kinect v2学习笔记第三篇BodyIndex
C#
简介:
BodyIndex(人体索引)
基于从Kinect取得的Depth数据(传感器的距离信息)获取人体区域。
因为人体区域基于Depth数据,同时也依赖Depth传感器的分辨率。因为Kinect v2 (512×424)的Depth传感器的分辨率大幅提高,但是,能检测出的人体区域的数量还是6个人这一点没有发生改变,16位深度数据的低3位二进制明明可以表示到7个人,但不知微软怎么想的┑( ̄Д  ̄)┍。
从代码中的ProcessBodyIndexFrameData方法可以看出来,传过来的深度数据中提取出来的BodyIndexFrame应该只有识别的人体索引值,所以也验证了DepthFrame深度值是经过处理的,不像一代那样把16位给你,你自己去做处理。
每像素人体索引值是不定的哦,一般是一个人对应一个人,但当你在画面消失又出现时(等等),索引值会变。
那么问题来了,如果要用到人体索引,骨骼,色彩啥的,怎么同步,好在微软已经准备了
AllFrameReady事件,可确保到来的各种流数据是对齐同步的。(通说有种东西叫时间戳)
BodyIndex显示代码分析(C#):采取ColorFrame提到的的事件模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
namespace myBodyIndexViewer
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
//位图中每RGB像素大小,在后面用于计算步长
private const int BytesPerPixel = 4;
//用于区别六个人体的色彩
private static readonly uint[] BodyColor =
{
0x0000FF00,//蓝
0x00FF0000,//绿
0xFFFF4000,
0x40FFFF00,
0xFF40FF00,
0xFF808000,
};
private KinectSensor kinectSensor = null;
private BodyIndexFrameReader bodyIndexFrameReader = null;
private FrameDescription bodyIndexFrameDescription = null;
private WriteableBitmap bodyIndexBitmap = null;
private uint[] bodyIndexPixels = null;//保存人体像素颜色值
以上添加了每像素的大小(BGR32对应四字节)与人体区别色数组
public MainWindow()
{
InitializeComponent();
}
private void myBodyIndex_Loaded(object sender, RoutedEventArgs e)
{
//这里未注释部分如同ColorFrame命名功能相似,可查阅前者
this.kinectSensor = KinectSensor.GetDefault();
this.bodyIndexFrameReader = this.kinectSensor.BodyIndexFrameSource.OpenReader();
this.bodyIndexFrameDescription = this.kinectSensor.BodyIndexFrameSource.FrameDescription;
this.bodyIndexFrameReader.FrameArrived += this.Reader_FrameArrived;
this.bodyIndexPixels = new uint[this.bodyIndexFrameDescription.Width * this.bodyIndexFrameDescription.Height];
this.bodyIndexBitmap = new WriteableBitmap(this.bodyIndexFrameDescription.Width, this.bodyIndexFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);
this.kinectSensor.Open();
this.DataContext = this;
}
以下提供了对到来数据的操作工厂Reader_FrameArrived,然后它将数据缓存检查下完整性与适配性再外包给ProcessBodyIndexFrameDate,经后者处理后交到RenderBodyIndexPixel处“展示销售”。
private void Reader_FrameArrived(object sender,BodyIndexFrameArrivedEventArgs e)
{
bool Processed = false;
//从Reader得到真正的BodyIndexFrame
using (BodyIndexFrame bodyIndexFrame = e.FrameReference.AcquireFrame())
{
if(bodyIndexFrame!=null)
{
//使用低层缓冲(略有指针操作的意思)
using(Microsoft.Kinect.KinectBuffer bodyIndexBuffer=bodyIndexFrame.LockImageBuffer())
{
if (((this.bodyIndexFrameDescription.Width * this.bodyIndexFrameDescription.Height) == bodyIndexBuffer.Size) &&
(this.bodyIndexFrameDescription.Width == this.bodyIndexBitmap.PixelWidth) && (this.bodyIndexFrameDescription.Height == this.bodyIndexBitmap.PixelHeight))
{
//每帧对应像素点判断与处理
this.ProcessBodyIndexFrameData(bodyIndexBuffer.UnderlyingBuffer, bodyIndexBuffer.Size);
Processed = true;
}
}
}
}
if (Processed) this.RenderBodyIndexPixels();//更新位图显示数据
}
以下是苦逼的代工厂ProcessBodyIndexFrameData,不能自己去访问底层数据,只能等上一级Reader_FrameArrived把工作配备给他,然后对像素数据进行“染色处理”,不合格的点“放弃(涂黑)”。唯一自豪的是有自己的检查合格的方式。
private unsafe void ProcessBodyIndexFrameData(IntPtr bodyIndexFrameData, uint bodyIndexFrameDataSize)
{
byte* frameData = (byte*)bodyIndexFrameData;
for (int i = 0; i < (int)bodyIndexFrameDataSize; ++i)
{
if (frameData[i] < BodyColor.Length)//深度值数据人体索引在1-6内
{
this.bodyIndexPixels[i] = BodyColor[frameData[i]];
}
else
{
this.bodyIndexPixels[i] = 0x00000000;//非人体点纯黑
}
}
}
以下是销售方 RenderBodyIndexPixels()在得到数据货后,进行包装,按照制定大小范围,数据来源,数据步长,偏移量来上架,供人们选购。
private void RenderBodyIndexPixels()
{
this.bodyIndexBitmap.WritePixels(
new Int32Rect(0, 0, this.bodyIndexBitmap.PixelWidth, this.bodyIndexBitmap.PixelHeight),
this.bodyIndexPixels,
this.bodyIndexBitmap.PixelWidth * (int)BytesPerPixel,
0);
}
以下是送货员
public ImageSource BodyIndex
{
get
{
return this.bodyIndexBitmap;
}
}
工厂看门大爷myBodyIndex_Closing在收到厂长关门指令后,断电断水,收拾下垃圾走人。
private void myBodyIndex_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (this.bodyIndexFrameReader != null)
{
//this.bodyIndexFrameReader.FrameArrived -= this.Reader_FrameArrived;
this.bodyIndexFrameReader.Dispose();
this.bodyIndexFrameReader = null;
}
if (this.kinectSensor != null)
{
this.kinectSensor.Close();
this.kinectSensor = null;
}
}
}
}
在这里接受到的BodyIndexFrameDate是处理后的人体索引值。
效果图如下:
地面上的那是什么鬼,为什么会被着色,难道是倒影?应该是硬件的问题。
顺便一提,Microsoft.Kinect的人体识别是由其摄像头的配合加上微软那大量数据交由其服务器来计算,进行像素级的学习分辨得到的一系列貌似叫做什么特征树状图来给我们用的Kinect的人体识别模式。所以他无法感知类人物是死或活,有类似人体的(大字型衣服,娃娃?之类类人物的也会被识别为人体)
程序流程:
1.KinectSensor.open()开启传感器。
2.获取KinectSensor传感器对象,初始化
Reader 、FrameDescription、bitmap、窗口、注册帧到达事件
3.打开对应的BodyIndexFrameReader,选定所用的流,FrameDescription获得对色彩帧的描述,什么大小长宽高啊等等。
4.注册色彩帧到达后的处理事件,绑定的方法要自己写,方法主要完成区分数据然后“染色”。
5.使用WriteableBitmap初始化bitmap(提前),在4.的方法中画出相应的位图。
6.写返回Bitmap的ImageSource函数,可用其他替带。
7.收尾,释放资源,KinectSensor.close()关闭传感器。
附:
Xaml部分代码:图像若显示不完全,应该是展示窗口大小的问题。
<Image HorizontalAlignment="Left" Source="{Binding BodyIndex}" Stretch="UniformToFill" VerticalAlignment="Top" />