Kinect应用开发

Posted by Elias on January 1, 2020

layout: post title: Kinect开发实践 subtitle: 如果不是毕设,我啥时候会挖这个坑呢 date: 2020-1-1 author: Elias header-img: img/post-bg-bluewhale.jpg catalog: true tags:

- Blog

Kinect应用开发实践

写在前面

本次实践要完成的目标

  1. 熟悉Kinect带有的人体骨骼识别功能,并能够实时获取骨骼数据流,建立人体骨骼图像。
  2. 实现对骨骼数据的处理,完成静态姿势识别。
  3. 实现对骨骼数据的处理,完成动态动作识别。
  4. 定义出危险动作,并在做出相应危险动作后,能够识别并标注。

搭建Kinect开发环境

主机性能

处理器:Intel(R) Core(TM) i5-6300HQ CPU @ 2.30GHz 2.30 GHz

机待RAM:8.00 GB

系统:Win10

VS2017

KinectForWindows SDK

开始实践!

从第一个程序开始

  • 获取Kinect设备传来的彩色帧,并关闭某个色彩通道

    新建一个WPF工程

    添加引用”Microsoft.Kinect”

    image

    在”MainWindow.xaml.cs”中引用命名空间

    image

    为了能够在窗体中将后续接收的图像显示出来,我们需要在设计器中加入一个Image,用于显示我们接收的图像。

    <Window x:Class="CloseColor.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:CloseColor"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <Image x:Name="image"/>
        </Grid>
    </Window>
    

    image

    接着在”MainWindow.xaml.cs”中完成我们对MainWindow.xaml的交互逻辑,代码如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using Microsoft.Kinect;
      
    namespace CloseColor
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            private KinectSensor kinect = null; //创建一个新的kinect
            private ColorFrameReader colorFrame = null; //创建色彩读取器
            private WriteableBitmap colorBitmap = null; //创建色彩位图
            private FrameDescription colorFrameDescription = null;  //创建色彩框架描述
            private Int32Rect colorBitmapRect;  //创建32位色彩位图矩阵
            private int colorBitmapStride;  //定义色彩位图的Stride
            byte[] pixelData = null;    //像素数据数组
      
            public MainWindow()
            {
                InitializeComponent();  //初始化组件
                this.kinect = KinectSensor.GetDefault();    //获得kinect初始化数据
                this.colorFrame = kinect.ColorFrameSource.OpenReader(); //通过读取器获得Kinect获取的色彩框架
                this.colorFrame.FrameArrived += ColorFrame_FrameArrived;    //绑定事件
                this.colorFrameDescription = this.kinect.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);    //获取kinect收集的色彩框架描述
                pixelData = new byte[colorFrameDescription.LengthInPixels * 4]; //创建实例
                //按照Bgra格式设定数据帧的描述信息(B:Blue,G:Green,R:Red,A:Alpha)
                // 1920*1080  彩色图像
                this.colorBitmap = new WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);    //
                //根据数据帧的宽高创建colorBitmap的实例
                this.colorBitmapRect = new Int32Rect(0, 0, this.colorBitmap.PixelWidth, colorBitmap.PixelHeight);
                this.colorBitmapStride = this.colorFrameDescription.Width * 4;  //32位图像中,Stride = Width * 4
                image.Source = this.colorBitmap;    //将色彩位图赋值给image
                this.kinect.Open(); //打开kinect
            }
      
            //定义事件处理函数
            void ColorFrame_FrameArrived(object sender, ColorFrameArrivedEventArgs e)
            {
                using(ColorFrame frame = e.FrameReference.AcquireFrame())
                {
                    if(frame != null)   //若获取的框架不为空
                    {
                        frame.CopyConvertedFrameDataToArray(this.pixelData, ColorImageFormat.Bgra); //将转换后的帧数据复制到数组this.pixelData
                        for(int i = 0; i < pixelData.Length; i += 4)
                        {
                            //pixelData[i] = 0x00;//关闭蓝色通道      B
                            //pixelData[i] = 0x00;//关闭绿色通道      G
                            pixelData[i + 2] = 0x00;//关闭红色通道    R
                        }
      
                        this.colorBitmap.WritePixels(this.colorBitmapRect, this.pixelData, this.colorBitmapStride, 0);
                    }
                }
            }
        }
    }
    

-知识补充

  1. Stride

    stride是图像处理中常用的概念。

    假设一行有11个像素(Width = 11),对于一个32位(每个像素4字节)的图像来说,Stride=11 * 4 = 44.

    其中还会有字节对齐的问题,譬如:假设一行有11个像素(Width = 11),对于一个24位(每个像素3字节)的图像,Stride = 11 * 3 + 3 = 36,注意到这里并不是Stride = 11 * 3 = 33,因为这里是按照4字节对齐的,因此33中,前32占满了,第33位占据一个4字节空间,因此是36.

    根据上述内容,我们能够手动计算Stride的值:

    (1)Stride = 像素位数/8 * Width;

    (2)若Stride不是4的倍数,那么Stride = Stride + (4 - Stride mod 4);

  2. C#中@的用法

    (1)加在字符串前面,字符串中的”\“失去转义符的作用,可直接写字符串而不需要考虑转义字符。

    private readonly string gestureDatabase = @"Database\uphand.gbd";
    // 若不加 @,需写成如下格式:
    private readonly string gestureDatabase = "Database\\uphand.gbd";
    

    (2)加在字符串前面,字符串中的 “ 要用 ““ 表示。

    (3)加在字符串前面,字符串中的空格、换行都会保存,方便阅读代码。