1. 从"点云糊成一团"说起:这篇论文到底想解决什么
如果你处理过城市级的三维重建数据,大概率见过这样的场景:无人机或者车载激光雷达扫了一条街,导出的点云文件打开一看,建筑物立面像被砂纸磨过,窗户和墙面的交界处糊成一片,屋顶的边缘线歪歪扭扭,甚至同一面墙上出现了两层"重影"。这不是扫描设备的问题,而是多帧点云配准累积误差加上扫描视角差异共同造成的典型症状。
《Non-local Scan Consolidation for 3D Urban Scenes》这篇论文瞄准的就是这个痛点。标题里的三个词拆开看很有意思:Non-local(非局部)、Scan Consolidation(扫描整合)、3D Urban Scenes(三维城市场景)。Scan Consolidation 不是简单的配准(registration),也不是单纯的重建(reconstruction),它介于两者之间——把已经粗略对齐的多帧扫描数据,进一步"揉"成一个干净、一致、没有重影的单一表面表达。
为什么强调 Non-local?因为传统方法大多盯着局部邻域做文章。比如某个点周围 5 厘米内的邻居点,算个法向量、做个平滑、投个影。这种做法在平坦区域没问题,但城市建筑有大量重复结构——一排窗户、一列柱子、连续的阳台栏杆。局部方法遇到这些重复模式,很容易"拉错邻居",把 A 窗户的点拉到 B 窗户上去,结果就是窗户越处理越模糊,甚至消失。
这篇论文的核心洞察是:城市建筑虽然复杂,但充满了重复性和规律性。同一栋楼的窗户尺寸基本一致,同一排建筑的立面朝向大致相同,地面和屋顶的平面结构反复出现。如果能把这种"非局部"的相似性利用起来,让相似的扫描片段互相约束、互相修正,就能在保留锐利边缘的同时消除重影和噪声。
适合读这篇论文的人,我大致分三类:一是做城市三维重建的工程师,手头有大量车载或机载点云需要出干净成果;二是研究点云处理的学生,想理解从配准到表面重建之间那块"灰色地带"的经典思路;三是做数字孪生或智慧城市相关产品的开发者,需要知道底层数据是怎么被"洗干净"的。这篇论文不是最新的端到端深度学习方法,但它对问题的拆解方式和几何直觉,放到今天依然有很强的参考价值。
2. 为什么"局部平滑"在城市点云上总是翻车
2.1 局部方法的三个致命假设
大多数点云去噪和平滑算法,背后都藏着几个默认假设。第一个假设是局部邻域内的点属于同一个表面。在单个物体扫描里这基本成立,但城市扫描中,一帧点云可能同时打到墙面、窗框、空调外机和树枝,5 厘米半径内混了四五个不同深度的表面。你按局部邻域做主成分分析,算出来的法向量是这几个表面的"平均方向",既不垂直于墙,也不垂直于窗,投影之后边缘直接圆角化。
第二个假设是噪声是独立同分布的。局部方法通常假设每个点的误差互不相关,所以可以用邻域平均来抵消。但多帧配准误差是系统性的——整帧点云可能整体偏移了几厘米,帧内点的误差高度相关。你用邻域平均去压这种误差,相当于用一把歪的尺子去量另一把歪的尺子,越量越歪。
第三个假设是表面是单层且连续的。城市扫描里,同一面墙可能被扫了三次,三次的深度略有不同,形成三层"薄片"。局部平滑会把这三层往中间挤,但挤的过程中,如果某层在某处缺了一块,平滑结果就会出现空洞或者厚度不均。更麻烦的是,当两层点云的交界处恰好是边缘时,平滑会把边缘"抹平",因为算法分不清这是噪声造成的双层,还是真实的锐利边缘。
2.2 重复结构带来的"错误吸引"
城市建筑最显著的特征就是重复。一栋 20 层的住宅楼,同一立面上可能有 60 个一模一样的窗户。局部算法处理第 30 个窗户时,如果邻域半径稍大,就会把第 29 和第 31 个窗户的点也拉进来。由于窗户之间有墙面隔开,这些点的法向量和深度差异很大,平均之后窗户的凹槽被填平,窗框的棱角被磨圆。
我实测过一个类似的场景:用某开源库的移动最小二乘法(MLS)对一栋楼做平滑,半径设 0.3 米。结果窗户的凹陷深度从原来的 15 厘米变成了 6 厘米,窗台线几乎消失。把半径降到 0.1 米,窗户保住了,但墙面的重影还在。这就是局部方法的死结——半径小了压不住重影,半径大了毁掉细节。
论文里把这个现象叫做"局部方法的尺度困境"。城市场景的特征尺度跨度太大:墙面是米级的大平面,窗框是厘米级的细结构,两者交织在一起,任何单一尺度的局部操作都会顾此失彼。
2.3 非局部思路的切入点
Non-local 的核心思想借鉴了图像处理里的非局部均值(Non-Local Means)。在图像去噪中,不是用周围像素平均,而是用整张图里所有相似的图像块来平均。放到三维点云上,就是用整个场景里所有相似的几何片段来约束当前片段的整合。
具体到城市建筑,相似性体现在几个层面。第一是几何相似:所有窗户的凹槽形状相似,所有阳台的栏杆间距相似。第二是方向相似:同一立面的所有点法向量应该一致,同一水平面的所有点应该共面。第三是结构相似:建筑通常是分段重复的,第 5 层到第 8 层的结构可能和第 15 层到第 18 层完全一样。
论文的做法是先把场景切成小块(patch),然后为每个小块在全局范围内找相似的块,形成一个"相似块集合"。整合的时候,不是只用自己的点,而是用相似块集合里的所有点来共同估计一个干净的表面。这样,即使某个窗户被扫描噪声污染了,其他 59 个干净的窗户会把它"拉回"正确形状。
这个思路的代价是计算量大——全局找相似块是 O(N²) 的复杂度。论文用了 KD 树加速和特征降维来缓解,但即便如此,处理一个街区级别的点云仍然需要可观的算力。这也是为什么这类方法在工业界落地时,通常只用于关键区域的精修,而不是全场景跑一遍。
3. 论文方法拆解:从相似块搜索到全局整合
3.1 整体流程的四个阶段
论文的方法可以拆成四个阶段,我用处理一栋沿街建筑的流程来串一遍。
第一阶段是初始配准和法向估计。输入是多帧已经粗略对齐的点云,每帧带有扫描位置信息。先对每个点估计法向量,这里用的是局部 PCA,但论文加了一个基于扫描视角的加权——扫描仪打到的点,入射角越小越可靠,入射角接近 90 度的点法向估计误差大,权重降低。这个细节很实用,因为城市扫描中掠射角区域(比如远处的地面)噪声特别大,不加权的话法向会乱飞。
第二阶段是相似块构建。把点云体素化,每个体素作为一个块,提取块的特征。特征包括几何特征(法向分布、曲率、深度直方图)和上下文特征(到最近边缘的距离、所在平面的朝向)。然后在全局范围内为每个块找 K 个最相似的块。这里论文用了一个技巧:先按朝向粗筛,再按几何特征精排,避免全量搜索。
第三阶段是相似块联合整合。对每个块,用它的相似块集合里的所有点,拟合一个局部表面。拟合不是简单的平面拟合,而是用移动最小二乘加上一个"一致性约束"——相似块之间的对应点应该满足相似的几何变换。这一步是论文的核心,它把非局部的相似性转化成了对局部表面的额外约束。
第四阶段是全局优化和表面重建。把每个块的局部表面拼起来,用泊松重建或者类似方法生成最终网格。拼接时有一个重叠区域的加权融合,权重取决于块内点的密度和相似块的数量——相似块越多,说明这个结构越常见,整合结果越可信,权重越高。
3.2 相似块搜索的工程细节
相似块搜索是计算瓶颈,论文里给了几个优化手段,我结合实际经验补充一下。
特征降维:原始特征可能有几十维,论文用 PCA 降到 8 到 12 维。降维后做最近邻搜索,速度提升明显。但降维会损失一些判别性,论文的做法是保留前几个主成分的同时,额外保留"边缘响应"这个特征不参与降维,因为边缘是城市建筑最重要的特征,不能丢。
搜索范围限制:全局搜索太慢,论文实际上做了两个限制。一是只在同一朝向的块之间搜索,比如所有法向朝北的墙面块互相搜索,不跨朝向。二是用扫描位置做粗聚类,同一扫描仪位置附近的块优先互相匹配,因为同一视角的扫描噪声模式相似,匹配更可靠。
块大小的选择:块太大,相似块数量少,约束不足;块太小,特征不稳定,容易误匹配。论文在实验部分给了一个经验值:城市建筑场景下,块边长取 0.5 到 1.0 米比较合适。这个尺度大致对应一个窗户或者一段栏杆的尺寸,既能捕捉结构,又有足够的重复性。
我自己的经验是,如果建筑立面重复单元特别小(比如密集的百叶窗),块可以降到 0.3 米;如果是大面积玻璃幕墙,重复性差,块要放大到 1.5 米以上,否则找不到足够的相似块。
3.3 整合阶段的权重设计
整合阶段的权重设计直接决定最终效果,论文里用了三个权重相乘。
第一个是扫描质量权重,基于入射角和距离。入射角越小、距离越近,权重越高。这个权重在法向估计阶段已经算过,这里复用。
第二个是相似度权重,当前块和相似块的相似度越高,权重越高。相似度用特征距离的倒数来算,但论文加了一个阈值,距离超过阈值的块直接丢弃,不参与整合。这个阈值需要根据点云密度调整,密度低的时候阈值放宽,否则找不到足够的相似块。
第三个是空间一致性权重,相似块在空间上离当前块越近,权重越高。这个权重防止了"远处的相似块把近处的结构带偏"。论文里用高斯核来算,带宽取块边长的 2 到 3 倍。
三个权重相乘之后归一化,然后对相似块集合里的所有点做加权最小二乘拟合。这里有一个实现上的坑:如果相似块数量太少(比如少于 3 个),拟合会不稳定,论文的做法是退回到局部方法,只用当前块的点做拟合,但降低平滑强度。
4. 实测中的表现与边界:哪些场景它搞不定
4.1 论文实验里的亮眼结果
论文在几个城市数据集上做了测试,我挑几个有代表性的结果来说。
重影消除:在多层扫描的墙面上,原始点云有明显的双层甚至三层重影,层间距 3 到 8 厘米。经过非局部整合后,重影基本消失,墙面变成单层,厚度从原来的 15 厘米降到 3 厘米以内。这个效果比单纯的局部 MLS 好很多,局部方法要么压不干净,要么把墙面压出波浪。
边缘保持:窗户和墙面的交界处,原始点云的边缘模糊带宽度大约 10 到 15 厘米。整合后边缘宽度降到 3 到 5 厘米,窗框的棱角重新变得锐利。论文给了一个定量指标:边缘处的曲率对比度提升了约 40%。
重复结构修复:对于被噪声严重污染的窗户(比如玻璃反射导致点云缺失或偏移),非局部方法能利用其他干净窗户的信息把它"补"回来。论文展示了一个极端案例:某个窗户 70% 的点缺失,整合后仍然恢复出了完整的窗框形状。这是局部方法完全做不到的。
4.2 我实测中遇到的三个边界
论文的方法不是万能的,我在类似思路上做实验时,遇到过几个明确的边界。
边界一:非重复结构效果有限。城市里不是所有东西都重复。比如一棵树、一个雕塑、一辆停着的车,这些物体没有足够的相似块,非局部约束起不了作用,效果和局部方法差不多。论文也承认这一点,它的目标场景是"建筑主导的城市环境",不是自然场景或杂乱场景。
边界二:大尺度形变处理不了。如果两帧点云之间的配准误差不是随机的,而是有系统性的弯曲(比如扫描仪标定漂移导致的缓慢形变),非局部方法只能局部修正,无法纠正全局弯曲。这种形变需要先做非刚性配准,或者用控制点做全局变形,非局部整合是后续的精修步骤。
边界三:计算成本随场景规模非线性增长。论文里处理的场景大概是几百万到千万级点云,再大就需要分块处理。分块会破坏全局相似性搜索,块边界处可能出现不一致。论文没有详细讨论分块策略,这是一个工程落地时的实际难点。
4.3 和深度学习方法的对比
现在有很多基于深度学习的点云去噪和整合方法,比如 PointCleanNet、ScoreDenoise 等。它们用神经网络学习点云的局部或全局特征,端到端输出干净点云。和这篇论文的方法相比,各有优劣。
深度学习方法在处理速度上有优势,推理阶段是前向传播,比非局部搜索快很多。在泛化能力上,如果训练数据覆盖了测试场景的类型,效果通常更好。但深度学习方法需要大量标注数据,而城市点云的干净真值很难获取,通常是用合成数据训练,迁移到真实数据时有域间隙。
论文的非局部方法不需要训练数据,是纯几何的,可解释性强,每一步为什么这么做都能说清楚。在数据稀缺或者场景特殊的情况下,这种方法的鲁棒性更好。而且非局部方法可以和深度学习结合——用网络预测相似块,或者用网络做初始去噪,再用非局部方法精修。
我个人在实际项目里的选择是:如果场景规整、重复性强、数据量不大,直接用非局部方法,可控且效果稳定;如果场景复杂、数据量大、有标注数据,先用深度学习方法粗处理,再用非局部方法做关键区域精修。
5. 如果要在项目里复现,我会这样搭流程
5.1 数据准备和预处理
复现的第一步是准备数据。你需要多帧已经粗略配准的点云,格式最好是 PCD 或 PLY,每帧附带扫描位置(传感器原点坐标)。如果没有扫描位置,法向估计的视角加权就用不了,效果会打折扣。
预处理包括三个操作。降采样:用体素栅格降采样,体素大小取最终块边长的 1/5 到 1/10。比如块边长 0.5 米,体素取 0.05 到 0.1 米。降采样太狠会丢细节,太密则计算量爆炸。离群点去除:用统计滤波去掉明显的飞点,半径取体素大小的 2 到 3 倍,邻居数取 20 到 30。法向估计:用 PCA 估计法向,邻域半径取体素大小的 3 到 5 倍,同时记录入射角。
这里有一个容易忽略的点:法向的一致性定向。PCA 估计的法向有正负二义性,如果不做定向,相似块搜索时会把朝向相反的两个块误判为不相似。定向的方法可以用扫描位置做参考——法向应该指向扫描仪方向,或者用最小生成树做全局定向。
5.2 相似块搜索的实现选择
相似块搜索可以用 KD 树或者 FAISS。KD 树适合几万到几十万块,FAISS 适合更大规模。特征向量建议包含:法向的球坐标(2 维)、曲率(1 维)、深度直方图的几个统计量(均值、方差、偏度,3 维)、到最近边缘的距离(1 维)、块内点的密度(1 维)。总共 8 维左右,再做 PCA 降到 6 维。
搜索时,K 值取 10 到 20。太小约束不足,太大引入误匹配。论文里用的是 15,我实测 10 到 15 比较稳。相似度阈值用特征距离的百分位数来定,比如取所有距离的第 30 百分位作为阈值,保证每个块至少有 3 到 5 个相似块。
注意:相似块搜索前一定要做朝向粗筛。我试过不筛直接搜,结果朝东的墙面块匹配到了朝西的块,因为两者的深度直方图碰巧相似,整合后墙面直接扭曲。
5.3 整合和重建的代码骨架
整合阶段的核心是加权最小二乘拟合。下面是一个简化的 Python 伪代码骨架,用 numpy 实现,实际项目里需要换成更高效的库。
import numpy as np from scipy.spatial import cKDTree def consolidate_block(block_points, similar_blocks, weights): """ block_points: 当前块的点,Nx3 similar_blocks: 相似块的点列表,每个 Mx3 weights: 每个相似块的权重,长度 K """ all_points = [block_points] all_weights = [1.0] # 当前块权重设为 1 for pts, w in zip(similar_blocks, weights): all_points.append(pts) all_weights.append(w) merged = np.vstack(all_points) w_arr = np.concatenate([np.full(len(p), w) for p, w in zip(all_points, all_weights)]) # 加权 PCA 拟合局部平面 centroid = np.average(merged, axis=0, weights=w_arr) centered = merged - centroid cov = (centered * w_arr[:, None]).T @ centered / w_arr.sum() eigvals, eigvecs = np.linalg.eigh(cov) normal = eigvecs[:, 0] # 最小特征值对应的方向 # 把点投影到拟合平面上 dist = centered @ normal projected = merged - dist[:, None] * normal return projected, normal, centroid这段代码只是示意,实际还需要处理块之间的重叠融合、边界处的权重过渡、以及最终网格重建。重建可以用 Open3D 的泊松重建,深度取 8 到 10,点云法向用整合后的法向。
5.4 参数调优的实操心得
参数调优是复现中最耗时的部分,我总结几个关键参数的影响和推荐范围。
| 参数 | 作用 | 推荐范围 | 调大效果 | 调小效果 |
|---|---|---|---|---|
| 块边长 | 控制相似块尺度 | 0.3-1.5 米 | 相似块少,约束弱 | 特征不稳,误匹配多 |
| 相似块数 K | 参与整合的块数 | 10-20 | 平滑强,细节可能丢 | 约束不足,重影残留 |
| 相似度阈值 | 筛选相似块 | 距离的 20-40 百分位 | 相似块多,可能误匹配 | 相似块少,可能不够 |
| 空间权重带宽 | 控制空间衰减 | 块边长的 2-3 倍 | 远处块影响大 | 只用近处块,约束弱 |
| 体素大小 | 降采样尺度 | 块边长的 1/5-1/10 | 计算快,细节少 | 计算慢,细节多 |
我的经验是,先固定块边长和体素大小,调相似块数和相似度阈值,让重影基本消除但边缘还保留。然后再微调空间权重带宽,把远处的误匹配压下去。最后如果边缘还是不够锐利,适当减小块边长,但要注意相似块数要相应增加。
6. 从这篇论文延伸出的几个实用思路
6.1 把非局部思想用到配准精修上
论文的方法是在配准之后做整合,但非局部相似性其实可以反过来指导配准。如果两个扫描帧里有大量相似块,这些相似块之间的几何变换应该一致,可以用这个一致性来修正配准误差。具体做法是:先做粗配准,然后提取相似块,用 RANSAC 估计相似块之间的变换,如果变换和粗配准结果差异大,说明粗配准有误,用相似块的变换来修正。
这个思路我在一个项目中试过,对于重复结构多的建筑立面,配准精度能提升 20% 到 30%。但对于重复结构少的场景,提升不明显,因为找不到足够的相似块来约束。
6.2 和语义分割结合做结构感知整合
论文的方法没有用语义信息,所有块一视同仁。但实际上,墙面、窗户、地面、屋顶的整合策略应该不同。墙面需要强平滑,窗户需要保边缘,地面需要保平面度。如果先做语义分割,给每个块打上标签,然后按标签调整整合参数,效果会更好。
比如窗户区域的块,相似块搜索时只搜窗户块,不搜墙面块,避免墙面把窗户带偏。地面区域的块,整合时加一个共面约束,强制所有地面块拟合到同一个平面上。这些语义约束在论文里没有,但工程上很实用。
6.3 增量式处理大规模场景
论文的方法是全量处理,大规模场景需要分块。分块的关键是块边界处的处理。我的做法是:分块时让相邻块有 50% 的重叠,整合时重叠区域的点参与两个块的拟合,最终融合时用距离加权。这样块边界处不会出现明显的接缝。
另一个技巧是分层处理。先处理大尺度的平面结构(墙面、地面、屋顶),把大结构定下来,再处理小尺度的细节(窗框、栏杆)。大结构处理时用大块,小块处理时用小块,避免尺度冲突。
6.4 评估指标的选取
复现之后怎么评估效果?论文用了三个指标:重影消除程度(用墙面厚度衡量)、边缘保持度(用边缘处的曲率对比度衡量)、以及和真值的距离误差。实际项目中,真值很难获取,我通常用前两个指标加上目视检查。
重影消除程度可以用局部平面拟合的残差来量化。取一面墙的区域,拟合平面,算点到平面的距离标准差。标准差越小,重影越少。边缘保持度可以用边缘检测算子(比如基于法向变化的算子)在整合前后的响应对比,响应越强,边缘越锐利。
目视检查也很重要,特别是对于重复结构,要检查窗户是否变形、栏杆是否断裂、阳台是否错位。这些细节指标很难量化,但一眼就能看出来。
7. 一些踩过的坑和最后的建议
第一个坑是法向估计的邻域半径。我一开始用固定半径,结果在点云密度变化大的区域,法向要么太光滑要么太噪。后来改成自适应半径,根据局部点密度调整,密度高的地方半径小,密度低的地方半径大。这个改动让后续的相似块搜索稳定了很多。
第二个坑是相似块搜索的对称性。理论上,如果块 A 把块 B 当作相似块,块 B 也应该把块 A 当作相似块。但实际搜索时,由于特征计算的不对称性(比如深度直方图的 bin 边界),这个对称性不成立。我后来强制做了对称化:只保留互相都是相似块的配对,去掉单向的。这样相似块集合更干净,整合效果更稳。
第三个坑是整合后的点云密度不均。相似块多的区域,整合后点特别密;相似块少的区域,点稀疏。直接做泊松重建会出现密度不均导致的孔洞。我的做法是整合后做一次均匀重采样,按体素栅格重新采样,保证密度一致。
最后一个建议:不要指望一篇论文的方法解决所有问题。非局部整合是点云处理流水线中的一环,它的上游是配准,下游是重建。配准没做好,整合再强也救不回来;重建算法不匹配,整合的干净点云也出不了好网格。把这篇论文的方法放在合适的位置,配合其他步骤,才能发挥最大价值。
我在实际项目里用这套思路处理过几个街区的建筑点云,整体效果比纯局部方法好一个档次,特别是重复结构多的住宅楼和办公楼。但遇到复杂雕塑、树木、临时停放的车辆,还是得靠其他方法。没有银弹,只有合适的工具用在合适的地方。