转自:http://www.cnblogs.com/yangecnu/archive/2012/04/04/KinectSDK_Depth_Image_Processing_Part1.html


深度数据是Kinect传感器的精髓

DepthImageStream的使用和ColorImageStream的使用类似。DepthImageStream和ColorImageStream都继承自ImageStream。可以像从ColorImageStream获取数据生成图像那样生成景深图像。

显示深度数据的套路相同

初始化

private void InitializeKinectSensor(KinectSensor kinectSensor)
{
if (kinectSensor != null)
{
DepthImageStream depthStream = kinectSensor.DepthStream;
depthStream.Enable(); depthImageBitMap = new WriteableBitmap(depthStream.FrameWidth, depthStream.FrameHeight, , ,
PixelFormats.Gray16, null);
depthImageBitmapRect = new Int32Rect(, , depthStream.FrameWidth, depthStream.FrameHeight);
depthImageStride = depthStream.FrameWidth * depthStream.FrameBytesPerPixel; DepthImage.Source = depthImageBitMap;
kinectSensor.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(kinectSensor_DepthFrameReady);
kinectSensor.Start();
}
}

事件处理

void kinectSensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
{
if (lastDepthFrame != null)
{
lastDepthFrame.Dispose();
lastDepthFrame = null;
}
lastDepthFrame = e.OpenDepthImageFrame();
if (lastDepthFrame != null)
{
depthPixelDate = new short[lastDepthFrame.PixelDataLength];
lastDepthFrame.CopyPixelDataTo(depthPixelDate);
depthImageBitMap.WritePixels(depthImageBitmapRect, depthPixelDate, depthImageStride, ); CreateColorDepthImage(this.lastDepthFrame, depthPixelDate);
}
}

获取单点深度信息

获取每一个像素的距离很容易,但是要直接使用还需要做一些位操作。可能大家在实际编程中很少情况会用到位运算。如上图所示,深度值存储在第3至15位中,要获取能够直接使用的深度数据需要向右移位,将游戏者索引(Player Index)位移除。后面将会介绍游戏者索引位的重要性。下面的代码简要描述了如何获取像素的深度值。代码中pixelData变量就是从深度帧数据中获取的short数组。PixelIndex基于待计算像素的位置就算出来的。SDK在DepthImageFrame类中定义了一个常量PlayerIndexBitmaskWidth,它定义了要获取深度数据值需要向右移动的位数。

有一点值得注意的是,在UI界面中Image空间的属性中,宽度和高度是硬编码的。如果不设置值,那么空间会随着父容器(From窗体)的大小进行缩放,如果空间的长宽尺寸和深度数据帧的尺寸不一致,当鼠标点击图片时,代码就会返回错误的数据,在某些情况下甚至会抛出异常。像素数组中的数据是固定大小的,它是根据DepthImageStream的Enable方法中的DepthImageFormat参数值来确定的。如果不设置图像控件的大小,那么他就会根据Form窗体的大小进行缩放,这样就需要进行额外的计算,将鼠标的在Form中的位置换算到深度数据帧的维度上。这种缩放和空间转换操作很常见,在后面的文章中我们将会进行讨论,现在为了简单,对图像控件的尺寸进行硬编码。

private void DepthImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// 获取鼠标位置的深度数据
Point p = e.GetPosition(DepthImage);
if (depthPixelDate != null && depthPixelDate.Length > )
{
Int32 pixelIndex = (Int32)(p.X + ((Int32)p.Y * this.lastDepthFrame.Width));
Int32 depth = this.depthPixelDate[pixelIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth; // 获取深度
Int32 depthInches = (Int32)(depth * 0.0393700787); // 英寸
Int32 depthFt = depthInches / ; // 英尺
depthInches = depthInches % ;
//PixelDepth.Text = String.Format("{0}mm~{1}'{1}", depth, depthFt, depthInches);
}
}

深度图像增强

代码中过滤掉了一些距离太近的点。因为过近的点和过远的点都不准确。所以过滤掉了大于3.5米小于0米的数据,将这些数据设置为白色。

private void CreateLighterShadesOfGray(DepthImageFrame depthFrame, short[] pixelData)
{
// 对深度图像的简单处理 —— 高低阈值
Int32 depth;
Int32 loThreashold = ;
Int32 hiThreshold = ;
short[] enhPixelData = new short[depthFrame.Width * depthFrame.Height];
for (int i = ; i < pixelData.Length; i++)
{
depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
if (depth < loThreashold || depth > hiThreshold)
{
enhPixelData[i] = 0xFF;
}
else
{
enhPixelData[i] = (short)~pixelData[i];
} }
EnhancedDepthImage.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height, , , PixelFormats.Gray16, null, enhPixelData, depthFrame.Width * depthFrame.BytesPerPixel);
}

