3、点云特征提取:法向量估计、PFH与FPFH特征、点云曲率计算

点云特征提取,说白了就是给每个点“做体检”。

你想想看,原始点云就是一坨坐标点,计算机根本不知道哪里是平面、哪里是拐角、哪里是边缘。我们要做的,就是把这些几何属性算出来。我个人习惯把这一步叫做“点云的灵魂注入”——没有特征的点云,就是一堆死数据。

3.1 法向量估计:点云的“朝向”密码

法向量是什么?就是垂直于局部曲面的方向向量。嗯,这里要注意:法向量不是点的固有属性,而是它所在局部曲面的属性。

估计方法:PCA(主成分分析)

最常用的方法就是PCA。思路很简单:

  1. 对每个点,找它的k个近邻(或者半径r内的邻居)
  2. 计算这些邻居点的协方差矩阵
  3. 求协方差矩阵的特征值和特征向量
  4. 最小特征值对应的特征向量,就是法向量方向

我在项目中遇到过一个问题:法向量方向不统一。有的朝里,有的朝外,这会导致后续分割直接崩掉。解决办法是——做方向一致性调整,让所有法向量都指向视点方向。

核心公式:
协方差矩阵 C = (1/k) * Σ(pi - p̄)(pi - p̄)T
其中 p̄ 是邻域质心,pi 是邻域点
// 伪代码:法向量估计
for each point p in cloud:
    neighbors = kdtree.radius_search(p, radius)
    centroid = mean(neighbors)
    covariance = compute_covariance(neighbors, centroid)
    eigenvalues, eigenvectors = eigen_decompose(covariance)
    normal = eigenvectors[:, 2]  // 最小特征值对应特征向量
    // 方向一致性调整
    if dot(normal, view_direction) < 0:
        normal = -normal
我的经验:k值的选择很关键。k太小,法向量对噪声敏感;k太大,会平滑掉细节特征。我一般取k=20~50,具体看点云密度。对于室内场景,半径法比k近邻法更稳定。

3.2 PFH与FPFH:点特征直方图

法向量只是基础特征。真正做分割和识别时,我们需要更丰富的描述子。PFH(Point Feature Histogram)就是其中之一。

PFH的核心思想:

对每个点,计算它和邻域内所有其他点对的几何关系。说白了,就是看两个点之间的法向量夹角、距离等,然后统计成直方图。

PFH用了四个角度特征:

  • α:法向量之间的夹角
  • φ:法向量与点对连线的夹角
  • θ:法向量在连线方向上的投影夹角
  • d:两点之间的欧氏距离

但是PFH有个大问题——计算量太大!每个点要和邻域内所有点配对,复杂度是O(n·k²)。我当年做室内场景分割时,用PFH跑了整整一个晚上,结果第二天发现FPFH只需要半小时。

FPFH(Fast Point Feature Histogram)的改进:

FPFH做了两件事来提速:

  1. 只计算查询点和邻居之间的特征(SPFH)
  2. 再对邻居的SPFH做加权求和

复杂度降到了O(n·k),而且效果几乎没差。说白了,FPFH就是PFH的“轻量版”,但保留了核心的几何区分能力。

FPFH计算公式:
FPFH(p) = SPFH(p) + (1/k) * Σ (1/ωi) * SPFH(pi)
其中 ωi 是查询点与邻居pi的距离
// 伪代码:FPFH计算
for each point p:
    // 第一步:计算SPFH
    for each neighbor n in p's neighbors:
        compute_angular_features(p, n)
        update_SPFH_histogram(p, features)
    
    // 第二步:加权邻居SPFH
    for each neighbor n in p's neighbors:
        weight = 1.0 / distance(p, n)
        FPFH[p] += weight * SPFH[n]
    
    FPFH[p] /= sum(weights)
避坑指南:我曾经在点云配准项目里直接用FPFH做匹配,结果发现两个完全不同的平面居然有相似的FPFH特征。后来才意识到——FPFH对平面区域区分度不够,需要结合曲率或位置信息一起用。

3.3 点云曲率计算:识别“拐弯”的地方

曲率,说白了就是曲面弯曲的程度。平面曲率为0,球面曲率为常数,边缘处曲率突变。

曲率估计方法:

还记得前面PCA算出来的特征值吗?三个特征值λ1 ≥ λ2 ≥ λ3,它们藏着曲率信息。

最常用的曲率指标是:

  • 表面曲率: σ = λ3 / (λ1 + λ2 + λ3)
  • 高斯曲率: K = λ1 * λ2 * λ3(近似)
  • 平均曲率: H = (λ1 + λ2) / 2(近似)

我个人习惯用表面曲率σ,因为它归一化到0~1之间,阈值好设。平面区域σ接近0,边缘区域σ会突然变大。

实用技巧:在做点云分割时,我经常先用曲率做“边缘检测”——曲率大的点标记为候选边界点,然后做区域生长。这样比直接聚类快得多,而且边界更清晰。
// 曲率计算示例
def compute_curvature(neighbors):
    centroid = np.mean(neighbors, axis=0)
    cov = np.cov((neighbors - centroid).T)
    eigenvalues, _ = np.linalg.eigh(cov)
    eigenvalues = np.sort(eigenvalues)  # 升序排列
    curvature = eigenvalues[0] / np.sum(eigenvalues)
    return curvature

3.4 知识体系总览

下面这张图,是我自己梳理的特征提取流程。你一看就明白这三者之间的关系了。

点云特征提取知识体系 原始点云 法向量估计(PCA) PFH / FPFH 曲率计算 其他几何特征 特征向量 → 分割/聚类/配准 三者层层递进:法向量是基础,PFH/FPFH是高级描述,曲率是局部几何度量

你看这个流程就清楚了:法向量是地基,PFH/FPFH是上层建筑,曲率是辅助判断。三者配合使用,才能做好点云分割和聚类。

总结一下:
  • 法向量:每个点的朝向信息,PCA估计,注意方向一致性
  • PFH/FPFH:局部几何描述子,FPFH是PFH的加速版,适合大规模点云
  • 曲率:弯曲程度度量,用PCA特征值计算,适合做边缘检测

嗯,特征提取这部分就讲到这里。实际项目中,我建议你先用法向量+曲率做快速分割,如果效果不够好,再上FPFH。别一上来就全量计算,点云数据量大的时候,计算时间会让你怀疑人生的。

专注资料整理