using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Kinect; using Microsoft.Kinect.Toolkit; using Microsoft.Kinect.Toolkit.Interaction; namespace KinPortal { public class HandsTracker : IInteractionClient { public double InteractionRegionWidth {get; set;} public double InteractionRegionHeight {get; set;} private const int InvalidTrackingId = 0; private readonly KinectSensorChooser sensorChooser = new KinectSensorChooser(); private readonly HashSet trackedUsers = new HashSet(); private InteractionStream interactionStream; private KinectSensor kinectSensor; private Skeleton[] skeletons; private UserInfo[] userInfos; public HandsState LeftHand {get; private set;} public HandsState RightHand {get; private set;} public HandsTracker(double screenWidth, double screenHeight) { InteractionRegionWidth = screenWidth; InteractionRegionHeight = screenHeight; LeftHand = new HandsState(); RightHand = new HandsState(); } public void Run() { this.sensorChooser.KinectChanged += this.OnSensorChanged; this.sensorChooser.Start(); } #region PressAndGripAdjustment public InteractionInfo GetInteractionInfoAtLocation(int skeletonTrackingId, InteractionHandType handType, double x, double y) { return new InteractionInfo(); } #endregion PressAndGripAdjustment #region Configuration private void InitializeInteractions(KinectSensor sensor) { this.skeletons = new Skeleton[sensor.SkeletonStream.FrameSkeletonArrayLength]; this.userInfos = new UserInfo[InteractionFrame.UserInfoArrayLength]; sensor.DepthFrameReady += this.SensorDepthFrameReady; sensor.SkeletonFrameReady += this.SensorSkeletonFrameReady; this.interactionStream = new InteractionStream(sensor, this); this.interactionStream.InteractionFrameReady += this.InteractionFrameReady; } private void UninitializeInteractions(KinectSensor sensor) { sensor.DepthFrameReady -= this.SensorDepthFrameReady; sensor.SkeletonFrameReady -= this.SensorSkeletonFrameReady; this.skeletons = null; this.userInfos = null; this.interactionStream.InteractionFrameReady -= this.InteractionFrameReady; this.interactionStream.Dispose(); this.interactionStream = null; } private void OnSensorChanged(object sender, KinectChangedEventArgs args) { if (args.OldSensor != null) { this.UninitializeInteractions(args.OldSensor); try { args.OldSensor.SkeletonStream.AppChoosesSkeletons = false; args.OldSensor.DepthStream.Range = DepthRange.Default; args.OldSensor.SkeletonStream.EnableTrackingInNearRange = false; args.OldSensor.DepthStream.Disable(); args.OldSensor.SkeletonStream.Disable(); } catch (InvalidOperationException) { // KinectSensor might enter an invalid state while enabling/disabling streams or stream features. // E.g.: sensor might be abruptly unplugged. } } this.kinectSensor = null; if (args.NewSensor != null) { try { // InteractionStream needs 640x480 depth data stream args.NewSensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30); args.NewSensor.SkeletonStream.Enable(); try { // Interactions work better in near range args.NewSensor.DepthStream.Range = DepthRange.Near; args.NewSensor.SkeletonStream.EnableTrackingInNearRange = true; } catch (InvalidOperationException) { // Non Kinect for Windows devices do not support Near mode, so reset back to default mode. args.NewSensor.DepthStream.Range = DepthRange.Default; args.NewSensor.SkeletonStream.EnableTrackingInNearRange = false; } } catch (InvalidOperationException) { // KinectSensor might enter an invalid state while enabling/disabling streams or stream features. // E.g.: sensor might be abruptly unplugged. } this.kinectSensor = args.NewSensor; this.InitializeInteractions(args.NewSensor); } } #endregion Configuration #region Processing private void SensorDepthFrameReady(object sender, DepthImageFrameReadyEventArgs depthImageFrameReadyEventArgs) { if (this.kinectSensor != sender) { return; } using (DepthImageFrame depthFrame = depthImageFrameReadyEventArgs.OpenDepthImageFrame()) { if (null != depthFrame) { try { // Hand data to Interaction framework to be processed this.interactionStream.ProcessDepth(depthFrame.GetRawPixelData(), depthFrame.Timestamp); } catch (InvalidOperationException) { // DepthFrame functions may throw when the sensor gets // into a bad state. Ignore the frame in that case. } } } } private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs skeletonFrameReadyEventArgs) { if (this.kinectSensor != sender) { return; } using (SkeletonFrame skeletonFrame = skeletonFrameReadyEventArgs.OpenSkeletonFrame()) { if (null != skeletonFrame) { try { skeletonFrame.CopySkeletonDataTo(this.skeletons); var accelerometerReading = this.kinectSensor.AccelerometerGetCurrentReading(); this.interactionStream.ProcessSkeleton(this.skeletons, accelerometerReading, skeletonFrame.Timestamp); } catch (InvalidOperationException) { // SkeletonFrame functions may throw when the sensor gets // into a bad state. Ignore the frame in that case. } } } } private void InteractionFrameReady(object sender, InteractionFrameReadyEventArgs e) { // Check for a null userInfos since we may still get posted events // from the stream after we have unregistered our event handler and // deleted our buffers. if (this.userInfos == null) { return; } UserInfo[] localUserInfos = null; long timestamp = 0; using (InteractionFrame interactionFrame = e.OpenInteractionFrame()) { if (interactionFrame != null) { interactionFrame.CopyInteractionDataTo(this.userInfos); timestamp = interactionFrame.Timestamp; localUserInfos = this.userInfos; } } if (localUserInfos != null) { var currentUserSet = new HashSet(); var usersToRemove = new HashSet(); foreach (var info in localUserInfos) { if (info.SkeletonTrackingId == InvalidTrackingId) { // Only look at user information corresponding to valid users continue; } /*if (!this.trackedUsers.Contains(info.SkeletonTrackingId)) { Console.WriteLine("New user '{0}' entered scene at time {1}", info.SkeletonTrackingId, timestamp); }*/ currentUserSet.Add(info.SkeletonTrackingId); this.trackedUsers.Add(info.SkeletonTrackingId); // Perform hit testing and look for Grip and GripRelease events foreach (var handPointer in info.HandPointers) { if (handPointer.HandType.ToString() == "Right") { RightHand.Pos = new Point( (int) ((handPointer.X / 2) * InteractionRegionWidth), (int) ((handPointer.Y / 2) * InteractionRegionHeight) ); RightHand.Depth = handPointer.RawZ; RightHand.PrevIsClosed = RightHand.IsClosed; if(handPointer.HandEventType != InteractionHandEventType.None) { if (handPointer.HandEventType.ToString() == "Grip") RightHand.IsClosed = true; else RightHand.IsClosed = false; } } else { LeftHand.Pos = new Point( (int) ((handPointer.X / 2) * InteractionRegionWidth), (int) ((handPointer.Y / 2) * InteractionRegionHeight) ); LeftHand.Depth = handPointer.RawZ; LeftHand.PrevIsClosed = LeftHand.IsClosed; if (handPointer.HandEventType != InteractionHandEventType.None) { if (handPointer.HandEventType.ToString() == "Grip") LeftHand.IsClosed = true; else LeftHand.IsClosed = false; } } } } foreach (var id in this.trackedUsers) { if (!currentUserSet.Contains(id)) { usersToRemove.Add(id); } } foreach (var id in usersToRemove) { this.trackedUsers.Remove(id); } } } #endregion Processing } }