如果能够将16位的灰度级用32位彩色表示效果会更好。当 RGB值一样时,就会呈现出灰色。灰度值的范围是0~255,0为黑色,255为白色,之间的颜色为灰色。现在将灰色值以RGB模式展现出来。

将彩色影像的格式改为了Bgr32位,这意味每一个像素占用32位(4个字节)。每一个R,G,B分别占8位,剩余8位留用。这种模式限制了RGB的取值为0-255,所以需要将深度值转换到这一个范围内。除此之外,我们还设置了最小最大的探测范围,这个和之前的一样,任何不在范围内的都设置为白色。将深度值除以4095(0XFFF,深度探测的最大值),然后乘以255,这样就可以将深度数据转换到0至255之间了。

private void CreateBetterShadesOfGray(DepthImageFrame depthFrame, short[] pixelData)
{
Int32 depth;
Int32 gray;
Int32 loThreashold = ;
Int32 bytePerPixel = ;
Int32 hiThreshold = ;
byte[] enhPixelData = new byte[depthFrame.Width * depthFrame.Height * bytePerPixel];
for (int i = , j = ; i < pixelData.Length; i++, j += bytePerPixel)
{
depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
if (depth < loThreashold || depth > hiThreshold)
{
gray = 0xFF;
}
else
{
gray = ( * depth / 0xFFF);
}
enhPixelData[j] = (byte)gray;
enhPixelData[j + ] = (byte)gray;
enhPixelData[j + ] = (byte)gray; }
EnhancedDepthImage.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height, , , PixelFormats.Bgr32, null, enhPixelData, depthFrame.Width * bytePerPixel);
}

深度数据的彩色渲染 —— 伪彩色图像

将深度数据值转化到0-255并用RGB模式进行显示可以起到增强图像的效果,能够从图像上直观的看出更多的深度细节信息。还有另外一种简单,效果也不错的方法,那就是将深度数据值转换为色调和饱和度并用图像予以显示。

private void CreateColorDepthImage(DepthImageFrame depthFrame, short[] pixelData)
{
Int32 depth;
Double hue;
Int32 loThreshold = ;
Int32 hiThreshold = ;
Int32 bytesPerPixel = ;
byte[] rgb = new byte[];
byte[] enhPixelData = new byte[depthFrame.Width * depthFrame.Height * bytesPerPixel]; for (int i = , j = ; i < pixelData.Length; i++, j += bytesPerPixel)
{
depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth; if (depth < loThreshold || depth > hiThreshold)
{
enhPixelData[j] = 0x00;
enhPixelData[j + ] = 0x00;
enhPixelData[j + ] = 0x00;
}
else
{
hue = (( * depth / 0xFFF) + loThreshold);
ConvertHslToRgb(hue, , , rgb); enhPixelData[j] = rgb[]; //Blue
enhPixelData[j + ] = rgb[]; //Green
enhPixelData[j + ] = rgb[]; //Red
}
} EnhancedDepthImage.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height, , , PixelFormats.Bgr32, null, enhPixelData, depthFrame.Width * bytesPerPixel);
}

以上代码中使用了ConvertHslToRgb这一函数,该函数的作用是进行两个颜色空间的转换,就是将H(Hue色调)S(Saturation饱和度)L(Light亮度)颜色空间转换到RGB颜色空间的函数。

public void ConvertHslToRgb(double hue, double saturation, double lightness, byte[] rgb)
{
double red = 0.0;
double green = 0.0;
double blue = 0.0;
hue = hue % 360.0;
saturation = saturation / 100.0;
lightness = lightness / 100.0; if (saturation == 0.0)
{
red = lightness;
green = lightness;
blue = lightness;
}
else
{
double huePrime = hue / 60.0;
int x = (int)huePrime;
double xPrime = huePrime - (double)x;
double L0 = lightness * (1.0 - saturation);
double L1 = lightness * (1.0 - (saturation * xPrime));
double L2 = lightness * (1.0 - (saturation * (1.0 - xPrime))); switch (x)
{
case :
red = lightness;
green = L2;
blue = L0;
break;
case :
red = L1;
green = lightness;
blue = L0;
break;
case :
red = L0;
green = lightness;
blue = L2;
break;
case :
red = L0;
green = L1;
blue = lightness;
break;
case :
red = L2;
green = L0;
blue = lightness;
break;
case :
red = lightness;
green = L0;
blue = L1;
break;
}
} rgb[] = (byte)(255.0 * red);
rgb[] = (byte)(255.0 * green);
rgb[] = (byte)(255.0 * blue);
}
}

