最近在做机器视觉方面的一点工作,用Kinect作sensor获取深度数据、颜色、手势识别等。非常感激CNBlog上的两篇博文:(1)独钓寒江的http://www.cnblogs.com/yangecnu/archive/2012/03/30/KinectSDK_Geting_Started.html 从中学到了不少关于在WPF平台上使用C#,利用微软Kinect SDK开发自己的应用程序的知识,给了我很大的帮助,在此表示感谢!
但是博主独钓寒江的博文中,针对深度图像滤波,只简要说了取反和用Bgr32表示深度图像,效果不是很理想。
后来,发现了另一篇博文(2)何文西的http://www.cnblogs.com/TravelingLight/archive/2012/05/25/2517680.html介绍了国外的一篇文章,上面介绍了外国作者自己开发的两种滤波方法:像素滤波法,加权移动平均法。效果很好!可惜只有代码片段无法试验,而且滤波算法不是特别容易理解。实在可惜!
前几天偶然跟踪并找到了作者的源代码http://kinectdepthsmoothing.codeplex.com/,下载来看。运行发现,程序要求:VS2012以上、Kinect SDK 1.7以上版本,而根据独钓寒江的博文指导,其中的代码用的是SDK 1.0,进一步发现其中的一些API函数,数据类型都不相同,再者,Kinectdepthsmoothing中xaml代码用的是后台创建,而独钓寒江的博文中xaml用的是布局式,而我对xaml又不懂,如何把这两个程序结合起来呢?
由于独钓寒江的博文给出的程序中,有大量的功能实现,比如保存图像、鼠标单击显示像素深度值、彩色渲染、人体尺寸获取,游戏者索引等等,而外国朋友的kinectdepthsmoothing程序只有滤波和保存图像,那么考虑把滤波模块移植到独钓寒江的程序中,并且不改变原来的布局式界面。在移植的过程中,主要解决了以下几个类型不兼容的问题:
a) 将short[] pixelData类型改成DepthImagePixel[] pixelData,这里注意:SDK 1.8版本中,private DepthImagePixel[] depthPixels;其中获取深度数据为
1 short depth=depthPixels[i].Depth;
而SKD 1.0版本中,short[] pixelData获取深度数据的方式为
1 Int32 depth = this.depthPixels[pIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth;
需要进行移位操作。而且两种方式的depth类型不一样,一个是short16位,一个是Int32位。
b) 用三通道Bgr32格式的colorBitmap存储深度图像,而不是Gray16格式的depthBitmap。除了深度数组的格式不一样之外,外国朋友的程序中,有这样几行代码值得关注,
1 //获得当前帧最大和最小可用的深度值 2 short minDepth = (short)lastDepthFrame.MinDepth; 3 short maxDepth = (short)lastDepthFrame.MaxDepth;
以及
1 //在这里将depthPixels数组中的深度值,逐元素,转换并存储到colorPixles中 2 3 //最后,将colorPixels写入colorBitmap中 4 5 int colorPixelIndex = 0; 6 7 for (int i = 0; i < depthPixels.Length; i++) 8 9 { 10 11 //获取该元素(点)的深度值 12 13 short depth = depthPixels[i].Depth; 14 15 byte intensity = (byte)255; 16 17 int newMax = depth - minDepth; 18 19 if (newMax > 0) 20 21 {intensity = (byte)(255 - (255 * newMax / (3150))); } 22 23 this.colorPixels[colorPixelIndex++] = intensity;//blue 24 25 this.colorPixels[colorPixelIndex++] = intensity;//green 26 27 this.colorPixels[colorPixelIndex++] = intensity;//red 28 29 ++colorPixelIndex; 30 }
最后将数据写入colorBitmap时,代码为
this.colorBitmap.WritePixels( new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight), this.colorPixels, this.colorBitmap.PixelWidth * sizeof(int), 0);
使用了 this.colorBitmap.PixelWidth这个写法,而不是独钓寒江博主的程序中,使用depthStream.FrameHeight创建图像的矩形区域this.depthRect = new Int32Rect(0, 0, depthStream.FrameWidth, depthStream.FrameHeight);
c) 此外,初始化变量(数据容器depthPixels和colorPixels)时,要注意colorPixels的大小
1 this.depthPixels = new DepthImagePixel[depthStream.FramePixelDataLength]; 2 this.colorPixels = new byte[depthStream.FramePixelDataLength * sizeof(int)];
总之,将两个程序结合起来,要注意两个方面,一是界面的形式,布局还是后台创建,二是,.cs程序中由于使用的SDK版本不同,有很多API和数据类型不一致的地方,需要修改。由于正在做后续的实验,比如加入了自己的利用深度数据进行边缘检测的代码,还有很多想法没有实现,暂时写到这里吧。到时候会将完整代码上传到网络上,供大家参考!谢谢。
下篇文章将介绍基于深度值的目标分割,并附上完整代码。