深度图的实时平滑
来源:互联网 发布:中国联合工程公司知乎 编辑:程序博客网 时间:2024/06/09 20:13
from: http://blog.csdn.net/jiaojialulu/article/details/53192887?locationNum=12&fps=1
深度图的实时平滑
一、背景
英文原文,使用的是第一代kinect
youtube上的演示效果
二、深度数据存在的问题
下图是我简单处理后的深度图:
蓝色表示采到深度值为0的点,而其他点用颜色来标识,颜色越深表示离相机越近。数据中的噪声表现为蓝色斑点在画面上不断出现和消失。一些噪点是由于红外在遇到物体表面时发生散射造成的,另一些是由于离得kinect较近的物体的遮挡。
另一个限制深度数据的地方在于kinect的工作范围(0.8m-4m)。在这个范围之外的物体就会表现为无数据,即深度值为0。
三、解决办法
作者提出了像素滤波器和加权移动平均两种方法,并且能在实时的要求下达到深度图平滑的效果。
3.1 像素滤波器
3.1.1 原理及步骤
第一步是将深度数据帧转换为我们方便处理的形式,比如UINT16[]。
UINT16 *depthData = new UINT16[424 * 512];m_pDepthFrame->CopyFrameDataToArray(nDepthBufferSize,reinterpret_cast<UINT16*>(depthData));
- 1
- 2
下面就是对一帧上的每个像素搜索,找到深度值为0的位置,我们希望除去这样的像素,但是又不会影响精度和数据的其他特性。那么应该如何去做呢?
我们先把深度值为0的像素定为候选滤波对象,然后看看它究竟是否符合我们滤去它的标准。我们利用它周围的一些像素对应的深度值来定义这个标准。我们以候选滤波像素为中心定义一个一个两“层”的滤波器,同时用它来寻找这个滤波器框内其他深度值非零的像素。滤波器将这些深度值做一个分布,并关注每层框内这种像素的数量。然后将每层内非零像素个数与一个阈值比较,进而决定这个候选像素是否应该被滤波。如果任意层内非零像素的数目超过了阈值,就要将所有非零像素深度值对应的统计众数(数目最多一个深度值)应用到候选滤波像素上,使其深度值不为0。滤波器如下图所示:
下图主要表明了采用众数,即滤波器框内频数最高的一个深度值来作为候选像素的深度值,要比直接采用框内所有深度值的平均要更加符合实际(我觉得如果改成内层的众数更好)。
原文使用的是C#,我这里改为C++:
// 滤波后深度图的缓存 unsigned short* smoothDepthArray = (unsigned short*)i_result.data; // 我们用这两个值来确定索引在正确的范围内 int widthBound = 512 - 1; int heightBound = 424 - 1; // 内(8个像素)外(16个像素)层阈值 int innerBandThreshold = 3; int outerBandThreshold = 7; // 处理每行像素 for (int depthArrayRowIndex = 0; depthArrayRowIndex<424;depthArrayRowIndex++) { // 处理一行像素中的每个像素 for (int depthArrayColumnIndex = 0; depthArrayColumnIndex < 512; depthArrayColumnIndex++) { int depthIndex = depthArrayColumnIndex + (depthArrayRowIndex * 512); // 我们认为深度值为0的像素即为候选像素 if (depthArray[depthIndex] == 0) { // 通过像素索引,我们可以计算得到像素的横纵坐标 int x = depthIndex % 512; int y = (depthIndex - x) / 512; // filter collection 用来计算滤波器内每个深度值对应的频度,在后面 // 我们将通过这个数值来确定给候选像素一个什么深度值。 unsigned short filterCollection[24][2] = {0}; // 内外层框内非零像素数量计数器,在后面用来确定候选像素是否滤波 int innerBandCount = 0; int outerBandCount = 0; // 下面的循环将会对以候选像素为中心的5 X 5的像素阵列进行遍历。这里定义了两个边界。如果在 // 这个阵列内的像素为非零,那么我们将记录这个深度值,并将其所在边界的计数器加一,如果计数器 // 高过设定的阈值,那么我们将取滤波器内统计的深度值的众数(频度最高的那个深度值)应用于候选 // 像素上 for (int yi = -2; yi < 3; yi++) { for (int xi = -2; xi < 3; xi++) { // yi和xi为操作像素相对于候选像素的平移量 // 我们不要xi = 0&&yi = 0的情况,因为此时操作的就是候选像素 if (xi != 0 || yi != 0) { // 确定操作像素在深度图中的位置 int xSearch = x + xi; int ySearch = y + yi; // 检查操作像素的位置是否超过了图像的边界(候选像素在图像的边缘) if (xSearch >= 0 && xSearch <= widthBound && ySearch >= 0 && ySearch <= heightBound) { int index = xSearch + (ySearch * 512); // 我们只要非零量 if (depthArray[index] != 0) { // 计算每个深度值的频度 for (int i = 0; i < 24; i++) { if (filterCollection[i][0] == depthArray[index]) { // 如果在 filter collection中已经记录过了这个深度 // 将这个深度对应的频度加一 filterCollection[i][1]++; break; } else if (filterCollection[i][0] == 0) { // 如果filter collection中没有记录这个深度 // 那么记录 filterCollection[i][0] = depthArray[index]; filterCollection[i][1]++; break; } } // 确定是内外哪个边界内的像素不为零,对相应计数器加一 if (yi != 2 && yi != -2 && xi != 2 && xi != -2) innerBandCount++; else outerBandCount++; } } } } } // 判断计数器是否超过阈值,如果任意层内非零像素的数目超过了阈值, // 就要将所有非零像素深度值对应的统计众数 if (innerBandCount >= innerBandThreshold || outerBandCount >= outerBandThreshold) { short frequency = 0; short depth = 0; // 这个循环将统计所有非零像素深度值对应的众数 for (int i = 0; i < 24; i++) { // 当没有记录深度值时(无非零深度值的像素) if (filterCollection[i][0] == 0) break; if (filterCollection[i][1] > frequency) { depth = filterCollection[i][0]; frequency = filterCollection[i][1]; } } smoothDepthArray[depthIndex] = depth; } else { smoothDepthArray[depthIndex] = 0; } } else { // 如果像素的深度值不为零,保持原深度值 smoothDepthArray[depthIndex] = depthArray[depthIndex]; } } }
3.1.2 滤波效果
我这里再次把原图贴上,左图是滤波后的效果图:可以看到物体边缘散乱的深度值为0的点已经减少了不少。
3.1.3 代码
因为只是为了理论上了解像素滤波器平滑的机制,因此我选择静态的读取kinect采集的原始图片,然后进行平滑。
代码下载链接
请自行配制环境–kinect 2.0SDK和OpenCV。
下一节将继续讲解平滑中的加权移动机制
- 深度图的实时平滑
- kinect 2.0 SDK学习笔记(六)--深度图的实时平滑之加权移动平均机制
- 平滑的折线图
- 曲线的平滑平滑处理
- 实时SLAM的未来及深度学习与SLAM对比
- 平滑的曲线
- 平滑滤波器的演化
- 平滑滤波器的演化
- 数字图像--图象的平滑
- nginx的平滑升级
- 平滑滤波器的演化
- 平滑的贝赛尔曲线
- Nginx的平滑升级
- nginx的平滑升级
- 平滑滤波器的演化
- 平滑
- 平滑
- Qt中QLabel显示动态图的平滑缩放
- ccf201403-2
- VB test
- 社群系统ThinkSNS正式启动创业扶持计划
- Paxos理论介绍(二):Multi-Paxos
- HttpServletRequest常用获取URL的方法
- 深度图的实时平滑
- MySQL忘记密码或者因为修改mysql.user导致无法登陆解决方法
- 在线视频应用:flash播放器换html5播放器常见的问题汇总
- 【比特币】SPV是如何工作的
- 线程池学习
- 给模型的模块添加触摸点击等交互事件
- 备忘录模式
- 用Java 实现文章内容上一篇下一篇功能
- Toolbar