[聚合文章] 【转】Kinect尝鲜(2)——骨骼识别

c# 2017-03-10 9 阅读

作者自转,原文链接http://blog.csdn.net/nmlh7448...

正文

  上一篇文章简单说了一些关于Kinect彩色图像和深度图像的处理。直接处理彩色数据流和深度数据流来写一些应用是比较困难的,所以微软在SDK中已经封装好了骨骼识别模块。虽说是骨骼识别,其实识别出来的是关节,骨骼可以当作两关节的连线。通过该模块,我们可以轻易获取关于人体各个关节的3D位置及其坐标,藉此可以轻松开发出一些需要体感识别的应用。
  其实骨骼识别更加简单,通过微软的SDK,我们仅仅要做的就是调用。微软这么做的好处就是开发者可以减少花费在底层、图像处理等方面的时间,专注于自己的应用逻辑,快速开发出有趣或实用的应用。
  为了学习骨骼识别,我写了一个仅获取头部位置的一个Demo。为了标记出识别出的头的位置,需要在该坐标位置画点什么,在这里我用了一张滑稽。上一篇显示骨骼图像的colorImage是Image控件,它本身也可以绘图,但效率略低。所以本篇利用Canvas控件。没错,和HTML5中的Canvas画布控件是类似的,我们可以在该画布上绘制彩色图像和滑稽。在窗体上加入画布控件后,为画布加上Image和Ellipse子控件。至于为什么用Ellipse,因为滑稽是圆的……修改过后的代码如下。

<Canvas HorizontalAlignment="Left" Height="480" VerticalAlignment="Top" Width="640">  
    <Image x:Name="colorImage" HorizontalAlignment="Left"
      Height="480" VerticalAlignment="Top" Width="640"/>  
    <Ellipse x:Name="ellipse" Height="48" Canvas.Left="10" Canvas.Top="10" Width="48"/>  
</Canvas>  

  .cs 文件中,首先要声明一个 Skeleton[] 类,是一个 Skeleton 的数组。Skeleton是骨骼类,该类封装了识别出的一个人的所有骨骼信息。由于视野中可能有多个人,每一个人的骨骼信息被保存在了一个Skeleton中,所以此处声明为数组。Kinect及其SDK已经把相关的识别和区分算法做好了。

_kinect.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(KinectSkeletonFrameReady);  
private void KinectSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)  
{  
    bool isSkeletonDataReady = false;  
    using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())  
    {  
        if (skeletonFrame != null)  
        {  
            skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];  
            skeletonFrame.CopySkeletonDataTo(skeletons);  
            isSkeletonDataReady = true;  
        }  
    }  
    if (isSkeletonDataReady)  
    {  
        Skeleton currentSkeleton = (from s in skeletons  
                                    where  
                                    s.TrackingState == SkeletonTrackingState.Tracked  
                                    select s).FirstOrDefault();  
        if (currentSkeleton != null)  
        {  
            var brush = new ImageBrush(); //定义图片画刷    
            var converter = new ImageSourceConverter();  
            brush.ImageSource = (ImageSource)converter.ConvertFromString("Images/huaji.jpg");  
            ellipse.Fill = brush;  //待优化  
  
            LockHeadWithRedSpot(currentSkeleton);  
        }  
        else  
        {  
            ellipse.Fill = null;  
        }  
   }  
}  

  using块中,将所有的骨骼信息复制到skeletons数组中。由于在Kinect视野中不一定是一个人,数量是不确定的,此处为变长数组,所以选择每一帧重新初始化。在currentSkeleton中,我们使用linq语句查询正在被跟踪的第一个人,所有人是按距离从近到远排序的。确保Kinect正在跟踪至少一个人,然后可以定义画刷,我使用的滑稽图片作为源,用该画刷填充ellipse。我使用了一个新的方法LockHeadWithRedSpot(Skeleton) 来跟踪头部。

private void LockHeadWithRedSpot(Skeleton s)  
{  
    Joint head = s.Joints[JointType.Head];  
    //将头部的骨骼点坐标映射到彩色视频流中  
    ColorImagePoint colorImagePoint = _kinect.CoordinateMapper.MapSkeletonPointToColorPoint(head.Position, _kinect.ColorStream.Format);  
    Point p = new Point((int)(colorImage.Width * colorImagePoint.X / _kinect.ColorStream.FrameWidth), (int)(colorImage.Height * colorImagePoint.Y / _kinect.ColorStream.FrameHeight));  
    Canvas.SetLeft(ellipse, p.X - 24);  
    Canvas.SetTop(ellipse, p.Y - 24);  
}  

  由于本篇及上篇使用的所有方法只能获得头部位置数据,并不能获得头部大小的数据,所以滑稽的大小要靠自己掌控。代码中24是我自己试出来的半径大小,可根据实际应用调整。

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。