貌似上面的颜色空间转换挺麻烦的 ~~  好了,差不多看懂了,自己实现一下:


namespace TestDepthProcess
{ /// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private KinectSensor kinect;
private WriteableBitmap depthImageBitMap;
private Int32Rect depthImageBitmapRect;
private Int32 depthImageStride;
private DepthImageFrame lastDepthFrame;
private short[] depthPixelDate; public KinectSensor Kinect
{
// C#的set ,get方法好奇葩的说
get { return kinect; }
set
{
if (kinect != null)
{
UninitializeKinectSensor(this.kinect);
kinect = null;
} if (value!=null && value.Status==KinectStatus.Connected)
{
kinect = value;
InitializeKinectSensor(this.kinect);
}
}
} public MainWindow()
{
InitializeComponent();
this.Loaded += (s, e) => DiscoverKinectSensor();
this.Unloaded += (s, e) => this.kinect = null;
} private void UninitializeKinectSensor(KinectSensor kinect)
{
if (kinect!=null)
{
kinect.Stop();
kinect.DepthFrameReady -= new EventHandler<DepthImageFrameReadyEventArgs>(kinectSensor_DepthFrameReady); }
} private void InitializeKinectSensor(KinectSensor kinect)
{
if (kinect!=null)
{
DepthImageStream depthStream = kinect.DepthStream;
depthStream.Enable(); depthImageBitMap = new WriteableBitmap(depthStream.FrameWidth, depthStream.FrameHeight, , , PixelFormats.Gray16, null);
depthImageBitmapRect = new Int32Rect(, , depthStream.FrameWidth, depthStream.FrameHeight);
depthImageStride = depthStream.FrameWidth * depthStream.FrameBytesPerPixel; DepthImage.Source = depthImageBitMap;
kinect.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(kinectSensor_DepthFrameReady);
kinect.Start();
}
} private void DiscoverKinectSensor()
{
KinectSensor.KinectSensors.StatusChanged += new EventHandler<StatusChangedEventArgs>(KinectSensors_StatusChanged);
this.Kinect = KinectSensor.KinectSensors.FirstOrDefault(sensor => sensor.Status == KinectStatus.Connected);
// 此处调用kinect的set方法,注意 this.Kinect 而不是 this.kinect
} private void KinectSensors_StatusChanged(object sender, StatusChangedEventArgs e)
{
switch (e.Status)
{
case KinectStatus.Connected:
if (this.kinect == null)
this.kinect = e.Sensor;
break;
case KinectStatus.Disconnected:
if (this.kinect == e.Sensor)
{
this.kinect = null;
this.kinect = KinectSensor.KinectSensors.FirstOrDefault(x => x.Status == KinectStatus.Connected);
if (this.kinect == null)
{
//TODO:通知用于Kinect已拔出
}
}
break;
//TODO:处理其他情况下的状态
}
} private void kinectSensor_DepthFrameReady(Object sender,DepthImageFrameReadyEventArgs e)
{
if (lastDepthFrame!=null)
{
lastDepthFrame.Dispose();
lastDepthFrame = null;
}
lastDepthFrame = e.OpenDepthImageFrame();
if (lastDepthFrame!=null)
{
depthPixelDate = new short[lastDepthFrame.PixelDataLength];
lastDepthFrame.CopyPixelDataTo(depthPixelDate);
depthImageBitMap.WritePixels(depthImageBitmapRect, depthPixelDate, depthImageStride, ); CreateColorDepthImage(this.lastDepthFrame, depthPixelDate);
}
} private void CreateColorDepthImage(DepthImageFrame depthFrame, short[] pixelData)
{
Int32 depth;
Double hue;
Int32 loThreshold = ;
Int32 hiThreshold = ;
Int32 bytesPerPixel = ;
byte[] rgb=new byte[]; byte[] enhPixelData=new byte[depthFrame.Width*depthFrame.Height*bytesPerPixel]; for (int i = , j = ; i < pixelData.Length;i++,j+=bytesPerPixel )
{
depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
if (depth<loThreshold || depth>hiThreshold)
{
enhPixelData[j] = 0x00;
enhPixelData[j + ] = 0x00;
enhPixelData[j + ] = 0x00;
}
else
{
hue = (( * depth / 0xFFF) + loThreshold);
ConvertHslToRgb(hue, , , rgb); enhPixelData[j] = rgb[]; //blue
enhPixelData[j + ] = rgb[]; // green;
enhPixelData[j + ] = rgb[]; //red }
} EnhancedDepthImage.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height, , , PixelFormats.Bgr32, null, enhPixelData, depthFrame.Width * bytesPerPixel);
} private void ConvertHslToRgb(double hue,double saturation,double lightness,byte[] rgb)
{
double red = 0.0;
double green = 0.0;
double blue = 0.0; hue = hue % ;
saturation = saturation / 100.0;
lightness = lightness / 100.0; if (saturation==0.0)
{
red = lightness;
green = lightness;
blue = lightness;
}
else
{
double huePrime = hue / 60.0;
int x = (int)huePrime;
double xPrime = huePrime - (double)x; ;
double L0 = lightness * (1.0 - saturation);
double L1 = lightness * (1.0 - (saturation * xPrime));
double L2 = lightness * (1.0 - (saturation * (1.0 - xPrime))); switch (x)
{
case :
red = lightness;
green = L2;
blue = L0;
break;
case :
red = L1;
green = lightness;
blue = L0;
break;
case :
red = L0;
green = lightness;
blue = L2;
break;
case :
red = L0;
green = L1;
blue = lightness;
break;
case :
red = L2;
green = L0;
blue = lightness;
break;
case :
red = lightness;
green = L0;
blue = L1;
break;
}
} rgb[] = (byte)(255.0 * red);
rgb[] = (byte)(255.0 * green);
rgb[] = (byte)(255.0 * blue);
} private void DepthImage_MouseLeftButtonUp(Object sender, MouseButtonEventArgs e)
{
Point p = e.GetPosition(DepthImage);
if (depthPixelDate!=null&&depthPixelDate.Length>)
{
Int32 pixelIndex = (Int32)(p.X + ((Int32)p.Y * this.lastDepthFrame.Width));
Int32 depth = this.depthPixelDate[pixelIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth;
Int32 depthInches = (Int32)(depth * 0.0393700787);
Int32 depthFt = depthInches / ;
depthInches = depthInches % ; PixelDepth.Text = string.Format("{0}mm~{1}'{1}", depth, depthFt, depthInches);
}
} private void CreateLighterShadesOfGray(DepthImageFrame depthFrame, short[] pixelData)
{
Int32 depth;
Int32 loThreshold = ;
Int32 hiThreshold = ;
short[] enhPixelData= new short[depthFrame.Width*depthFrame.Height]; for (int i = ; i < pixelData.Length;i++ )
{
depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
if (depth<loThreshold || depth>hiThreshold)
{
enhPixelData[i] = 0xFF;
}
else
{
enhPixelData[i]=(short)~pixelData[i];
}
} EnhancedDepthImage.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height, , , PixelFormats.Gray16, null, enhPixelData, depthFrame.Width * depthFrame.BytesPerPixel);
} private void CreateBetterShadesOfGray(DepthImageFrame depthFrame, short[] pixelData)
{
Int32 depth;
Int32 gray;
Int32 loThreashold = ;
Int32 bytePerPixel = ; // 4个通道只用前面3个bgr
Int32 hiThreshold = ;
byte[] enhPixelData = new byte[depthFrame.Width * depthFrame.Height * bytePerPixel];
for (int i = , j = ; i < pixelData.Length; i++, j += bytePerPixel)
{
depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
if (depth < loThreashold || depth > hiThreshold)
{
gray = 0xFF;
}
else
{
gray = ( * depth / 0xFFF);
}
enhPixelData[j] = (byte)gray;
enhPixelData[j + ] = (byte)gray;
enhPixelData[j + ] = (byte)gray; }
EnhancedDepthImage.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height, , , PixelFormats.Bgr32, null, enhPixelData, depthFrame.Width * bytePerPixel);
} }
}

