`
什么世道
  • 浏览: 222460 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

Kinect开发之获取骨骼关节数据并绘制成火柴人

阅读更多

   上篇博文已经介绍了用Kinect获取彩色摄像头数据,当然,深景摄像头数据是一样的做法,获取图像数据的处理与其他颜色数据的处理大同小异,在此就不展开说明。本博文主要讨论用Kinect获取骨骼关节数据并绘制成图形。

    Kinect SDK 中骨骼追踪有一些和其他对象不一样的对象结构和枚举。 在 SDK 中骨骼追踪相关的内容几乎占据了三分之一的内容,可见 Kinect 中骨骼追踪技术的重要性。下图展示了骨骼追踪系统中涉及到的一些主要的对象模型。有四个最主要的对象,他们 是SkeletonStream,SkeletonFrame,Skeleton 和 Joint 。下面将介绍这四个对象。



 

 

 

1 SkeletonStream 对象

SkeletonStream 对象 产生 SkeletonFrame 。从 SkeletonStream 获取 骨骼帧 数据和 从ColorStream 及 DepthStream 中获取数据类似。可以注册 SkeletonFrameReady 事件或 者AllFramesReady 事件通过事件模型来获取数据,或者是使用 OpenNextFrame 方法通过“ 拉”模型来获取数据。不能对同一个 SkeletonStream 同时使用这两种模式。如果注册 了SkeletonFrameReady 事 件 然 后 又 调 用 OpenNextFrame 方 法 将 会 返 回 一 个InvalidOperationException 异常。

 

/// <summary>
/// Active Kinect sensor
/// </summary>
private KinectSensor sensor;
this.sensor.SkeletonStream.Enable();

 

 

2 SkeletonFrame

SkeletonStream 产生 SkeletonFrame 对象。 可以使用事 件模型从事 件参数中调 用OpenSkeletonFrame 方法来获取 SkeletonFrame 对象,或者采用”拉” 模型调用 SkeletonStream的 OpenNextFrame 来获取 SkeletonFrame 对象。 SkeletonFrame 对象会存储骨骼数据一段时间。同以通过调用 SkeletonFrame 对象的 CopySkeletonDataTo 方法将其保存的数据拷贝到骨骼对象数组中。SkeletonFrame 对象有一个 SkeletonArrayLength 的属性,这个属性表示追踪到的骨骼信息的个数。

 

//Open the Skeleton frame 
using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
{
    // check that a frame is available  
    if (skeletonFrame != null)
    {
       // get the skeleton data
       skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
        // get the skeletal information in this frame  
        skeletonFrame.CopySkeletonDataTo(skeletons);
     }
}
 

 

 

 

 

3 Skeleton

Skeleton 类定义了一系列字段来描述骨骼信息, 包括描述骨骼的位置以及骨骼中关节可能的位置信息。骨骼数据可以通过调用 SkeletonFrame 对象的 CopySkeletonDataTo 方法获得Skeleton 数组。CopySkeletonDataTo 方法有一些不可预料的行为,可能会影响内存使用和其引用的骨骼数组对象。产生的每一个骨骼数组对象数组都是唯一的。

例如:

 

Skeleton [] skeletonA = new Skeleton [frame.SkeletonArrayLength];
Skeleton [] skeletonB = new Skeleton [frame.SkeletonArrayLength];
frame.CopySkeletonDataTo(skeletonA);
frame.CopySkeletonDataTo(skeletonB);
Boolean resultA = skeletonA[0] == skeletonB[0]; //false
Boolean resultB = skeletonA[0].TrackingId == skeletonB[0].TrackingId;//true

 上面的代码可以看出, 使用 CopySkeletonDataTo 是深拷贝对象, 会产生两个不同的 Skeleton

 

数组对象。

 

几个主要的属性:TrackingID,TrackingState, Position,ClippedEdges,Joints 

 

 

TrackingID

骨骼追踪引擎对于每一个追踪到的游戏者的骨骼信息都有一个唯一编号。当 Kinect 追踪到了一个新的游戏者,他会为其分配一个新的唯一编号,编号值为0表示这个骨骼信息不是游戏者的,他在集合中仅仅是一

个占位符。

 

 

TrackingState

该字段表示当前的骨骼数据的状态。下表展示了 SkeletonTrackingState 枚举的可能值机

器含义:

 




 
 Position

Position 一个 SkeletonPoint 类型的字段,代表所有骨骼的中间点。身体的中间点和脊柱

关节的位置相当。

 

 

ClippedEdges

ClippedEdges 字段用来描述追踪者的身体哪部分位于 Kinect 的视野范围外。 他大体上提

供了一个追踪这的位置信息。下面列出了 FrameEdges 的所有可能的值。



 

 

Joints 

 

每一个骨骼对象都有一个 Joints 字段。 该字段是一个 JointsCollection 类型, 它存储了一些列

的 Joint 结构来描述骨骼中可追踪的关节点( 如 head,hands,elbow 等等 ) 。应用程序使用

JointsCollection 索引获取特定的关节点, 并通过节点的 JointType 枚举来过滤指定的关节点。

即使 Kinect 视场中没有游戏者 Joints 对象也被填充。

 

 

 

 

 

4 Joint

骨骼追踪引擎能够跟踪和获取每个用户的近20个点或者关节点信息。 追踪的数据以关节点数

据展现,它有三个属性。JointType 属性是一个枚举类型。下图描述了可追踪的所有关节点。

Kinect神图:



 每一个关节点都有类型为 SkeletonPoint 的 Position 属性,他通过 X,Y,Z 三个值来描述关节点的控件位置。 X,Y 值是相对于骨骼平面空间的位置,他和深度影像, 彩色影像的空间坐标系不一样。KinectSnesor 对象有一些列的坐标转换方法,可以将骨骼坐标点转换到对应的深度数据影像中去。 最后每一个 Skeleton 对象还有一个 JointTrackingState 属性, 他描述了该关节点的跟踪状态及方式,下面列出了所有的可能值。

 


 

 获取骨骼数据后,就可以绘制火柴人啦,逻辑很简单,将每个关节作一个一个节点,按照人体结构,将具有相对旋转运动的关节运动连接起来就OK啦

 

首先,我我们要获取Kinect传感器中的骨骼数据。步骤如下

搜寻一台可用的Kinect sensor

 

foreach (var potentialSensor in KinectSensor.KinectSensors)
 {
     if (potentialSensor.Status == KinectStatus.Connected)
     {
         this.sensor = potentialSensor;
         break;
     }
 }
 
 启用骨骼数据流
this.sensor. SkeletonStream.Enable()
 
 注册FrameReady处理函数
this.sensor.SkeletonFrameReady += this.Sensor.SkeletonFrameReady;
 开始捕获数据 
try
       {
           this.sensor.Start();
       }
       catch (IOException)
       {
           this.sensor = null;
       }
 

然后,要将获得的骨骼对象关节点的信息转换成UI绘图坐标系统的坐标,定义一个方法以骨骼作为参数,然后调用 KinectSensor 对象的 CoordinateMapper.MapSkeletonPointToDepth 方法将骨骼坐标转换到深度影像坐标上去。因为骨骼坐标系和深度坐标及彩色影像坐标系不一样,甚至和 UI 界面上的坐标系不一样。

 

 

SkeletonPointToScreen方法的目的就是将骨骼关节点转换到 UI 绘图坐标系统,返回该骨骼关节点在 UI 上的位置

 

 

        /// <summary>
        /// Maps a SkeletonPoint to lie within our render space and converts to Point
        /// </summary>
        /// <param name="skelpoint">point to map</param>
        /// <returns>mapped point</returns>
        private Point SkeletonPointToScreen(SkeletonPoint skelpoint)
        {
            // Convert point to depth space.  
            // We are not using depth directly, but we do want the points in our 640x480 output resolution.
            DepthImagePoint depthPoint = this.sensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skelpoint, DepthImageFormat.Resolution640x480Fps30);
            return new Point(depthPoint.X, depthPoint.Y);
        }
 

 

 

然后定义一个画骨骼的方法DrawBone,以骨骼对象,画布,和2个关节点为参数。关节点之间连线

 

        /// <summary>
        /// Draws a bone line between two joints
        /// </summary>
        /// <param name="skeleton">skeleton to draw bones from</param>
        /// <param name="g">drawing context to draw to</param>
        /// <param name="jointType0">joint to start drawing from</param>
        /// <param name="jointType1">joint to end drawing at</param>
        private void DrawBone(Skeleton skeleton, Graphics g, JointType jointType0, JointType jointType1)
        {
            Joint joint0 = skeleton.Joints[jointType0];
            Joint joint1 = skeleton.Joints[jointType1];

            // If we can't find either of these joints, exit
            if (joint0.TrackingState == JointTrackingState.NotTracked ||
                joint1.TrackingState == JointTrackingState.NotTracked)
            {
                return;
            }

            // Don't draw if both points are inferred
            if (joint0.TrackingState == JointTrackingState.Inferred &&
                joint1.TrackingState == JointTrackingState.Inferred)
            {
                return;
            }

            // We assume all drawn bones are inferred unless BOTH joints are tracked
            Pen drawPen = this.inferredBonePen;
            if (joint0.TrackingState == JointTrackingState.Tracked && joint1.TrackingState == JointTrackingState.Tracked)
            {
                drawPen = this.trackedBonePen;
            }

            g.DrawLine(drawPen, this.SkeletonPointToScreen(joint0.Position), this.SkeletonPointToScreen(joint1.Position));
        }
 

 

再然后就是火柴人啦,定义一个画火柴人的方法DrawBonesAndJoints,以骨骼对象作为参数,将有关联运动的关节点调用DrawBone方法连接起来,并在每个关节点上画一个圆。

 

        /// <summary>
        /// Draws a skeleton's bones and joints
        /// </summary>
        /// <param name="skeleton">skeleton to draw</param>
        /// <param name="g">Graphics to draw to</param>
        private void DrawBonesAndJoints(Skeleton skeleton, Graphics g)
        {
            // Render Torso
            this.DrawBone(skeleton, g, JointType.Head, JointType.ShoulderCenter);
            this.DrawBone(skeleton, g, JointType.ShoulderCenter, JointType.ShoulderLeft);
            this.DrawBone(skeleton, g, JointType.ShoulderCenter, JointType.ShoulderRight);
            this.DrawBone(skeleton, g, JointType.ShoulderCenter, JointType.Spine);
            this.DrawBone(skeleton, g, JointType.Spine, JointType.HipCenter);
            this.DrawBone(skeleton, g, JointType.HipCenter, JointType.HipLeft);
            this.DrawBone(skeleton, g, JointType.HipCenter, JointType.HipRight);

            // Left Arm
            this.DrawBone(skeleton, g, JointType.ShoulderLeft, JointType.ElbowLeft);
            this.DrawBone(skeleton, g, JointType.ElbowLeft, JointType.WristLeft);
            this.DrawBone(skeleton, g, JointType.WristLeft, JointType.HandLeft);

            // Right Arm
            this.DrawBone(skeleton, g, JointType.ShoulderRight, JointType.ElbowRight);
            this.DrawBone(skeleton, g, JointType.ElbowRight, JointType.WristRight);
            this.DrawBone(skeleton, g, JointType.WristRight, JointType.HandRight);

            // Left Leg
            this.DrawBone(skeleton, g, JointType.HipLeft, JointType.KneeLeft);
            this.DrawBone(skeleton, g, JointType.KneeLeft, JointType.AnkleLeft);
            this.DrawBone(skeleton, g, JointType.AnkleLeft, JointType.FootLeft);

            // Right Leg
            this.DrawBone(skeleton, g, JointType.HipRight, JointType.KneeRight);
            this.DrawBone(skeleton, g, JointType.KneeRight, JointType.AnkleRight);
            this.DrawBone(skeleton, g, JointType.AnkleRight, JointType.FootRight);
 
            // Render Joints
            foreach (Joint joint in skeleton.Joints)
            {
                Pen drawPen = null;

                if (joint.TrackingState == JointTrackingState.Tracked)
                {
                    drawPen = this.trackedJointPen;                    
                }
                else if (joint.TrackingState == JointTrackingState.Inferred)
                {
                    drawPen = this.inferredJointPen;                    
                }

                if (drawPen != null)
                {
                    g.DrawEllipse(drawPen,this.SkeletonPointToScreen(joint.Position).X, this.SkeletonPointToScreen(joint.Position).Y, (int)JointThickness, (int)JointThickness);
                }
            }
        }
 

 

 

 

最后就是调用画火柴人的方法了,为了界面不闪烁,将动作分解为位图一张张地画在窗体上。

 

//Initialize background panel
Image myImage = new Bitmap(panel1.Width, panel1.Height);
// Create the Graphics we'll use for drawing
Graphics g = Graphics.FromImage(myImage);

if (skeletons.Length != 0)
                {
                    foreach (Skeleton skel in skeletons)
                    {

                        if (skel.TrackingState == SkeletonTrackingState.Tracked)
                        {
                            g.Clear(this.panel1.BackColor);
                            this.DrawBonesAndJoints(skel, g);
                            //为了界面不闪,将动作以一张张位图的形式显现
                            Graphics gg = panel1.CreateGraphics();
                            gg.DrawImage(myImage, 0, 0);
                        }
                        else if (skel.TrackingState == SkeletonTrackingState.PositionOnly)
                        {
                            g.DrawEllipse(
                            this.centerPointPen,
                            this.SkeletonPointToScreen(skel.Position).X, this.SkeletonPointToScreen(skel.Position).Y,
                            BodyCenterThickness,
                            BodyCenterThickness);
                            Graphics gg = panel1.CreateGraphics();
                            gg.DrawImage(myImage, 0, 0);
                        }
                    }
                }
            }

 

运行示例:



 

 

友情提示:该项目需要有Kinect SDK的支持才能运行。

Kinect SDK下载地址:http://www.microsoft.com/en-us/kinectforwindowsdev/default.aspx

 

程序所有源代码和项目已打包上传到:
 

下一篇博客分析Kinect的简单姿势识别,

传送门:http://yacare.iteye.com/blog/1950112

 

希望大家多多支持和指正。

 

 

Kinect开发系列博文: 

Kinect开发之 Interaction交互设计  
 
Kinect开发之结合Unity3D进行游戏应用开发
 
Kinect开发之体感举起手来程序设计(Kinect俄罗斯方块)
 
Kinect开发之简单姿势识别
 
Kinect开发之获取骨骼关节数据并绘制成火柴人
 
Kinect开发之获取彩色摄像头数据 
  • 大小: 497.3 KB
  • 大小: 14.7 KB
  • 大小: 9.1 KB
  • 大小: 10.7 KB
  • 大小: 108.4 KB
  • 大小: 16 KB
分享到:
评论

相关推荐

    Kinect骨骼关节火柴人程序

    本资源与Blog《Kinect开发之获取骨骼关节数据并绘制成图形》配套。 如果您下载到了本资源。请去blog上看原文,方便一起学习交流。 本资源对应博文地址为: http://yacare.iteye.com/blog/1950085

    Kinect开发之获取彩色摄像头数据

    【标题】"Kinect开发之获取彩色摄像头数据"涉及到的是微软Kinect设备的编程应用,主要关注如何通过软件开发获取并处理来自Kinect彩色摄像头的数据。Kinect是一款体感输入设备,广泛应用于游戏、交互式设计和计算机...

    Kinect 2.0 骨头获取

    接着,为了获取骨骼数据,你需要初始化 Kinect 设备并开启骨骼跟踪。这通常在应用程序的启动或窗口加载事件中完成。通过 `Microsoft.Kinect.KinectSensor` 类,你可以访问设备并设置骨骼跟踪器。注意,由于 Kinect ...

    Kinect与Unity结合的人体骨骼控制方法_乐小燕

    该方法首先使用Kinect设备来获取人体骨骼关节点数据,然后在Unity中建立Avatar系统,将三维人物模型与Kinect骨骼关节点相对应。最后,使用者骨骼与Unity3D里的人物模型的骨骼节点一一对应,使人物模型的运动轨迹与...

    Kinect2.0+Opencv人体骨骼捕捉及绘制VisualStudio2015工程文件

    在这个项目中,OpenCV将用于接收并解析由Kinect 2.0获取的骨骼数据,并在屏幕上实时绘制出人体骨骼的图像。这通常涉及到图像处理、图形绘制和可能的坐标转换等技术。 在描述中提到,项目提供的是一个已经配置好的...

    Kinectv2_深度图和骨骼图源码

    Kinectv2是一款由微软开发的体感设备,它能够捕获用户的身体动作并生成深度图像和骨骼图,为开发者提供了一种新颖的人机交互方式。本文将深入探讨如何使用C++语言结合OpenCV库来处理和显示由Kinectv2获取的深度图和...

    kinect获取深度和彩色数据并保存

    标题“kinect获取深度和彩色数据并保存”指的是利用微软的Kinect设备,通过编程方式获取其输出的深度图像和彩色图像数据,并将其保存到本地文件中。这通常涉及到计算机视觉、传感器技术以及图像处理等领域。 Kinect...

    c#实现的kinect2.0骨骼检测,通过wpf界面进行展现

    c#实现的kinect2.0骨骼检测,通过wpf界面进行展现,可供大家参考

    kinect 骨骼位置信息输出

    2. 获取骨骼数据:通过调用Kinect SDK提供的API,如` SkeletonFrame`类,你可以获取到当前帧的骨骼数据。这个数据通常以数组形式存在,包含了所有检测到的人体骨骼信息。 3. 定义输出顺序:根据需求,你可以定义一...

    KinectSDK2.0骨骼帧与笑面男

    例如,`WindowsPreview::Kinect::Input::KinectSensor` 类是与Kinect设备交互的基础,通过这个类可以打开设备并获取骨骼帧数据。 骨骼帧数据是以人体模型为基础,包括了25个关键关节(如头、肩、肘、腕、髋、膝、...

    kinect图像获取+骨架识别+MFC控制程序

    标题中的“kinect图像获取+骨架识别+MFC控制程序”指的是一个使用Microsoft Kinect设备,通过MFC(Microsoft Foundation Classes)框架实现的C++应用程序,该程序能够捕获Kinect的图像数据,并进行骨架识别。...

    KinectSkeletonViewer.rar_KINECT 坐标_Skeleton Viewer 3.6_kinect 骨骼

    标题中的“KinectSkeletonViewer.rar”是一个压缩包文件,它包含了一个名为“KinectSkeletonViewer”的项目或应用程序,专门用于展示KINECT设备获取的骨骼数据。KINECT是微软开发的一款体感输入设备,能够捕捉人体...

    2010中基于MFC实现Kinect彩色和深度及骨骼数据显示

    在本文中,我们将深入探讨如何在Visual Studio 2010中使用Microsoft Foundation Class (MFC)库来实现Kinect的彩色、深度以及骨骼数据的显示。MFC是微软为Windows应用程序开发提供的一种C++类库,它简化了窗口、菜单...

    SkeletonBasics-D2D.rar_kinect_kinect骨骼

    标题中的"SkeletonBasics-D2D.rar"是一个与Kinect传感器相关的项目,主要涉及的是如何通过Kinect设备获取并处理人体骨骼数据。"kinect_kinect骨骼"标签进一步明确了这个项目的焦点是处理和理解Kinect捕获的人体骨骼...

    OpenNI的Kinect骨骼追踪程序

    5. 解析骨骼数据:获取每个关节的3D坐标,通常是以毫米为单位,这些数据可以实时地更新并反映用户的动作。 此外,开发者还需要考虑一些实际问题,如多人追踪、追踪稳定性、骨骼漂移等。OpenNI提供了多种优化和调整...

    Kinect开发教程.pdf

    10. 在Kinect开发的过程中,开发者要了解Kinect传感器的工作原理和数据结构,这样才能有效地处理和分析捕捉到的数据,并将其转换为应用程序能够使用的控制信号或输入数据。 11. 开发者在使用Kinect时,还需要注意...

    kinect 通过opencv显示骨骼图像

    2. Kinect开发:理解其工作原理,获取并解析骨骼数据。 3. 数据平滑技术:了解并实现一种或多种滤波算法来优化骨骼跟踪的稳定性。 4. 图形渲染:在OpenCV窗口中绘制骨骼关节,可能用到了点、线和颜色的表示方法。 5....

    kinect骨骼识别

    Kinect骨骼识别技术是微软Kinect设备的一项核心功能,它允许设备实时地追踪人体的骨骼运动,为开发者提供了丰富的交互可能性。在C#环境中,利用Kinect SDK(软件开发工具包)可以创建出各种应用程序,如游戏、健身...

    Kinect获取点云数据+点云读取显示代码整合

    ### Kinect点云数据获取与显示知识点详解 #### 一、Kinect简介 Kinect是一种由微软公司开发的基于自然用户界面的技术,最初是为Xbox 360视频游戏主机设计的一种体感输入设备。它能够捕捉人体的动作,并且通过内置的...

Global site tag (gtag.js) - Google Analytics