PageRenderTime 58ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Gui/FMSPlayBack/Content/Kinect/SkeletonStreamRenderer.cs

https://bitbucket.org/david_nthn/kinect_sseh
C# | 297 lines | 150 code | 45 blank | 102 comment | 22 complexity | 6f7a2abc8ea385f1313e9db622718e8f MD5 | raw file
  1. using System.Windows;
  2. using System.Windows.Controls;
  3. using System.Windows.Media;
  4. using Microsoft.Kinect;
  5. using SkeletonReaderWriter;
  6. namespace FMSPlayBack
  7. {
  8. class SkeletonStreamRenderer : UserControl
  9. {
  10. /// <summary>
  11. /// Width of output drawing
  12. /// </summary>
  13. private const float RenderWidth = 640.0f;
  14. /// <summary>
  15. /// Height of our output drawing
  16. /// </summary>
  17. private const float RenderHeight = 480.0f;
  18. /// <summary>
  19. /// Thickness of drawn joint lines
  20. /// </summary>
  21. private const double JointThickness = 3;
  22. /// <summary>
  23. /// Thickness of body center ellipse
  24. /// </summary>
  25. private const double BodyCenterThickness = 10;
  26. /// <summary>
  27. /// Thickness of clip edge rectangles
  28. /// </summary>
  29. private const double ClipBoundsThickness = 10;
  30. /// <summary>
  31. /// Brush used to draw skeleton center point
  32. /// </summary>
  33. private readonly Brush centerPointBrush = Brushes.Blue;
  34. /// <summary>
  35. /// Brush used for drawing joints that are currently tracked
  36. /// </summary>
  37. private readonly Brush trackedJointBrush = new SolidColorBrush(Color.FromArgb(255, 68, 192, 68));
  38. /// <summary>
  39. /// Brush used for drawing joints that are currently inferred
  40. /// </summary>
  41. private readonly Brush inferredJointBrush = Brushes.Yellow;
  42. /// <summary>
  43. /// Pen used for drawing bones that are currently tracked
  44. /// </summary>
  45. private readonly Pen trackedBonePen = new Pen(Brushes.Green, 6);
  46. /// <summary>
  47. /// Pen used for drawing bones that are currently inferred
  48. /// </summary>
  49. private readonly Pen inferredBonePen = new Pen(Brushes.Gray, 1);
  50. /// <summary>
  51. /// Drawing group for skeleton rendering output
  52. /// </summary>
  53. private DrawingGroup drawingGroup;
  54. /// <summary>
  55. /// Drawing image that we will display
  56. /// </summary>
  57. private DrawingImage imageSource;
  58. /// <summary>
  59. /// Image control to show camera data in the user interface
  60. /// </summary>
  61. private Image image = new Image();
  62. /// <summary>
  63. /// Cached skeleton data
  64. /// </summary>
  65. private SkeletonReader.SkeletonFrameData skeleton;
  66. public SkeletonStreamRenderer()
  67. {
  68. // Create the drawing group we'll use for drawing
  69. drawingGroup = new DrawingGroup();
  70. // Create an image source that we can use in our image control
  71. imageSource = new DrawingImage(drawingGroup);
  72. // Display the drawing using our image control
  73. image.Source = imageSource;
  74. //set up a viewbox to handle uniform scaling
  75. Viewbox viewbox = new Viewbox();
  76. viewbox.Stretch = Stretch.Uniform;
  77. viewbox.HorizontalAlignment = HorizontalAlignment.Center;
  78. viewbox.VerticalAlignment = VerticalAlignment.Center;
  79. viewbox.Child = image;
  80. Content = viewbox;
  81. }
  82. /// <summary>
  83. /// Update rendered skeleton
  84. /// </summary>
  85. /// <param name="skeleton">skeleton data to process</param>
  86. public void ProcessSkeletonFrame(SkeletonReader.SkeletonFrameData skeleton)
  87. {
  88. this.skeleton = skeleton;
  89. ProcessSkeletonFrame();
  90. }
  91. /// <summary>
  92. /// Internal update to the rendered skeleton
  93. /// </summary>
  94. private void ProcessSkeletonFrame()
  95. {
  96. using (DrawingContext dc = drawingGroup.Open())
  97. {
  98. // Draw a background to set the render size
  99. dc.DrawRectangle(BackgroundBrush, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight));
  100. if (skeleton != null)
  101. {
  102. DrawBonesAndJoints(skeleton, dc);
  103. }
  104. // prevent drawing outside of our render area
  105. drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight));
  106. }
  107. }
  108. /// <summary>
  109. /// Draws a skeleton's bones and joints
  110. /// </summary>
  111. /// <param name="skeleton">skeleton to draw</param>
  112. /// <param name="drawingContext">drawing context to draw to</param>
  113. private void DrawBonesAndJoints(SkeletonReader.SkeletonFrameData skeleton, DrawingContext drawingContext)
  114. {
  115. // Render Torso
  116. DrawBone(skeleton, drawingContext, JointType.Head, JointType.ShoulderCenter);
  117. DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderLeft);
  118. DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderRight);
  119. DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.Spine);
  120. DrawBone(skeleton, drawingContext, JointType.Spine, JointType.HipCenter);
  121. DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipLeft);
  122. DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipRight);
  123. // Left Arm
  124. DrawBone(skeleton, drawingContext, JointType.ShoulderLeft, JointType.ElbowLeft);
  125. DrawBone(skeleton, drawingContext, JointType.ElbowLeft, JointType.WristLeft);
  126. DrawBone(skeleton, drawingContext, JointType.WristLeft, JointType.HandLeft);
  127. // Right Arm
  128. DrawBone(skeleton, drawingContext, JointType.ShoulderRight, JointType.ElbowRight);
  129. DrawBone(skeleton, drawingContext, JointType.ElbowRight, JointType.WristRight);
  130. DrawBone(skeleton, drawingContext, JointType.WristRight, JointType.HandRight);
  131. // Left Leg
  132. DrawBone(skeleton, drawingContext, JointType.HipLeft, JointType.KneeLeft);
  133. DrawBone(skeleton, drawingContext, JointType.KneeLeft, JointType.AnkleLeft);
  134. DrawBone(skeleton, drawingContext, JointType.AnkleLeft, JointType.FootLeft);
  135. // Right Leg
  136. DrawBone(skeleton, drawingContext, JointType.HipRight, JointType.KneeRight);
  137. DrawBone(skeleton, drawingContext, JointType.KneeRight, JointType.AnkleRight);
  138. DrawBone(skeleton, drawingContext, JointType.AnkleRight, JointType.FootRight);
  139. // Render Joints
  140. foreach (SkeletonReader.SkeletonFrameJoint joint in skeleton.joints)
  141. {
  142. Brush drawBrush = null;
  143. if (joint.trackingState == JointTrackingState.Tracked)
  144. {
  145. drawBrush = trackedJointBrush;
  146. }
  147. else if (joint.trackingState == JointTrackingState.Inferred)
  148. {
  149. drawBrush = inferredJointBrush;
  150. }
  151. if (drawBrush != null)
  152. {
  153. drawingContext.DrawEllipse(drawBrush, null, SkeletonPointToScreen(joint), JointThickness, JointThickness);
  154. }
  155. }
  156. }
  157. /// <summary>
  158. /// Maps a 3D Point to lie within our render space and converts to Point
  159. /// This is done without needing to use a Kinect Sensor
  160. /// </summary>
  161. /// <param name="X">X coordinate to map to depth point</param>
  162. /// <param name="Y">Y coordinate to map to depth point</param>
  163. /// <param name="Z">Z coordinate to map to depth point</param>
  164. /// <returns>mapped point</returns>
  165. private DepthImagePoint MapSkeletonPointToDepthPoint(float X, float Y, float Z)
  166. {
  167. float pfDepthX = 0.5f + X * 285.63f / (Z * 320f);
  168. float pfDepthY = 0.5f - Y * 285.63f / (Z * 240f);
  169. float pfDepthValue = Z * 1000f;
  170. return new DepthImagePoint
  171. {
  172. X = (int)(pfDepthX * 640f + 0.5f),
  173. Y = (int)(pfDepthY * 480f + 0.5f),
  174. Depth = (int)(pfDepthValue + 0.5f)
  175. };
  176. }
  177. /// <summary>
  178. /// Maps a SkeletonPoint to lie within our render space and converts to Point
  179. /// </summary>
  180. /// <param name="joint">joint to get color pos from</param>
  181. /// <returns>mapped point</returns>
  182. private Point SkeletonPointToScreen(SkeletonReader.SkeletonFrameJoint joint)
  183. {
  184. if(joint.ColorPosSupported)
  185. {
  186. return new Point(joint.ColourX, joint.ColourY);
  187. }
  188. //Due to variations between Kinects, it is not valid to map to a Color Point without using the Kinect that generated the skeleton
  189. //As the mapping is going to be off anyway (without the Kinect) and this is a backup anyway,
  190. // we may as well map to depth as the skeleton is calculated off of the depth camera
  191. DepthImagePoint depth = MapSkeletonPointToDepthPoint(joint.X, joint.Y, joint.Z);
  192. return new Point(depth.X, depth.Y);
  193. }
  194. /// <summary>
  195. /// Draws a bone line between two joints
  196. /// </summary>
  197. /// <param name="skeleton">skeleton to draw bones from</param>
  198. /// <param name="drawingContext">drawing context to draw to</param>
  199. /// <param name="jointType0">joint to start drawing from</param>
  200. /// <param name="jointType1">joint to end drawing at</param>
  201. private void DrawBone(SkeletonReader.SkeletonFrameData skeleton, DrawingContext drawingContext, JointType jointType0, JointType jointType1)
  202. {
  203. SkeletonReader.SkeletonFrameJoint joint0 = skeleton.joints[(int)jointType0];
  204. SkeletonReader.SkeletonFrameJoint joint1 = skeleton.joints[(int)jointType1];
  205. // If we can't find either of these joints, exit
  206. if (joint0.trackingState == JointTrackingState.NotTracked ||
  207. joint1.trackingState == JointTrackingState.NotTracked)
  208. {
  209. return;
  210. }
  211. // Don't draw if both points are inferred
  212. if (joint0.trackingState == JointTrackingState.Inferred &&
  213. joint1.trackingState == JointTrackingState.Inferred)
  214. {
  215. return;
  216. }
  217. // We assume all drawn bones are inferred unless BOTH joints are tracked
  218. Pen drawPen = inferredBonePen;
  219. if (joint0.trackingState == JointTrackingState.Tracked
  220. && joint1.trackingState == JointTrackingState.Tracked)
  221. {
  222. drawPen = trackedBonePen;
  223. }
  224. drawingContext.DrawLine(drawPen, SkeletonPointToScreen(joint0), SkeletonPointToScreen(joint1));
  225. }
  226. /// <summary>
  227. /// Background to use
  228. /// </summary>
  229. public Brush BackgroundBrush
  230. {
  231. get { return (Brush)GetValue(BackgroundBrushProperty); }
  232. set { SetValue(BackgroundBrushProperty, value); }
  233. }
  234. public static readonly DependencyProperty BackgroundBrushProperty = DependencyProperty.Register("BackgroundBrush", typeof(Brush), typeof(SkeletonStreamRenderer), new PropertyMetadata(Brushes.Transparent, OnBackgroundBrushChanged));
  235. /// <summary>
  236. /// Handle changes to the BackgroundBrush
  237. /// </summary>
  238. /// <param name="sender">object sending the event</param>
  239. /// <param name="args">event arguments</param>
  240. private static void OnBackgroundBrushChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
  241. {
  242. //sender should be this class
  243. var renderer = sender as SkeletonStreamRenderer;
  244. //if not, ignore it
  245. if (null == renderer)
  246. {
  247. return;
  248. }
  249. renderer.ProcessSkeletonFrame();
  250. }
  251. }
  252. }