结构化的室内场景建模_过滤点云中红外光强度过高和过低的点
- 2019 年 11 月 1 日
- 筆記
在论文 “Structured Indoor Modeling” 的补充文档中,作者提到了数据集的预处理方法:Intensity-based Filtering 和 Connected Component Filtering。
Structured Indoor Modeling Project Website https://www2.cs.sfu.ca/~furukawa/sim/
我将会写两篇文章介绍这两个 Filter,并以继承 PCL 过滤工具 Filter 类的形式作为我的实现方式。
先看作者提到的第一个 Filter: Intensity-based Filtering。
这个 Filter 的字面意思是按照光照强度来过滤点云。在我之前的一篇文章中提到,以深度摄像头为采集设备的数据集中有一个属性 intensity。
intensity 值的理解 无雨森,公众号:无雨森的技术分享KinectAzureDK编程实战_用OpenCV、PCL操作和可视化Kinect数据
深度摄像头上的 “红外光投影仪” 投影 “红外光点” 到实际场景中。深度摄像头的 “红外光接收器” 接收实际场景反射过来的红外光。

这里没有找到 Azure Kinect 投影红外光点到实际场景的图,我们看一下 Kinect 1代的在夜视情况下看到的红外光点的情况[5]。

既然是光,对于镜面反射比较强的地方,比如光滑的桌面,镜子,显示器,玻璃窗户等等,就会出现光点很强或很暗的情况。
想象我拿着一个手电筒正面直直的照向一面镜子,强光会反射到我眼里。
换成红外光,深度摄像头的红外接收器很可能解析不了强红外光,给其一个 nan 值。如下图[1]所示。关于 nan 值的讲述,请参见我的上篇文章。
点云中 nan 值的理解 无雨森,公众号:无雨森的技术分享结构化的室内场景建模_预处理
或者给这些红外光点很高的 intensity 值。

如果红外光照向的是反光很强的物体,那么 “红外接收器” 接收到的红外光信号强度不够,则会使该深度图的像素示效,也会置一个 nan 值。或者给其一个很小的 intensity 值。如下图[2]所示。

Intensity-based Filtering 这个 Filter 的目的就是过滤掉这些过小和过大的 intensity 值。
在计算机图形学和计算机视觉算法中,常常使用 "高斯分布" 来描述一组点的分布。因为深度摄像头投影出的红外光照向实际场景时,光点足够多,红外光照向的场景是相互独立且随机的,所以可以把红外光点的强度值的分布近似的看作 ”高斯分布“。如下图[3]。

根据高斯分布的特性[4],以中值 mean 为中轴,向左向右扩展 1 个标准差,2 个标准差,3 个标准差分别对应 68%,95%,99.7%的数据(曲线下范围内的面积占曲线下总面积的比例)。
按照这个思想,我们只需要求出需要过滤的点云中所有点的光照强度 intensity 值的中值 mean 和 标准差 stddev。然后按照我们想过滤的范围,设定左右扩展的标准差的个数,即可过滤掉 intensity 值过高和过低的点。

继承自 PCL 的过滤工具类 FilterIndices 的 IntensityOutlierRemoval 类。
过滤工具类 FilterIndices 类继承自基类 Filter。比如 PCL 中众多的点云过滤类,比如 Voxel Grid Filter,Bilateral Filter 等。
这里实现的 IntensityOutlierRemoval 类似于 PCL 中的 Pass Through Filter。Pass Through Filter 类通过提供特定需要过滤的属性范围来过滤点云。比如,需要保留 z 值范围在 [0.1, 0.5] 之间的点云。
IntensityOutlierRemoval 过滤的是 intensity 特定范围外的点。
即上述代码中的 filter_field_name_。设定其
filter_field_name_ = "intensity";
另外,我们还需要设定中值左右扩展的标准差的个数 std_mul_。
直接看过滤的函数 applyFilterIndices()。

取出点云所有有效点的 intensity 值存储在一个数组中。
for (int iii = 0; iii < static_cast<int>(indices_->size()); ++iii) { const uint8_t *pt_data = reinterpret_cast<const uint8_t*>(&input_->points[iii]); memcpy(&distances[iii], pt_data + fields[distance_idx].offset, sizeof(float)); }
然后求出点云所有点的 intensity 值的中值 mean 和标准差 stddev。
并设定左右两个范围 distance_threshold_low 和 distance_threshold_high。
double mean = 0; double stddev = 0; pcl::getMeanStd(distances, mean, stddev); distance_threshold_low = mean - std_mul_ * stddev; distance_threshold_high = mean + std_mul_ * stddev;
我们测试一下 IntensityOutlierRemoval。这里设定标准差扩展范围是 2 个标准差,读取的文件夹保存的是我上篇文章中提前处理成 organized 的点云。具体转换方式,详见上篇文章。
organized 点云转换方式 无雨森,公众号:无雨森的技术分享结构化的室内场景建模_预处理

在过滤点云前,我们需要先把 organized 点云中的所有 nan 点剔除。因为这些点的 intensity 值也是无效的。所以点云中的 nan 点都不能进入过滤以及将来的运算过程。
先看一下原始的所有点云叠加起来是什么样的效果。

本文片头说过,在镜面反射比较强的地方会出现大量这样的 intensity 值异常的点。比如窗户,镜子之类的。如下图。红外光在照向窗户时,会通过玻璃射向窗外,反射回来的光强会降低很多。这部分点云就是我们需要过滤的 intensity 值过低的点。

看一下过滤的效果。

如果红外光直射镜面反射强的地方,红外接收器也会接收到很高光强的红外光。如下图所示。红框标记出来的地方有几片点。很明显,这些就是需要过滤的 intensity 值过高的点。

看一下过滤的效果。红框标记出来的这几片点云过滤效果非常明显。

引用
[1] https://docs.microsoft.com/zh-cn/azure/Kinect-dk/media/concepts/depth-camera-invalidation-saturation.png
[2] https://docs.microsoft.com/zh-cn/azure/Kinect-dk/media/concepts/depth-camera-invalidation-low-signal.png
[3] https://www.statisticshowto.datasciencecentral.com/wp-content/uploads/2013/09/standard-normal-distribution.jpg
[4] https://www.mathsisfun.com/data/standard-normal-distribution.html
[5] https://www.buzzfeed.com/southafrican/kinect-filmed-with-a-night-vision-camera-17vf