本节摘要:表面重建给配准完成的点云蒙上连续"皮肤"。法向定向决定成败,泊松重建全局求解水密网格,阿尔法形状局部包络适合薄壳与采样不匀场景。本节是外业整理的收官工序,交棒第四章成图。
拼好的整体点云仍是"散沙"——渲染像雾、打印不了、无法算体积。收官工序是把它升级为三角网格:点变成面,散沙变成壳。这一步与第四章的成图一线之隔:本章处理"给点蒙皮"的通用技术,第四章再谈多视图与深度学习这些"成图路线"的完整体系。
第三章前几节多次提到法向,但重建对法向有更苛刻的要求——全局一致定向。主成分分析给出的法向是"无头箭"(朝内朝外皆可),而泊松重建要求整片点云的法向一致朝外。定向错误的表现非常有辨识度:重建出的网格出现"泡泡穿孔"、大片翻转的黑面。定向手段有三:朝相机参考点定向(单视角扫描有效)、法向传播(最小生成树式漫延,Open3D 生成式接口默认做)、基于可见性的多视角一致化。
# 重建前的法向体检与定向 import open3d as o3d import numpy as np pcd = o3d.io.read_point_cloud(o3d.data.EaglePointCloud().path) pcd = pcd.voxel_down_sample(0.03) pcd.estimate_normals( o3d.geometry.KDTreeSearchParamHybrid(radius=0.12, max_nn=50)) pcd.orient_normals_consistent_tangent_plane(k=15) ns = np.asarray(pcd.normals) ps = np.asarray(pcd.points) - np.asarray(pcd.points).mean(0) cos_out = np.sum(ns * ps / (np.linalg.norm(ns,axis=1) * np.linalg.norm(ps,axis=1))[:,None], axis=1) print('法向与外向径向夹角余弦均值: %.3f' % cos_out.mean()) # 输出: 法向与外向径向夹角余弦均值: 0.841 -- 正值说明整体朝外,定向合格
结果解读:对闭合物体(鹰像),法向与"从质心指向外"的径向方向夹角余弦应为正,均值零点八四说明定向基本一致;若均值接近零或为负,重建前必须重做定向。变式:开放式场景(一面墙)没有"外"的概念,靠相机可见性定向更稳。
泊松重建的思路与逐点三角化完全不同:把点云当作一组带方向的采样,求解一个全局的指示函数(场内为一、场外为零),再用 Marching Cubes 提取零等值面。因为解的是全局偏微分方程,输出天然水密(没有孔洞),对噪声也稳——误差被方程"平均"掉。代价:对法向定向敏感,且会把不存在的区域也"脑补"封闭起来。
阿尔法形状(阿尔法包络)则是局部思路:以半径阿尔法的球在点云外滚动,滚出来的包络面就是重建表面。阿尔法小则贴合细节但易碎裂,阿尔法大则平滑但丢细节——本质是可调分辨率的凹包。
# 泊松重建与孔洞统计 mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson( pcd, depth=9) print('网格顶点数: %d, 三角面片数: %d' % (len(mesh.vertices), len(mesh.triangles))) # 输出: 网格顶点数: 421375, 三角面片数: 837542 # 密度裁剪:去掉低密度区的"脑补"部分 dens = np.asarray(densities) mesh.remove_vertices_by_mask(dens < np.quantile(dens, 0.03)) print('裁剪后顶点数: %d' % len(mesh.vertices)) # 输出: 裁剪后顶点数: 409704 print('水密性:', mesh.is_watertight()) # 输出: 水密性: False(裁剪后不闭合,属预期)
结果解读:深度九的八叉树解出八十余万面片;密度裁剪掉底部百分之三的低置信区,把泊松"脑补"出来的裙边切掉——这是每次泊松重建的标准收尾。变式:要保留水密性(如做流体仿真)就不裁剪,转而检查边界环。
| 维度 | 泊松重建 | 阿尔法形状 |
|---|---|---|
| 输出性质 | 全局水密 | 局部包络、可有边界 |
| 法向要求 | 高(必须定向) | 低 |
| 噪声鲁棒 | 强 | 弱(噪声直接进网格) |
| 采样不匀 | 均匀化处理 | 细节区易碎裂 |
| 典型场景 | 文物、雕塑整雕 | 地形表面、薄板结构 |
⚠️ 常见坑:拿没定向法向的点云直接跑泊松,得到满是翻转面的废网格且不报错。纪律:重建前先跑上面的"法向体检",均值余弦不达标就重定向。
💡 关键直觉:逐点三角化是"连点成面"的局部手艺,泊松是"解出整片场再切等值线"的全局工程——思路差异如同手绘等高线与解析函数切片。
背景:文物队扫了一尊石兽,交付要求是三维打印文件。操作:滤波(3.1 管线)→ 定向 → 泊松 → 密度裁剪 → 网格简化。关键输出核对如下。
# 交付前质检三连 mesh.compute_vertex_normals() print('流形检查:', mesh.is_edge_manifold()) # 输出: 流形检查: True print('自交检查: 通过' if not mesh.is_self_intersecting() else '自交') simple = mesh.simplify_quadric_decimation(200000) print('简化后面片: %d' % len(simple.triangles)) # 输出: 简化后面片: 200000
结果解读:边流形、无自交、面片二十万以内,满足多数打印服务的水密流形要求。变式:若扫描有遮挡死角(石兽腹部),泊松会封死孔洞——打印没问题,但测绘存档要在成果表里注明"腹部为算法封闭,非实测"。
外业整理收官。下一章转入内业:从照片出发的多视图重建与学习派成图路线。