C#的set方法 ~~ 这个错误找了半天 ~~

Kinect 开发 —— 深度信息的更多相关文章

  1. Kinect 开发 —— 深度信息(二)

    转自(并致谢):http://www.cnblogs.com/yangecnu/archive/2012/04/05/KinectSDK_Depth_Image_Processing_Part2.ht ...

  2. Kinect开发学习笔记之(一)Kinect介绍和应用

    Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...

  3. 使用OpenNI 2获取RGBD摄像头深度信息

    NiViewer 安装好摄像头驱动和OpenNI后,在Tools文件夹中可以找到一个程序NiViewer.NiViewer的一些基本控制方法如下: 1. ESC关闭NiViewer程序 2. 右键可以 ...

  4. Kinect 开发 —— 全息图

    Kinect的另一个有趣的应用是伪全息图(pseudo-hologram).3D图像可以根据人物在Kinect前面的各种位置进行倾斜和移动.如果方法够好,可以营造出3D控件中3D图像的效果,这样可以用 ...

  5. Kinect 开发 —— 骨骼追踪(下)

    Kinect 连线游戏 在纸上将一些列数字(用一个圆点表示)从小到大用线连起来.游戏逻辑很简单,只不过我们在这里要实现的是动动手将这些点连起来,而不是用笔或者鼠标. 在开始写代码之前,需要明确定义我们 ...

  6. Kinect开发文章目录

    整理了一下去年为止到现在写的和翻译的Kinect的相关文章,方便大家查看.另外,最近京东上微软在搞活动, 微软 Kinect for Windows 京东十周年专供礼包 ,如果您想从事Kinect开发 ...

  7. Kinect开发资源汇总

    Kinect开发资源汇总   转自: http://www.sigvc.org/bbs/forum.php?mod=viewthread&tid=254&highlight=kinec ...

  8. Kinect开发笔记之二Kinect for Windows 2.0新功能

    这是本博客翻译文档的第一篇文章.笔者已经苦逼的竭尽全力的在翻译了.但无奈英语水平也是非常有限.不正确或者不妥当不准确的地方必定会有,还恳请大家留言或者邮件我以批评指正.我会虚心接受. 谢谢大家.   ...

  9. Kinect 开发 —— 杂一

    Kinect 提供了非托管(C++)和托管(.NET)两种开发方式的SDK,如果您用C++开发的话,需要安装Speech Runtime(V11),Kinect for Windows Runtime ...

