开发学习笔记(C#)第一篇
ColorFream的获取与显示
(找到的二代笔记居然是C++,可恶! 自己来写C#。Code摘自SDK。来熟悉程序工作调用流程。 )(XXX?)皆为待议 若看代码底色不爽,剪切粘贴在txt,再粘回来,格式就取消了,是不是很神奇?
然而二代我找了半天楞没找到那个一代多个kinect遍历的东西的存在,战五渣不解释,以后发现了再补。
色彩图有RGB/YUV2种,Kinect V2 采用USB3.0,Fps与质量都有提高。最高:1920*1080
从「Frame」取得Color图像的数据时,Kinect SDK v1是预先指定图像尺寸和格式,Kinect SDK v2不能指定图像尺寸。因此,取得数据后可以按任意形状来整理。
数据流获取的两种模型
轮询模型:程序定期检查数据,若有帧数据返回,可请求下一帧。
事件模型:注册帧FrameReady事件,帧到达触发事件,调用事件的属性FrameReadyEventArgs来获得数据帧,但也要检查是否为空。本例采用事件模型。
直接上彩色帧读取显示代码(WPF项目):
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;//添加对Kinect对象的引用
引用空间不多介绍,在自动生成的上加了kinect
namespace myKinectColorViewer
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private KinectSensor kinectSensor = null;
private ColorFrameReader colorFrameReader = null;
private WriteableBitmap colorBitmap = null;
public MainWindow()
{
默认KinectSensor对象
this.kinectSensor = KinectSensor.GetDefault();
//ReaderForColor打开kinectsensor的色彩帧源的reader
this.colorFrameReader = this.kinectSensor.ColorFrameSource.OpenReader();
//注册色彩帧到达后的发生事件,绑定执行函数Reader_ColorFreamArrived
this.colorFrameReader.FrameArrived += this.Reader_ColorFrameArrived;
//获得对色彩帧的描述,以Rgba格式
FrameDescription colorFrameDescription = this.kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
//创造用于显示的bitmap,图宽,图高,水平/垂直每英寸像素点数,像素点格式,调色板为空?
this.colorBitmap = new WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);
//Kinect传感器是否可用
this.kinectSensor.Open();
this.DataContext = this;//将此窗口对象作为视窗
InitializeComponent();
}
以上部分为初始化+事件触发执行方法
以下是Reader_ColorFrameArrived方法的构造
Using表示使用完就释放资源,从传来的色彩帧事件参数得到色彩帧,然后由FrameDescription得到对他的描述,
锁定位图数据
{用于处理。
将数据以所需格式拷至指定区域,
然后调用位图数据刷新(更改),
}
最后解锁,
由Imageource绑定输出。
在这里陈述下Lock与Unlock,是锁定数据用于某一线程,一定要解锁啊!
{}内具体是先通过把传来的色彩帧数据传到色彩位图的缓冲区,然后使用This.colorBitmap.AddDirtyRec()更新指定区域的色彩帧位图数据,Int32Recdt(x0,y0,width,height) 指定原点与宽高,确定矩形范围。
private void Reader_ColorFrameArrived(object sender,ColorFrameArrivedEventArgs e)
{
using(ColorFrame colorFrame=e.FrameReference.AcquireFrame())
{
if(colorFrame!=null)
{
FrameDescription colorFreamDescription = colorFrame.FrameDescription;
using(KinectBuffer colorBuffer=colorFrame.LockRawImageBuffer())
{
this.colorBitmap.Lock();
if((colorFreamDescription.Width==this.colorBitmap.PixelWidth)&&(colorFreamDescription.Height==this.colorBitmap.Height))
{
colorFrame.CopyConvertedFrameDataToIntPtr(
this.colorBitmap.BackBuffer,
(uint)(colorFreamDescription.Width * colorFreamDescription.Height * 4),
ColorImageFormat.Bgra);
this.colorBitmap.AddDirtyRect(new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight));
}
this.colorBitmap.Unlock();
}
}
}
}
以下为wpf的显示提供图源
public ImageSource ImageSource
{
get
{
return this.colorBitmap;
}
}
以下为在窗口关闭后关闭传感与释放从传感器得到的数据,关闭之前先检查,变成好习惯。此段不多述。
private void Window_Closed(object sender, EventArgs e)
{
if (this.colorFrameReader != null)
{
this.colorFrameReader.Dispose();
this.colorFrameReader = null;
}
if (this.kinectSensor != null)
{
this.kinectSensor.Close();
this.kinectSensor = null;
}
}
}
}
效果图如下:
数据流程:
程序流程:
1.KinectSensor.open()开启传感器。
2.获取KinectSensor传感器对象,初始化
Reader 、FrameDescription、bitmap、窗口、注册帧到达事件
3.打开对应的this.kinectSensor.ColorFrameSource.OpenReader(),选定所用的流,(说明Kinect V2会将流都扔给你。) FrameDescription获得对色彩帧的描述,在这里是以何种格式。
4.注册色彩帧到达后的处理事件,绑定的方法要自己写,
5.使用WriteableBitmap初始化bitmap(提前),在4.的方法中画出相应的位图。
6.写返回Bitmap的ImageSource函数,可用其他替带。
7.收尾,释放资源,KinectSensor.close()关闭传感器。
补充:
后续代码中附加计算显示FPS
取出的Color图像的数据如果指定的是BGRA格式,就会像图一样的排列着。蓝(B),绿(G),红(R),和无效值(A)的共计32bit构成1像素。
WriteableBitmap之于Bitmap,就好像StringBuilder之于String(重写复制?)。可以减少内存消耗。WriteableBitmap在初始化时需要指定高度,宽度和格式。
BitmapSource 是WPF中图片的核心类型。所以在Mainwindows
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.kinectSensor = KinectSensor.GetDefault();
this.colorFrameReader = this.kinectSensor.ColorFrameSource.OpenReader();
this.colorFrameReader.FrameArrived += this.Reader_ColorFrameArrived;
FrameDescription colorFrameDescription = this.kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
this.colorBitmap = new WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);
this.kinectSensor.Open();
this.DataContext = this;
}
先调用构造函数MainWindow(),在窗体加载时才会引发MainWindow_Loaded()事件 WinForm 中的 Form_Load函数和他的构造函数
public Form()有区别: (1): public Form():程序入口,它的InitializeComponent为你的程序作了必要的初始化工作,所以Load才能成为一个事件, (2) Form_Load:装载窗口事件, 是窗体启动时触发的事件 总的来讲: 程序先执行InitializeComponent(); 后执行Form1_Load,InitializeComponent是进行一系列的初始化,Form1_Load是在窗体装载时发生的一个事件。
附xaml添加部分的代码:
<Grid>
<Image HorizontalAlignment="Left"
Source="{Binding ImageSource}" Stretch="Fill" VerticalAlignment="Top" />
<!--若变形严重修改stretch为Uniform-->
</Grid>