下面我们来一个比较复杂的,bodybasic的显示。这个就是显示骨骼数据。
这里我就只做备注,不自己写demo了
//------------------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace Microsoft.Samples.Kinect.BodyBasics
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Kinect;
/// <summary>
/// Interaction logic for MainWindow
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
/// <summary>
/// Radius of drawn hand circles
/// </summary>
///显示手的范围-圆角的尺寸
private const double HandSize = 30;
/// <summary>
/// Thickness of drawn joint lines
/// </summary>
/// 线的粗细
private const double JointThickness = 3;
/// <summary>
/// Thickness of clip edge rectangles
/// </summary>
/// 线的角度
private const double ClipBoundsThickness = 10;
/// <summary>
/// Constant for clamping Z values of camera space points from being negative
/// </summary>
/// z轴的位置
private const float InferredZPositionClamp = 0.1f;
/// <summary>
/// Brush used for drawing hands that are currently tracked as closed
/// </summary>
/// 当手闭合时的颜色
private readonly Brush handClosedBrush = new SolidColorBrush(Color.FromArgb(128, 255, 0, 0));
/// <summary>
/// Brush used for drawing hands that are currently tracked as opened
/// </summary>
/// 手张开时的颜色
private readonly Brush handOpenBrush = new SolidColorBrush(Color.FromArgb(128, 0, 255, 0));
/// <summary>
/// Brush used for drawing hands that are currently tracked as in lasso (pointer) position
/// </summary>
/// 链接点的颜色
private readonly Brush handLassoBrush = new SolidColorBrush(Color.FromArgb(128, 0, 0, 255));
/// <summary>
/// Brush used for drawing joints that are currently tracked
/// </summary>
/// 骨骼的颜色
private readonly Brush trackedJointBrush = new SolidColorBrush(Color.FromArgb(255, 68, 192, 68));
/// <summary>
/// Brush used for drawing joints that are currently inferred
/// </summary>
/// 当前推断出的用于绘制关节的画笔
private readonly Brush inferredJointBrush = Brushes.Yellow;
/// <summary>
/// Pen used for drawing bones that are currently inferred
/// </summary>
/// 用于绘制当前推断的骨骼的笔
private readonly Pen inferredBonePen = new Pen(Brushes.Gray, 1);
/// <summary>
/// Drawing group for body rendering output
/// </summary>
/// drawinggroup对象
private DrawingGroup drawingGroup;
/// <summary>
/// Drawing image that we will display
/// </summary>
/// image对象
private DrawingImage imageSource;
/// <summary>
/// Active Kinect sensor
/// </summary>
/// 传感器对象
private KinectSensor kinectSensor = null;
/// <summary>
/// Coordinate mapper to map one type of point to another
/// </summary>
/// 坐标映射器将一种类型的点映射到另一种类型
private CoordinateMapper coordinateMapper = null;
/// <summary>
/// Reader for body frames
/// </summary>
/// 身体结构的对象
private BodyFrameReader bodyFrameReader = null;
/// <summary>
/// Array for the bodies
/// </summary>
/// 身体的数组
private Body[] bodies = null;
/// <summary>
/// definition of bones
/// </summary>
/// 骨头的定义
private List<Tuple<JointType, JointType>> bones;
/// <summary>
/// Width of display (depth space)
/// </summary>
/// 显示的高度
private int displayWidth;
/// <summary>
/// Height of display (depth space)
/// </summary>
/// 显示的宽度
private int displayHeight;
/// <summary>
/// List of colors for each body tracked
/// </summary>
/// 身体的颜色
private List<Pen> bodyColors;
/// <summary>
/// Current status text to display
/// </summary>
private string statusText = null;
/// <summary>
/// Initializes a new instance of the MainWindow class.
/// </summary>
public MainWindow()
{
// one sensor is currently supported 获取传感对象
this.kinectSensor = KinectSensor.GetDefault();
// get the coordinate mapper 获取坐标映射
this.coordinateMapper = this.kinectSensor.CoordinateMapper;
// get the depth (display) extents 获取深度数据
FrameDescription frameDescription = this.kinectSensor.DepthFrameSource.FrameDescription;
// get size of joint space 获取
this.displayWidth = frameDescription.Width;
this.displayHeight = frameDescription.Height;
// open the reader for the body frames 获取关节空间的大小
this.bodyFrameReader = this.kinectSensor.BodyFrameSource.OpenReader();
// a bone defined as a line between two joints 新建关节list
this.bones = new List<Tuple<JointType, JointType>>();
// Torso 躯干
this.bones.Add(new Tuple<JointType, JointType>(JointType.Head, JointType.Neck));
this.bones.Add(new Tuple<JointType, JointType>(JointType.Neck, JointType.SpineShoulder));
this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineShoulder, JointType.SpineMid));
this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineMid, JointType.SpineBase));
this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineShoulder, JointType.ShoulderRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineShoulder, JointType.ShoulderLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineBase, JointType.HipRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineBase, JointType.HipLeft));
// Right Arm 右手
this.bones.Add(new Tuple<JointType, JointType>(JointType.ShoulderRight, JointType.ElbowRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.ElbowRight, JointType.WristRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.WristRight, JointType.HandRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.HandRight, JointType.HandTipRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.WristRight, JointType.ThumbRight));
// Left Arm 左手
this.bones.Add(new Tuple<JointType, JointType>(JointType.ShoulderLeft, JointType.ElbowLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.ElbowLeft, JointType.WristLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.WristLeft, JointType.HandLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.HandLeft, JointType.HandTipLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.WristLeft, JointType.ThumbLeft));
// Right Leg 右腿
this.bones.Add(new Tuple<JointType, JointType>(JointType.HipRight, JointType.KneeRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.KneeRight, JointType.AnkleRight));
this.bones.Add(new Tuple<JointType, JointType>(JointType.AnkleRight, JointType.FootRight));
// Left Leg 左腿
this.bones.Add(new Tuple<JointType, JointType>(JointType.HipLeft, JointType.KneeLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.KneeLeft, JointType.AnkleLeft));
this.bones.Add(new Tuple<JointType, JointType>(JointType.AnkleLeft, JointType.FootLeft));
// populate body colors, one for each BodyIndex 因为kinect支持同时标注多个人,我们每个人都标注一个颜色
this.bodyColors = new List<Pen>();
this.bodyColors.Add(new Pen(Brushes.Red, 6));
this.bodyColors.Add(new Pen(Brushes.Orange, 6));
this.bodyColors.Add(new Pen(Brushes.Green, 6));
this.bodyColors.Add(new Pen(Brushes.Blue, 6));
this.bodyColors.Add(new Pen(Brushes.Indigo, 6));
this.bodyColors.Add(new Pen(Brushes.Violet, 6));
// set IsAvailableChanged event notifier 传感器发生变化的调用的数据
this.kinectSensor.IsAvailableChanged += this.Sensor_IsAvailableChanged;
// open the sensor 打开传感器
this.kinectSensor.Open();
// set the status text 状态文字
this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
: Properties.Resources.NoSensorStatusText;
// Create the drawing group we'll use for drawing 创建绘画组用来绘画
this.drawingGroup = new DrawingGroup();
// Create an image source that we can use in our image control 用drawinggroup来作为控制器
this.imageSource = new DrawingImage(this.drawingGroup);
// use the window object as the view model in this simple example
this.DataContext = this;
// initialize the components (controls) of the window
this.InitializeComponent();
}
/// <summary>
/// INotifyPropertyChangedPropertyChanged event to allow window controls to bind to changeable data
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Gets the bitmap to display
/// </summary>
public ImageSource ImageSource
{
get
{
return this.imageSource;
}
}
/// <summary>
/// Gets or sets the current status text to display
/// </summary>
public string StatusText
{
get
{
return this.statusText;
}
set
{
if (this.statusText != value)
{
this.statusText = value;
// notify any bound elements that the text has changed
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("StatusText"));
}
}
}
}
/// <summary>
/// Execute start up tasks
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if (this.bodyFrameReader != null)
{
//当有数据到来时调用的函数
this.bodyFrameReader.FrameArrived += this.Reader_FrameArrived;
}
}
/// <summary>
/// Execute shutdown tasks
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
if (this.bodyFrameReader != null)
{
// BodyFrameReader is IDisposable
this.bodyFrameReader.Dispose();
this.bodyFrameReader = null;
}
if (this.kinectSensor != null)
{
this.kinectSensor.Close();
this.kinectSensor = null;
}
}
/// <summary>
/// Handles the body frame data arriving from the sensor
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
/// 处理获取到的数据
private void Reader_FrameArrived(object sender, BodyFrameArrivedEventArgs e)
{
//是否获取到了数据
bool dataReceived = false;
using (BodyFrame bodyFrame = e.FrameReference.AcquireFrame())
{
if (bodyFrame != null)
{
//判断身体数据是否为空
if (this.bodies == null)
{
this.bodies = new Body[bodyFrame.BodyCount];
}
// The first time GetAndRefreshBodyData is called, Kinect will allocate each Body in the array.
// As long as those body objects are not disposed and not set to null in the array,
// those body objects will be re-used.
//获取并刷新数据
bodyFrame.GetAndRefreshBodyData(this.bodies);
dataReceived = true;
}
}
if (dataReceived)
{
using (DrawingContext dc = this.drawingGroup.Open())
{
// Draw a transparent background to set the render size
//初始时绘制一个背景
dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, this.displayWidth, this.displayHeight));
int penIndex = 0;
//遍历每一个身体(遍历每一个人)
foreach (Body body in this.bodies)
{
//每个人一种颜色
Pen drawPen = this.bodyColors[penIndex++];
//身体是否跟踪到了
if (body.IsTracked)
{
//绘制修剪边缘(就是当要失去跟踪的时候显示红框
this.DrawClippedEdges(body, dc);
//红外传感器的的关节数据
IReadOnlyDictionary<JointType, Joint> joints = body.Joints;
// convert the joint points to depth (display) space
//吧关节点转换为深度的数据
Dictionary<JointType, Point> jointPoints = new Dictionary<JointType, Point>();
//获取每一个关节
foreach (JointType jointType in joints.Keys)
{
// sometimes the depth(Z) of an inferred joint may show as negative 深度可能为负数
// clamp down to 0.1f to prevent coordinatemapper from returning (-Infinity, -Infinity)
// 钳位到0.1f以防止坐标映射器返回(-Infinity,-Infinity)---有点看不懂。。。,应该是避免z轴为负的时候的处理时间
CameraSpacePoint position = joints[jointType].Position;
if (position.Z < 0)
{
position.Z = InferredZPositionClamp;
}
//深度点数据
DepthSpacePoint depthSpacePoint = this.coordinateMapper.MapCameraPointToDepthSpace(position);
jointPoints[jointType] = new Point(depthSpacePoint.X, depthSpacePoint.Y);
}
//绘制身体
this.DrawBody(joints, jointPoints, dc, drawPen);
//绘制手
this.DrawHand(body.HandLeftState, jointPoints[JointType.HandLeft], dc);
this.DrawHand(body.HandRightState, jointPoints[JointType.HandRight], dc);
}
}
// prevent drawing outside of our render area
//防止在渲染区域之外绘制
this.drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, this.displayWidth, this.displayHeight));
}
}
}
/// <summary>
/// Draws a body
/// </summary>
/// <param name="joints">joints to draw</param>
/// <param name="jointPoints">translated positions of joints to draw</param>
/// <param name="drawingContext">drawing context to draw to</param>
/// <param name="drawingPen">specifies color to draw a specific body</param>
/// 绘制身体
private void DrawBody(IReadOnlyDictionary<JointType, Joint> joints, IDictionary<JointType, Point> jointPoints, DrawingContext drawingContext, Pen drawingPen)
{
// Draw the bones
//绘制骨头
foreach (var bone in this.bones)
{
this.DrawBone(joints, jointPoints, bone.Item1, bone.Item2, drawingContext, drawingPen);
}
// Draw the joints
//绘制关节
foreach (JointType jointType in joints.Keys)
{
Brush drawBrush = null;
TrackingState trackingState = joints[jointType].TrackingState;
//判断关节的跟踪状态
if (trackingState == TrackingState.Tracked)
{
drawBrush = this.trackedJointBrush;
}
else if (trackingState == TrackingState.Inferred)
{
drawBrush = this.inferredJointBrush;
}
if (drawBrush != null)
{
//绘制关节(绘制点
drawingContext.DrawEllipse(drawBrush, null, jointPoints[jointType], JointThickness, JointThickness);
}
}
}
/// <summary>
/// Draws one bone of a body (joint to joint)
/// </summary>
/// <param name="joints">joints to draw</param>
/// <param name="jointPoints">translated positions of joints to draw</param>
/// <param name="jointType0">first joint of bone to draw</param>
/// <param name="jointType1">second joint of bone to draw</param>
/// <param name="drawingContext">drawing context to draw to</param>
/// /// <param name="drawingPen">specifies color to draw a specific bone</param>
/// 绘制骨头
private void DrawBone(IReadOnlyDictionary<JointType, Joint> joints, IDictionary<JointType, Point> jointPoints, JointType jointType0, JointType jointType1, DrawingContext drawingContext, Pen drawingPen)
{
Joint joint0 = joints[jointType0];
Joint joint1 = joints[jointType1];
// If we can't find either of these joints, exit
//如果无法发现下面的关节就退出
if (joint0.TrackingState == TrackingState.NotTracked ||
joint1.TrackingState == TrackingState.NotTracked)
{
return;
}
// We assume all drawn bones are inferred unless BOTH joints are tracked
//我们假设所有的关节都是推断出的,除非同时跟踪两个关节
Pen drawPen = this.inferredBonePen;
if ((joint0.TrackingState == TrackingState.Tracked) && (joint1.TrackingState == TrackingState.Tracked))
{
drawPen = drawingPen;
}
//这里就是绘制直线了
drawingContext.DrawLine(drawPen, jointPoints[jointType0], jointPoints[jointType1]);
}
/// <summary>
/// Draws a hand symbol if the hand is tracked: red circle = closed, green circle = opened; blue circle = lasso
/// </summary>
/// <param name="handState">state of the hand</param>
/// <param name="handPosition">position of the hand</param>
/// <param name="drawingContext">drawing context to draw to</param>
/// 绘制手
private void DrawHand(HandState handState, Point handPosition, DrawingContext drawingContext)
{
//手的状态不同就绘制不同的内容
switch (handState)
{
case HandState.Closed:
//手闭合和打开都是显示不同的状态
drawingContext.DrawEllipse(this.handClosedBrush, null, handPosition, HandSize, HandSize);
break;
case HandState.Open:
drawingContext.DrawEllipse(this.handOpenBrush, null, handPosition, HandSize, HandSize);
break;
case HandState.Lasso:
drawingContext.DrawEllipse(this.handLassoBrush, null, handPosition, HandSize, HandSize);
break;
}
}
/// <summary>
/// Draws indicators to show which edges are clipping body data
/// </summary>
/// <param name="body">body to draw clipping information for</param>
/// <param name="drawingContext">drawing context to draw to</param>
/// 绘制椭圆
private void DrawClippedEdges(Body body, DrawingContext drawingContext)
{
FrameEdges clippedEdges = body.ClippedEdges;
if (clippedEdges.HasFlag(FrameEdges.Bottom))
{
drawingContext.DrawRectangle(
Brushes.Red,
null,
new Rect(0, this.displayHeight - ClipBoundsThickness, this.displayWidth, ClipBoundsThickness));
}
if (clippedEdges.HasFlag(FrameEdges.Top))
{
drawingContext.DrawRectangle(
Brushes.Red,
null,
new Rect(0, 0, this.displayWidth, ClipBoundsThickness));
}
if (clippedEdges.HasFlag(FrameEdges.Left))
{
drawingContext.DrawRectangle(
Brushes.Red,
null,
new Rect(0, 0, ClipBoundsThickness, this.displayHeight));
}
if (clippedEdges.HasFlag(FrameEdges.Right))
{
drawingContext.DrawRectangle(
Brushes.Red,
null,
new Rect(this.displayWidth - ClipBoundsThickness, 0, ClipBoundsThickness, this.displayHeight));
}
}
/// <summary>
/// Handles the event which the sensor becomes unavailable (E.g. paused, closed, unplugged).
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void Sensor_IsAvailableChanged(object sender, IsAvailableChangedEventArgs e)
{
// on failure, set the status text
this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
: Properties.Resources.SensorNotAvailableStatusText;
}
}
}