随机推荐

  1. PostgreSQL Replication之第九章 与pgpool一起工作(2)

    9.2 理解pgpool的功能 pgpool提供了如下功能: •连接池 •语句级别的复制 •负载均衡 •限制连接 •内存缓存 •并行查询 [当决定使用那些功能的时候,记住并非所有的功能可以在同一时间使 ...

  2. php时间戳转化成时间相差8小时问题

    php时间戳 转化成时间的时候 $mytime=time(); echo $mytime.'<br />'; echo date('Y-m-d H:i:s',$mytime); 会产生8个 ...

  3. caffe(11) 图像数据转换成db文件

    在深度学习的实际应用中,我们经常用到的原始数据是图片文件,如jpg,jpeg,png,tif等格式的,而且有可能图片的大小还不一致.而在caffe中经常使用的数据类型是lmdb或leveldb,因此就 ...

  4. vue-cli生成的模板各个文件详解(转)

    vue-cli脚手架中webpack配置基础文件详解 一.前言 原文:https://segmentfault.com/a/1190000014804826 vue-cli是构建vue单页应用的脚手架 ...

  5. [POI2012]HUR-Warehouse Store(贪心,堆)

    题意 n天.第i天上午会进货Ai件商品,中午的时候会有顾客需要购买Bi件商品,可以选择满足顾客的要求,或是无视掉他. 如果要满足顾客的需求,就必须要有足够的库存.问最多能够满足多少个顾客的需求. (n ...

  6. P3168 [CQOI2015]任务查询系统(主席树)

    题目描述 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei ...

  7. 2014 CodingTrip - 携程编程大赛 (预赛第一场)

    1001: 可以证明(扩展欧几里得),只要卡片中有两个卡片互素,旁边点就是可达的. 因此只需要算出所有卡片不互素的情况有多少种,可用容斥原理. #include <cstdio> #inc ...

  8. Mysql忘记rootpassword

    1,停止MYSQL服务,CMD打开DOS窗体.输入 net stop mysql 2,在CMD命令行窗体,进入MYSQL安装文件夹 比方E:\Program Files\MySQL\MySQL Ser ...

  9. 大话设计模式C++实现-第15章-抽象工厂模式

    一.UML图 二.概念 抽象方法模式(Abstract Factory):提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们详细的类. 三.包括的角色 (1)抽象工厂 (2)详细工厂:包含详细 ...

  10. HDOJ 4009 Transfer water 最小树形图

    Transfer water Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) T ...