和其他摄像机一样,近红外摄像机也有视场。Kinect摄像机的视野是有限的,如下图所示:

如图,红外摄像机的视场是金字塔形状的。离摄像机远的物体比近的物体拥有更大的视场横截面积。这意味着影像的高度和宽度,比如640X480和摄像机视场的物理位置并不一一对应。但是每个像素的深度值是和视场中物体离摄像机的距离是对应的。深度帧数据中,每个像素占16位,这样BytesPerPixel属性,即每一个像素占2个字节。每一个像素的深度值只占用了16个位中的13个位。如下图:

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

 Int32 pixelIndex = (Int32)(p.X + ((Int32)p.Y * frame.Width));
Int32 depth = this.depthPixelDate[pixelIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth;

显示深度数据最简单的方式是将其打印出来。我们要将像素的深度值显示到界面上,当鼠标点击时,显示鼠标点击的位置的像素的深度值。第一步是在主UI界面上添加一个TextBlock:

<Window x:Class="KinectDepthImageDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="KinectDepthImage" Height="" Width="" WindowStartupLocation="CenterScreen">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="PixelDepth" FontSize="" HorizontalAlignment="Left" />
<Image x:Name="DepthImage" Width="" Height="" ></Image>
</StackPanel>
</Grid>
</Window>

接着我们要处理鼠标点击事件。在添加该事件前,需要首先添加一个私有变量lastDepthFrame来保存每一次DepthFrameReady事件触发时获取到的DepthFrame值。因为我们保存了对最后一个DepthFrame对象的引用,所以事件处理代码不会马上释放该对象。然后,注册DepthFrame 图像控件的MouseLeftButtonUp事件。当用户点击深度图像时,DepthImage_MouseLeftButtonUp事件就会触发,根据鼠标位置获取正确的像素。最后一步将获取到的像素值的深度值显示到界面上,代码如下:

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, );
}
}
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}'{2}", depth, depthFt, depthInches);
}
}

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

结果如下图,由于截屏时截不到鼠标符号,所以用红色点代表鼠标位置,下面最左边图片中的红色点位于墙上,该点距离Kinect 2.905米,中间图的点在我的手上,可以看出手离Kinect距离为1.221米,实际距离和这个很相近,可见Kinect的景深数据还是很准确的。

上面最右边图中白色点的深度数据为-1mm。这表示Kinect不能够确定该像素的深度。在处理上数据时,这个值通常是一个特殊值,可以忽略。-1深度值可能是物体离Kinect传感器太近了的缘故。

Kinect 深度测量原理的更多相关文章

  1. Kinect for Windows SDK开发入门(四):景深数据处理 上

    原文来自:http://www.cnblogs.com/yangecnu/archive/2012/04/04/KinectSDK_Depth_Image_Processing_Part1.html ...

  2. Kinect 开发 —— 硬件设备解剖

    Kinect for Xbox: 360 不支持“近景模式” 三只眼睛 —— 红外投影机,RGB摄像头,红外深度投影头  —— 色彩影像中的每个像素分别与深度影像中的一个像素对应 四只耳朵 —— L形 ...

  3. Kinect v2控制鼠标原理分析和源码

    https://blog.csdn.net/baolinq/article/details/54381284 此程序为利用Kinect v2实现用手指隔空控制鼠标,是我另一个项目的一部分,因为在另外那 ...

  4. C#动手实践:Kinect V2 开发(2):数据源工作原理及红外源Demo

    Kinect体系架构

  5. 使用HTML5开发Kinect体感游戏

    一.简介 我们要做的是怎样一款游戏? 在前不久成都TGC2016展会上,我们开发了一款<火影忍者手游>的体感游戏,主要模拟手游章节<九尾袭来 >,用户化身四代,与九尾进行对决, ...

  6. Kinect for Windows SDK开发入门(十九):Kinect Fusion

        Kinect for Windows SDK1.7中引入了Kinect Fusion功能.在1.8的SDK中对该功能进行了改进和强化,Kinect Fusion能够使得我们使用Kinect f ...

  7. Kinect 总结---Kinect基本认识

    玩了Kinect也有差不多一年的时间了,觉得Kinect是个挺好玩挺有未来的玩意.但是很经常做完了一次,下次又得重新看源码,没有把Kinect里的知识总结起来变为自己的知识,所以特意重新总结一下自己使 ...

  8. 利用Kinect将投影变得可直接用手操控

    Finally 总算是到了这一天了!假期里算法想不出来,或者被BUG折磨得死去活来的时候,总是YY着什么时候能心情愉快地坐在电脑前写一篇项目总结,今天总算是抽出时间来总结一下这神奇的几个月. 现在回过 ...

  9. 【翻译】Kinect v2程序设计(C++) Depth编

    Kinect SDK v2预览版,取得Depth数据的方法说明. 上一节,介绍了通过使用Kinect for Windows SDK v2预览版(以下简称为,Kinect SDK v2预览版)从Kin ...

随机推荐

  1. centos7 vim环境优化

    centos7默认是使用vi,而不是使用vim,所以,我们需要修改一下vi的别名,并且,我们使用neovim,vi毕竟还是有很多功能比较原始 所以 yum install neovim -ycat & ...

  2. CMake速记

    目录 CMake速记 我的demo 外部构建 基础语法 常用指令 环境变量 一些变量 参考文档 title: CMake速记 date: 2019/11/18 19:17:40 toc: true - ...

  3. [转帖]从入门到实践:创作一个自己的 Helm Chart

    从入门到实践:创作一个自己的 Helm Chart https://www.cnblogs.com/alisystemsoftware/p/11436469.html 自己已经搭建好了 helm 和t ...

  4. curl使用举例

    我在银行工作时,一个具体的用例:shell脚本中使用的 sendAddr=`echo http:192.168.1.100:8080/cloud-monitor/perfaddperf` SendDa ...

  5. C++ 继承 - 在派生类中对基类初始化

    构造函数与基类的其他成员不同,不能被派生类继承,因此为了初始化基类中的成员变量,需要在派生类中调用基类的构造函数(即显式调用),如果派送类没有调用则默认调用基类的无参构造函数(即隐式调用). 显式调用 ...

  6. PHP学习之PHP trait解析

    自PHP5.4.0起,PHP实现了一种代码复用的方法,称为trait. 众所周知,PHP中是单继承的,trait是为类似PHP的单继承语言而准备得一种代码复用机制.trait为了减少单继承语言的限制, ...

  7. python 基础(十七)--hashlib加密模块

    hashlib加密模块 两种方式使用 字符串是中文时需要先编码成utf-8 常用加密算法:md5,sha1(已被破解)等... >>> a= hashlib.md5() >&g ...

  8. CF网络流练习

    1. 103E 大意: 给定$n$个集合, 满足对于任意的$k$, 任意$k$个集合的并集都不少于$k$. 要求选出$k$个集合$(k> 0)$, 使得并恰好等于$k$, 输出最少花费. Hal ...

  9. servlet报错“严重: Allocate exception for servlet 类名java.lang.ClassNotFoundException: 路径. 类名”可能原因

    1.WEB-INF下web.xml中<servlet-class>路径错误,正确路径为 <servlet-class>包名.类名</servlet-class> 2 ...

  10. c# internal关键字

    对于一些大型的项目,通常由很多个DLL文件组成,引用了这些DLL,就能访问DLL里面的类和类里面的方法.比如,你写了一个记录日志的DLL,任何项目只要引用此DLL就能实现记录日志的功能,这个DLL文件 ...