none
Why is SkeletonFrame sealed with no constructor? RRS feed

  • Question

  • Is there a reason why Microsoft.Research.Kinect.Nui.SkeletonFrame is both sealed and has no constructor defined? This makes it very hard (if not impossible without TypeMock) to unit test code that is processing the SkeletonFrameReady event.

    Is there any other means of simulating this event? Do we really have to have actual hardware in use to test code?

    Sunday, June 19, 2011 9:13 PM

Answers

  • We probably should not have used a sealed class for this type (see, for example, http://blogs.msdn.com/b/brada/archive/2009/01/19/framework-design-guidelines-sealed-classes.aspx). We should evolve our approach in a future revision to the API, because this is an important scenario that we care about.

    In the meantime, here's a technique you can use with the current beta to enable mocking of the skeleton data:

    You can define simple interfaces corresponding to each of the relevant types. Then, you can implement a straightforward wrapper class for the Kinect implementation of SkeletonFrame. Separately, you can implement your own MockSkeletonFrame that implements the same interfaces.

    Here's one possible way to define your interfaces:


    public interface ISkeletonFrame
    {
        Int64 TimeStamp { get; }
        int FrameNumber { get; }
        SkeletonFrameQuality Quality { get; }
        Vector FloorClipPlane { get; }
        Vector NormalToGravity { get; }
        ISkeletonCollection Skeletons { get; }
    }

    public interface ISkeletonCollection : IEnumerable
    {
        int Count { get; }
        ISkeletonData this[int index] { get; }
        new IEnumerator GetEnumerator();
    }

    public interface ISkeletonData
    {
        SkeletonTrackingState TrackingState { get; }
        int TrackingID { get; }
        int EnrollmentIndex { get; }
        int UserIndex { get; }
        Vector Position { get; }
        IJointsCollection Joints { get; }
        SkeletonQuality Quality { get; }
    }

    public interface IJointsCollection : IEnumerable
    {
        int Count { get; }
        Joint this[JointID i] { get; }
        new IEnumerator GetEnumerator();
    }

    Then, your wrapper for the Kinect implementation would be:


    public class KinectSkeletonFrame : ISkeletonFrame
    {
        public KinectSkeletonFrame(SkeletonFrame frame) { _frame = frame; }
        public Int64 TimeStamp { get { return _frame.TimeStamp; } }
        public int FrameNumber { get { return _frame.FrameNumber; } }
        public SkeletonFrameQuality Quality { get { return _frame.Quality; } }
        public Vector FloorClipPlane { get { return _frame.FloorClipPlane; } }
        public Vector NormalToGravity { get { return _frame.NormalToGravity; } }
        public ISkeletonCollection Skeletons { get { return new KinectSkeletonCollection(_frame.Skeletons); } }
        private SkeletonFrame _frame;

        private class KinectSkeletonCollection : ISkeletonCollection
        {
            public KinectSkeletonCollection(SkeletonData[] skeletons)
            {
                _skeletons = new KinectSkeletonData[skeletons.GetLength(0)];
                int i = 0;
                foreach (SkeletonData skeleton in skeletons)
                {
                    _skeletons[i++] = new KinectSkeletonData(skeleton);
                }
            }
            public int Count { get { return _skeletons.GetLength(0); } }
            public ISkeletonData this[int i] { get { return _skeletons[i]; } }
            public IEnumerator GetEnumerator() { return _skeletons.GetEnumerator(); }
            private KinectSkeletonData[] _skeletons;

            private class KinectSkeletonData : ISkeletonData
            {
                public KinectSkeletonData(SkeletonData data) { _data = data; }
                public SkeletonTrackingState TrackingState { get { return _data.TrackingState; } }
                public int TrackingID { get { return _data.TrackingID; } }
                public int EnrollmentIndex { get { return _data.EnrollmentIndex; } }
                public int UserIndex { get { return _data.UserIndex; } }
                public Vector Position { get { return _data.Position; } }
                public IJointsCollection Joints { get { return new KinectJointsCollection(_data.Joints); } }
                public SkeletonQuality Quality { get { return _data.Quality; } }
                private SkeletonData _data;

                private class KinectJointsCollection : IJointsCollection
                {
                    public KinectJointsCollection(JointsCollection joints) { _joints = joints; }
                    public int Count { get { return _joints.Count; } }
                    public Joint this[JointID i] { get { return _joints[i]; } }
                    public IEnumerator GetEnumerator() { return _joints.GetEnumerator(); }
                    private JointsCollection _joints;
                }
            }
        }
    }

    Next, define your own mock implementation for ISkeletonFrame:


    public class MockSkeletonFrame : ISkeletonFrame
    {
        // ... your mock implementation here ...
    }

    Then, write all of your application's skeleton processing code in terms of ISkeletonFrame instead of SkeletonFrame. When you get a new SkeletonFrameReady event from Kinect, wrap the event's skeleton frame data (i.e., new KinectSkeletonFrame(e.SkeletonFrame)) and pass it along to your app's skeleton processing code. For your own mock skeleton data, create an instance of your MockSkeletonFrame and pass it to the same app code.

    John
    Kinect for Windows Team

     


    Monday, June 20, 2011 11:56 PM

All replies

  • We probably should not have used a sealed class for this type (see, for example, http://blogs.msdn.com/b/brada/archive/2009/01/19/framework-design-guidelines-sealed-classes.aspx). We should evolve our approach in a future revision to the API, because this is an important scenario that we care about.

    In the meantime, here's a technique you can use with the current beta to enable mocking of the skeleton data:

    You can define simple interfaces corresponding to each of the relevant types. Then, you can implement a straightforward wrapper class for the Kinect implementation of SkeletonFrame. Separately, you can implement your own MockSkeletonFrame that implements the same interfaces.

    Here's one possible way to define your interfaces:


    public interface ISkeletonFrame
    {
        Int64 TimeStamp { get; }
        int FrameNumber { get; }
        SkeletonFrameQuality Quality { get; }
        Vector FloorClipPlane { get; }
        Vector NormalToGravity { get; }
        ISkeletonCollection Skeletons { get; }
    }

    public interface ISkeletonCollection : IEnumerable
    {
        int Count { get; }
        ISkeletonData this[int index] { get; }
        new IEnumerator GetEnumerator();
    }

    public interface ISkeletonData
    {
        SkeletonTrackingState TrackingState { get; }
        int TrackingID { get; }
        int EnrollmentIndex { get; }
        int UserIndex { get; }
        Vector Position { get; }
        IJointsCollection Joints { get; }
        SkeletonQuality Quality { get; }
    }

    public interface IJointsCollection : IEnumerable
    {
        int Count { get; }
        Joint this[JointID i] { get; }
        new IEnumerator GetEnumerator();
    }

    Then, your wrapper for the Kinect implementation would be:


    public class KinectSkeletonFrame : ISkeletonFrame
    {
        public KinectSkeletonFrame(SkeletonFrame frame) { _frame = frame; }
        public Int64 TimeStamp { get { return _frame.TimeStamp; } }
        public int FrameNumber { get { return _frame.FrameNumber; } }
        public SkeletonFrameQuality Quality { get { return _frame.Quality; } }
        public Vector FloorClipPlane { get { return _frame.FloorClipPlane; } }
        public Vector NormalToGravity { get { return _frame.NormalToGravity; } }
        public ISkeletonCollection Skeletons { get { return new KinectSkeletonCollection(_frame.Skeletons); } }
        private SkeletonFrame _frame;

        private class KinectSkeletonCollection : ISkeletonCollection
        {
            public KinectSkeletonCollection(SkeletonData[] skeletons)
            {
                _skeletons = new KinectSkeletonData[skeletons.GetLength(0)];
                int i = 0;
                foreach (SkeletonData skeleton in skeletons)
                {
                    _skeletons[i++] = new KinectSkeletonData(skeleton);
                }
            }
            public int Count { get { return _skeletons.GetLength(0); } }
            public ISkeletonData this[int i] { get { return _skeletons[i]; } }
            public IEnumerator GetEnumerator() { return _skeletons.GetEnumerator(); }
            private KinectSkeletonData[] _skeletons;

            private class KinectSkeletonData : ISkeletonData
            {
                public KinectSkeletonData(SkeletonData data) { _data = data; }
                public SkeletonTrackingState TrackingState { get { return _data.TrackingState; } }
                public int TrackingID { get { return _data.TrackingID; } }
                public int EnrollmentIndex { get { return _data.EnrollmentIndex; } }
                public int UserIndex { get { return _data.UserIndex; } }
                public Vector Position { get { return _data.Position; } }
                public IJointsCollection Joints { get { return new KinectJointsCollection(_data.Joints); } }
                public SkeletonQuality Quality { get { return _data.Quality; } }
                private SkeletonData _data;

                private class KinectJointsCollection : IJointsCollection
                {
                    public KinectJointsCollection(JointsCollection joints) { _joints = joints; }
                    public int Count { get { return _joints.Count; } }
                    public Joint this[JointID i] { get { return _joints[i]; } }
                    public IEnumerator GetEnumerator() { return _joints.GetEnumerator(); }
                    private JointsCollection _joints;
                }
            }
        }
    }

    Next, define your own mock implementation for ISkeletonFrame:


    public class MockSkeletonFrame : ISkeletonFrame
    {
        // ... your mock implementation here ...
    }

    Then, write all of your application's skeleton processing code in terms of ISkeletonFrame instead of SkeletonFrame. When you get a new SkeletonFrameReady event from Kinect, wrap the event's skeleton frame data (i.e., new KinectSkeletonFrame(e.SkeletonFrame)) and pass it along to your app's skeleton processing code. For your own mock skeleton data, create an instance of your MockSkeletonFrame and pass it to the same app code.

    John
    Kinect for Windows Team

     


    Monday, June 20, 2011 11:56 PM
  • Thank you for the interfaces/wrappers, that will work in the interim. Hope you're able to evolve those classes a bit to give the mock frameworks some love. This is a very rewarding SDK to play with, keep it coming!
    Wednesday, June 22, 2011 12:38 AM
  •  

    There is a bug in the constructor:

        public KinectSkeletonCollection(SkeletonData[] skeletons)
        {
          _skeletons = new KinectSkeletonData[skeletons.GetLength(0)];
          int i = 0;
          foreach (SkeletonData skeleton in skeletons)
          {
            _skeletons[i++] = new KinectSkeletonData(skeletons[i]);
          }
        }
    

    The value of i will start at 1 and go to 6, but it should be from 0 to 5.

    Therefore it would be better to substitute the following:

          public KinectSkeletonCollection(SkeletonData[] skeletons)
          {
            _skeletons = new KinectSkeletonData[skeletons.GetLength(0)];
            for (int i = 0; i < skeletons.Length; i++)
            {
              _skeletons[i] = new KinectSkeletonData(skeletons[i]);
              i++;
            }
          }
    


    Thursday, July 7, 2011 1:07 PM
  • Won't that proposed fix increment i twice per loop iteration? I think you want either a foreach loop with a single i++ after the copy, or you want the for loop with no i++ in the body.
    Thursday, July 7, 2011 6:19 PM
  • You're right, there was a bug in the original. My intention was:

    _skeletons[i++] = new KinectSkeletonData(skeleton);

    The rest of the loop can remain unchanged.

    I've edited my original response, so nobody else will be tripped up by it. Thanks!

     

    Thursday, July 7, 2011 6:29 PM
  • Hi John,

    since SDK 1.0 is out and I happen to step into that trap... are there any updates on creating SkeletonFrame for mock scenarios...

    For me it looked like it is still sealed with no constructor or internal...

    since with V1.0 there came some changes for example

    Renamed SkeletonQuality -> FrameEdges ,etc...

    Is this still the recommended way to go ... are there any updates or heads up!

    Thanks

    F.

    Saturday, March 24, 2012 12:59 PM
  • SkeletonFrame is still sealed, but Skeleton is not. So you should now be able to mock at least at the level of the skeletons obtained by from SkeletonFrame.CopySkeletonDataTo().

    John
    K4W Dev

    Monday, March 26, 2012 5:26 PM