点云数据曲率计算算法实现

点云数据曲率计算算法实现

1. 基础数据结构和数学库

1.1 点云数据结构 (point_cloud.h)

#ifndef POINT_CLOUD_H
#define POINT_CLOUD_H

#include <vector>
#include <Eigen/Dense>
#include <memory>
#include <queue>
#include <cmath>

// 点结构体
struct Point3D {
    float x, y, z;
    float nx, ny, nz;    // 法向量
    float curvature;     // 曲率
    float principal_k1;  // 主曲率1
    float principal_k2;  // 主曲率2
    float gaussian_k;    // 高斯曲率
    float mean_k;        // 平均曲率
    
    Point3D() : x(0), y(0), z(0), 
                nx(0), ny(0), nz(0),
                curvature(0), principal_k1(0), principal_k2(0),
                gaussian_k(0), mean_k(0) {}
    
    Point3D(float x_, float y_, float z_) : 
        x(x_), y(y_), z(z_), 
        nx(0), ny(0), nz(0),
        curvature(0), principal_k1(0), principal_k2(0),
        gaussian_k(0), mean_k(0) {}
    
    // 转换为Eigen向量
    Eigen::Vector3f toEigen() const { 
        return Eigen::Vector3f(x, y, z); 
    }
    
    // 计算两点间距离
    float distanceTo(const Point3D& p) const {
        float dx = x - p.x;
        float dy = y - p.y;
        float dz = z - p.z;
        return sqrt(dx*dx + dy*dy + dz*dz);
    }
};

// 点云类
class PointCloud {
private:
    std::vector<Point3D> points;
    std::shared_ptr<std::vector<std::vector<int>>> neighbors;  // 邻域索引
    
public:
    PointCloud() = default;
    
    // 添加点
    void addPoint(const Point3D& p) { points.push_back(p); }
    void addPoint(float x, float y, float z) { points.emplace_back(x, y, z); }
    
    // 获取点
    const std::vector<Point3D>& getPoints() const { return points; }
    std::vector<Point3D>& getPoints() { return points; }
    
    // 获取点数量
    size_t size() const { return points.size(); }
    
    // 清空
    void clear() { points.clear(); }
    
    // 获取点的Eigen矩阵表示
    Eigen::MatrixXf toMatrix() const {
        Eigen::MatrixXf mat(points.size(), 3);
        for (size_t i = 0; i < points.size(); i++) {
            mat(i, 0) = points[i].x;
            mat(i, 1) = points[i].y;
            mat(i, 2) = points[i].z;
        }
        return mat;
    }
    
    // 计算包围盒
    void computeBoundingBox(Eigen::Vector3f& min_pt, Eigen::Vector3f& max_pt) const {
        if (points.empty()) return;
        
        min_pt = Eigen::Vector3f(points[0].x, points[0].y, points[0].z);
        max_pt = min_pt;
        
        for (const auto& p : points) {
            min_pt[0] = std::min(min_pt[0], p.x);
            min_pt[1] = std::min(min_pt[1], p.y);
            min_pt[2] = std::min(min_pt[2], p.z);
            
            max_pt[0] = std::max(max_pt[0], p.x);
            max_pt[1] = std::max(max_pt[1], p.y);
            max_pt[2] = std::max(max_pt[2], p.z);
        }
    }
};

1.2 数学工具类 (math_utils.h)

#ifndef MATH_UTILS_H
#define MATH_UTILS_H

#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#include <vector>
#include <cmath>

namespace MathUtils {
    
    // 计算协方差矩阵
    inline Eigen::Matrix3f computeCovarianceMatrix(const std::vector<Eigen::Vector3f>& points, 
                                                  const Eigen::Vector3f& centroid) {
        Eigen::Matrix3f covariance = Eigen::Matrix3f::Zero();
        
        for (const auto& p : points) {
            Eigen::Vector3f diff = p - centroid;
            covariance += diff * diff.transpose();
        }
        
        if (points.size() > 0) {
            covariance /= static_cast<float>(points.size());
        }
        
        return covariance;
    }
    
    // 计算质心
    inline Eigen::Vector3f computeCentroid(const std::vector<Eigen::Vector3f>& points) {
        if (points.empty()) return Eigen::Vector3f::Zero();
        
        Eigen::Vector3f centroid = Eigen::Vector3f::Zero();
        for (const auto& p : points) {
            centroid += p;
        }
        centroid /= static_cast<float>(points.size());
        return centroid;
    }
    
    // 计算质心(从Point3D)
    inline Eigen::Vector3f computeCentroid(const std::vector<Point3D>& points) {
        if (points.empty()) return Eigen::Vector3f::Zero();
        
        Eigen::Vector3f centroid = Eigen::Vector3f::Zero();
        for (const auto& p : points) {
            centroid[0] += p.x;
            centroid[1] += p.y;
            centroid[2] += p.z;
        }
        centroid /= static_cast<float>(points.size());
        return centroid;
    }
    
    // PCA主成分分析
    inline bool computePCA(const Eigen::Matrix3f& covariance,
                          Eigen::Vector3f& eigenvalues,
                          Eigen::Matrix3f& eigenvectors) {
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> solver(covariance);
        if (solver.info() != Eigen::Success) {
            return false;
        }
        
        eigenvalues = solver.eigenvalues();
        eigenvectors = solver.eigenvectors();
        
        // 确保特征值从小到大排序
        // Eigen默认从大到小,我们需要从小到大
        for (int i = 0; i < 2; i++) {
            for (int j = i + 1; j < 3; j++) {
                if (eigenvalues[i] > eigenvalues[j]) {
                    std::swap(eigenvalues[i], eigenvalues[j]);
                    eigenvectors.col(i).swap(eigenvectors.col(j));
                }
            }
        }
        
        return true;
    }
    
    // 计算曲面的最小二乘拟合平面
    inline bool fitPlane(const std::vector<Eigen::Vector3f>& points,
                        Eigen::Vector3f& normal, 
                        Eigen::Vector3f& centroid) {
        if (points.size() < 3) return false;
        
        centroid = computeCentroid(points);
        Eigen::Matrix3f covariance = computeCovarianceMatrix(points, centroid);
        
        // PCA找到最小特征值对应的特征向量(法向量)
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> solver(covariance);
        if (solver.info() != Eigen::Success) {
            return false;
        }
        
        // 最小特征值对应的特征向量
        normal = solver.eigenvectors().col(0);
        
        // 确保法向量指向正确方向(可以统一指向视点)
        return true;
    }
    
    // 计算向量夹角
    inline float angleBetween(const Eigen::Vector3f& v1, const Eigen::Vector3f& v2) {
        float cos_angle = v1.dot(v2) / (v1.norm() * v2.norm());
        cos_angle = std::max(-1.0f, std::min(1.0f, cos_angle));
        return std::acos(cos_angle);
    }
};

2. 曲率计算核心算法

2.1 基于PCA的曲率计算 (pca_curvature.h)

#ifndef PCA_CURVATURE_H
#define PCA_CURVATURE_H

#include "point_cloud.h"
#include "math_utils.h"

class PCACurvature {
private:
    // 邻域搜索方法
    enum NeighborhoodType {
        K_NEAREST,      // K最近邻
        RADIUS_SEARCH   // 半径搜索
    };
    
public:
    // 计算点云的曲率
    static bool compute(PointCloud& cloud, 
                       int k_neighbors = 30,
                       float radius = 0.0f,
                       NeighborhoodType type = K_NEAREST) {
        
        if (cloud.size() < k_neighbors) {
            std::cerr << "点数量少于K近邻数" << std::endl;
            return false;
        }
        
        // 建立KD树加速搜索
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            if (type == K_NEAREST) {
                kdtree.knnSearch(cloud.getPoints()[i], k_neighbors, 
                                neighbor_indices, distances);
            } else {
                kdtree.radiusSearch(cloud.getPoints()[i], radius,
                                   neighbor_indices, distances);
            }
            
            if (neighbor_indices.size() < 3) {
                cloud.getPoints()[i].curvature = 0;
                continue;
            }
            
            // 计算PCA曲率
            float curvature = computePCACurvature(cloud, neighbor_indices, i);
            cloud.getPoints()[i].curvature = curvature;
        }
        
        return true;
    }
    
private:
    // 基于PCA的曲率计算
    static float computePCACurvature(const PointCloud& cloud,
                                    const std::vector<int>& neighbor_indices,
                                    int point_idx) {
        std::vector<Eigen::Vector3f> points;
        points.reserve(neighbor_indices.size());
        
        // 包含中心点
        const Point3D& center = cloud.getPoints()[point_idx];
        points.push_back(center.toEigen());
        
        for (int idx : neighbor_indices) {
            if (idx != point_idx) {  // 不重复添加中心点
                const Point3D& p = cloud.getPoints()[idx];
                points.push_back(p.toEigen());
            }
        }
        
        if (points.size() < 4) return 0.0f;
        
        // 计算质心
        Eigen::Vector3f centroid = MathUtils::computeCentroid(points);
        
        // 计算协方差矩阵
        Eigen::Matrix3f covariance = MathUtils::computeCovarianceMatrix(points, centroid);
        
        // PCA分解
        Eigen::Vector3f eigenvalues;
        Eigen::Matrix3f eigenvectors;
        
        if (!MathUtils::computePCA(covariance, eigenvalues, eigenvectors)) {
            return 0.0f;
        }
        
        // 计算曲率:最小特征值 / 特征值之和
        float sum_eigenvalues = eigenvalues.sum();
        if (sum_eigenvalues < 1e-9f) return 0.0f;
        
        float curvature = eigenvalues[0] / sum_eigenvalues;
        
        // 保存法向量(最小特征值对应的特征向量)
        Eigen::Vector3f normal = eigenvectors.col(0);
        cloud.getPoints()[point_idx].nx = normal[0];
        cloud.getPoints()[point_idx].ny = normal[1];
        cloud.getPoints()[point_idx].nz = normal[2];
        
        return curvature;
    }
    
    // 计算主曲率和高斯曲率
    static bool computePrincipalCurvatures(const PointCloud& cloud,
                                          const std::vector<int>& neighbor_indices,
                                          int point_idx,
                                          float& k1, float& k2,
                                          float& gaussian_k, float& mean_k) {
        const Point3D& center = cloud.getPoints()[point_idx];
        
        // 收集邻域点
        std::vector<Eigen::Vector3f> points;
        for (int idx : neighbor_indices) {
            const Point3D& p = cloud.getPoints()[idx];
            points.push_back(p.toEigen());
        }
        
        if (points.size() < 6) return false;  // 需要足够的点拟合曲面
        
        // 使用二次曲面拟合
        return fitQuadricSurface(points, center.toEigen(),
                                k1, k2, gaussian_k, mean_k);
    }
    
    // 二次曲面拟合
    static bool fitQuadricSurface(const std::vector<Eigen::Vector3f>& points,
                                 const Eigen::Vector3f& center,
                                 float& k1, float& k2,
                                 float& gaussian_k, float& mean_k) {
        // 构建法向量估计
        Eigen::Vector3f normal, centroid;
        if (!MathUtils::fitPlane(points, normal, centroid)) {
            return false;
        }
        
        // 建立局部坐标系
        Eigen::Vector3f u, v, w = normal;
        
        // 选择u轴(与w垂直)
        if (std::abs(w[0]) > std::abs(w[1])) {
            u = Eigen::Vector3f(-w[2], 0, w[0]);
        } else {
            u = Eigen::Vector3f(0, w[2], -w[1]);
        }
        u.normalize();
        
        // 计算v轴
        v = w.cross(u);
        v.normalize();
        
        // 转换到局部坐标系
        Eigen::MatrixXf A(points.size(), 6);
        Eigen::VectorXf b(points.size());
        
        for (size_t i = 0; i < points.size(); i++) {
            Eigen::Vector3f p_local = points[i] - center;
            float x = p_local.dot(u);
            float y = p_local.dot(v);
            float z = p_local.dot(w);
            
            // 二次曲面方程:z = a*x² + b*y² + c*x*y + d*x + e*y + f
            A(i, 0) = x * x;
            A(i, 1) = y * y;
            A(i, 2) = x * y;
            A(i, 3) = x;
            A(i, 4) = y;
            A(i, 5) = 1.0f;
            
            b[i] = z;
        }
        
        // 最小二乘法求解
        Eigen::VectorXf coeffs = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
        
        float a = coeffs[0];
        float b_coef = coeffs[1];
        float c = coeffs[2];
        float d = coeffs[3];
        float e = coeffs[4];
        
        // 计算曲率
        float E = 1.0f + d * d;
        float F = d * e;
        float G = 1.0f + e * e;
        
        float D = std::sqrt(d * d + e * e + 1.0f);
        float L = 2.0f * a / D;
        float M = c / D;
        float N = 2.0f * b_coef / D;
        
        // 计算第一基本形式和第二基本形式的系数
        float coeff = 1.0f / (E * G - F * F);
        
        // 计算主曲率
        float H = 0.5f * coeff * (E * N - 2.0f * F * M + G * L);
        float K = coeff * (L * N - M * M);
        
        // 计算主曲率
        float discriminant = H * H - K;
        if (discriminant < 0) discriminant = 0;
        
        k1 = H + std::sqrt(discriminant);
        k2 = H - std::sqrt(discriminant);
        
        gaussian_k = K;
        mean_k = H;
        
        return true;
    }
};

2.2 KD树加速结构 (kd_tree.h)

#ifndef KD_TREE_H
#define KD_TREE_H

#include "point_cloud.h"
#include <queue>
#include <algorithm>
#include <limits>

struct KdTreeNode {
    Point3D point;
    int index;
    KdTreeNode* left;
    KdTreeNode* right;
    int axis;  // 分割轴: 0=x, 1=y, 2=z
    
    KdTreeNode(const Point3D& p, int idx) : 
        point(p), index(idx), left(nullptr), right(nullptr), axis(0) {}
};

class KdTree {
private:
    KdTreeNode* root;
    std::vector<Point3D> points;
    
public:
    KdTree(const PointCloud& cloud) {
        points = cloud.getPoints();
        root = buildTree(0, points.size(), 0);
    }
    
    ~KdTree() {
        deleteTree(root);
    }
    
    // K近邻搜索
    void knnSearch(const Point3D& query, int k,
                  std::vector<int>& indices,
                  std::vector<float>& distances) {
        indices.clear();
        distances.clear();
        
        std::priority_queue<std::pair<float, KdTreeNode*>> pq;
        
        searchKnn(root, query, k, pq);
        
        while (!pq.empty()) {
            indices.push_back(pq.top().second->index);
            distances.push_back(pq.top().first);
            pq.pop();
        }
        
        std::reverse(indices.begin(), indices.end());
        std::reverse(distances.begin(), distances.end());
    }
    
    // 半径搜索
    void radiusSearch(const Point3D& query, float radius,
                     std::vector<int>& indices,
                     std::vector<float>& distances) {
        indices.clear();
        distances.clear();
        
        searchRadius(root, query, radius, indices, distances);
    }
    
private:
    // 递归构建KD树
    KdTreeNode* buildTree(int start, int end, int depth) {
        if (start >= end) return nullptr;
        
        int axis = depth % 3;
        
        // 按当前轴排序
        if (axis == 0) {
            std::sort(points.begin() + start, points.begin() + end,
                     const Point3D& a, const Point3D& b { return a.x < b.x; });
        } else if (axis == 1) {
            std::sort(points.begin() + start, points.begin() + end,
                     const Point3D& a, const Point3D& b { return a.y < b.y; });
        } else {
            std::sort(points.begin() + start, points.begin() + end,
                     const Point3D& a, const Point3D& b { return a.z < b.z; });
        }
        
        int mid = start + (end - start) / 2;
        KdTreeNode* node = new KdTreeNode(points[mid], mid);
        node->axis = axis;
        
        node->left = buildTree(start, mid, depth + 1);
        node->right = buildTree(mid + 1, end, depth + 1);
        
        return node;
    }
    
    // 递归删除树
    void deleteTree(KdTreeNode* node) {
        if (!node) return;
        deleteTree(node->left);
        deleteTree(node->right);
        delete node;
    }
    
    // K近邻搜索递归
    void searchKnn(KdTreeNode* node, const Point3D& query, int k,
                  std::priority_queue<std::pair<float, KdTreeNode*>>& pq) {
        if (!node) return;
        
        float dist = query.distanceTo(node->point);
        
        if (pq.size() < k) {
            pq.push({dist, node});
        } else if (dist < pq.top().first) {
            pq.pop();
            pq.push({dist, node});
        }
        
        int axis = node->axis;
        float diff = 0;
        
        if (axis == 0) diff = query.x - node->point.x;
        else if (axis == 1) diff = query.y - node->point.y;
        else diff = query.z - node->point.z;
        
        KdTreeNode* first = (diff < 0) ? node->left : node->right;
        KdTreeNode* second = (diff < 0) ? node->right : node->left;
        
        searchKnn(first, query, k, pq);
        
        if (pq.size() < k || diff * diff < pq.top().first) {
            searchKnn(second, query, k, pq);
        }
    }
    
    // 半径搜索递归
    void searchRadius(KdTreeNode* node, const Point3D& query, float radius,
                     std::vector<int>& indices, std::vector<float>& distances) {
        if (!node) return;
        
        float dist = query.distanceTo(node->point);
        if (dist <= radius) {
            indices.push_back(node->index);
            distances.push_back(dist);
        }
        
        int axis = node->axis;
        float diff = 0;
        
        if (axis == 0) diff = query.x - node->point.x;
        else if (axis == 1) diff = query.y - node->point.y;
        else diff = query.z - node->point.z;
        
        KdTreeNode* first = (diff < 0) ? node->left : node->right;
        KdTreeNode* second = (diff < 0) ? node->right : node->left;
        
        searchRadius(first, query, radius, indices, distances);
        
        if (std::abs(diff) <= radius) {
            searchRadius(second, query, radius, indices, distances);
        }
    }
};

3. 高级曲率计算方法

3.1 基于法向变化的曲率 (normal_variation_curvature.h)

#ifndef NORMAL_VARIATION_CURVATURE_H
#define NORMAL_VARIATION_CURVATURE_H

#include "point_cloud.h"
#include "math_utils.h"

class NormalVariationCurvature {
public:
    // 基于法向量变化的曲率估计
    static float compute(const PointCloud& cloud,
                        const std::vector<int>& neighbor_indices,
                        int point_idx) {
        if (neighbor_indices.size() < 3) return 0.0f;
        
        const Point3D& center = cloud.getPoints()[point_idx];
        Eigen::Vector3f center_normal(center.nx, center.ny, center.nz);
        
        if (center_normal.norm() < 1e-6f) {
            return 0.0f;  // 法向量未计算
        }
        
        // 计算法向量变化的平均角度
        float total_angle = 0.0f;
        int valid_count = 0;
        
        for (int idx : neighbor_indices) {
            const Point3D& neighbor = cloud.getPoints()[idx];
            Eigen::Vector3f neighbor_normal(neighbor.nx, neighbor.ny, neighbor.nz);
            
            if (neighbor_normal.norm() < 1e-6f) continue;
            
            float angle = MathUtils::angleBetween(center_normal, neighbor_normal);
            total_angle += angle;
            valid_count++;
        }
        
        if (valid_count == 0) return 0.0f;
        
        float avg_angle = total_angle / valid_count;
        
        // 角度越大,曲率越大
        return avg_angle;
    }
};

3.2 基于曲率张量的方法 (curvature_tensor.h)

#ifndef CURVATURE_TENSOR_H
#define CURVATURE_TENSOR_H

#include "point_cloud.h"
#include "math_utils.h"

class CurvatureTensor {
public:
    // 计算曲率张量
    static bool compute(const PointCloud& cloud,
                       const std::vector<int>& neighbor_indices,
                       int point_idx,
                       Eigen::Matrix3f& curvature_tensor,
                       float& principal_curvature1,
                       float& principal_curvature2,
                       Eigen::Vector3f& principal_dir1,
                       Eigen::Vector3f& principal_dir2) {
        
        if (neighbor_indices.size() < 6) return false;
        
        const Point3D& center = cloud.getPoints()[point_idx];
        Eigen::Vector3f normal(center.nx, center.ny, center.nz);
        
        if (normal.norm() < 1e-6f) return false;
        
        // 构建投影平面
        Eigen::Vector3f u, v, w = normal;
        
        // 选择u轴
        if (std::abs(w[0]) > std::abs(w[1])) {
            u = Eigen::Vector3f(-w[2], 0, w[0]);
        } else {
            u = Eigen::Vector3f(0, w[2], -w[1]);
        }
        u.normalize();
        
        v = w.cross(u);
        v.normalize();
        
        // 收集邻域点
        std::vector<Eigen::Vector3f> local_points;
        for (int idx : neighbor_indices) {
            const Point3D& p = cloud.getPoints()[idx];
            Eigen::Vector3f diff(p.x - center.x, p.y - center.y, p.z - center.z);
            
            float x_local = diff.dot(u);
            float y_local = diff.dot(v);
            float z_local = diff.dot(w);
            
            local_points.emplace_back(x_local, y_local, z_local);
        }
        
        // 构建法向变化矩阵
        Eigen::MatrixXf A(local_points.size(), 2);
        Eigen::VectorXf b(local_points.size());
        
        for (size_t i = 0; i < local_points.size(); i++) {
            float x = local_points[i][0];
            float y = local_points[i][1];
            float z = local_points[i][2];
            
            A(i, 0) = x;
            A(i, 1) = y;
            b[i] = z;
        }
        
        // 最小二乘法拟合平面
        Eigen::Vector2f coeffs = (A.transpose() * A).inverse() * A.transpose() * b;
        
        float a = coeffs[0];
        float b_coef = coeffs[1];
        
        // 构造曲率张量
        curvature_tensor = Eigen::Matrix3f::Zero();
        curvature_tensor(0, 0) = 2.0f * a;
        curvature_tensor(1, 1) = 2.0f * b_coef;
        curvature_tensor(0, 1) = curvature_tensor(1, 0) = 0.0f;
        
        // 提取主曲率和方向
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix2f> solver;
        solver.compute(curvature_tensor.block<2,2>(0,0));
        
        principal_curvature1 = solver.eigenvalues()[0];
        principal_curvature2 = solver.eigenvalues()[1];
        
        principal_dir1 = solver.eigenvectors().col(0)[0] * u + 
                        solver.eigenvectors().col(0)[1] * v;
        principal_dir2 = solver.eigenvectors().col(1)[0] * u + 
                        solver.eigenvectors().col(1)[1] * v;
        
        return true;
    }
};

4. 曲率特征提取和应用

4.1 曲率特征计算器 (curvature_feature.h)

#ifndef CURVATURE_FEATURE_H
#define CURVATURE_FEATURE_H

#include "point_cloud.h"
#include "pca_curvature.h"
#include "normal_variation_curvature.h"
#include "curvature_tensor.h"

class CurvatureFeatureExtractor {
private:
    int k_neighbors;
    float search_radius;
    bool compute_principal_curvatures;
    
public:
    CurvatureFeatureExtractor(int k = 30, float radius = 0.1f, 
                             bool compute_principal = true) :
        k_neighbors(k), search_radius(radius), 
        compute_principal_curvatures(compute_principal) {}
    
    // 计算所有曲率特征
    bool computeFeatures(PointCloud& cloud) {
        if (cloud.size() < k_neighbors) {
            std::cerr << "点云点数不足" << std::endl;
            return false;
        }
        
        // 建立KD树
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            kdtree.knnSearch(cloud.getPoints()[i], k_neighbors, 
                           neighbor_indices, distances);
            
            if (neighbor_indices.size() < 3) {
                continue;
            }
            
            // 1. 计算PCA曲率
            float pca_curvature = PCACurvature::computePCACurvature(
                cloud, neighbor_indices, i);
            cloud.getPoints()[i].curvature = pca_curvature;
            
            // 2. 计算法向量变化曲率
            float normal_variation = NormalVariationCurvature::compute(
                cloud, neighbor_indices, i);
            
            // 3. 如果需要,计算主曲率
            if (compute_principal_curvatures) {
                float k1, k2, gaussian_k, mean_k;
                if (PCACurvature::computePrincipalCurvatures(
                    cloud, neighbor_indices, i, k1, k2, gaussian_k, mean_k)) {
                    
                    cloud.getPoints()[i].principal_k1 = k1;
                    cloud.getPoints()[i].principal_k2 = k2;
                    cloud.getPoints()[i].gaussian_k = gaussian_k;
                    cloud.getPoints()[i].mean_k = mean_k;
                }
            }
            
            // 4. 计算曲率张量
            Eigen::Matrix3f curvature_tensor;
            float pc1, pc2;
            Eigen::Vector3f dir1, dir2;
            
            if (CurvatureTensor::compute(cloud, neighbor_indices, i,
                                        curvature_tensor, pc1, pc2, dir1, dir2)) {
                // 保存张量信息
            }
        }
        
        return true;
    }
    
    // 计算曲率直方图
    std::vector<float> computeCurvatureHistogram(const PointCloud& cloud, 
                                                int bins = 20) {
        std::vector<float> histogram(bins, 0.0f);
        
        if (cloud.size() == 0) return histogram;
        
        // 找到曲率范围
        float min_curvature = std::numeric_limits<float>::max();
        float max_curvature = std::numeric_limits<float>::lowest();
        
        for (const auto& p : cloud.getPoints()) {
            min_curvature = std::min(min_curvature, p.curvature);
            max_curvature = std::max(max_curvature, p.curvature);
        }
        
        if (max_curvature <= min_curvature) {
            return histogram;
        }
        
        float bin_width = (max_curvature - min_curvature) / bins;
        
        // 统计直方图
        for (const auto& p : cloud.getPoints()) {
            int bin_idx = static_cast<int>((p.curvature - min_curvature) / bin_width);
            bin_idx = std::max(0, std::min(bins - 1, bin_idx));
            histogram[bin_idx] += 1.0f;
        }
        
        // 归一化
        float total = cloud.size();
        for (auto& val : histogram) {
            val /= total;
        }
        
        return histogram;
    }
    
    // 基于曲率的点云分割
    std::vector<int> segmentByCurvature(const PointCloud& cloud,
                                       float curvature_threshold,
                                       int min_cluster_size = 10) {
        std::vector<int> labels(cloud.size(), -1);
        int current_label = 0;
        
        // 建立KD树
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            if (labels[i] != -1) continue;
            
            if (cloud.getPoints()[i].curvature < curvature_threshold) {
                // 低曲率区域,开始区域增长
                std::queue<int> queue;
                queue.push(i);
                labels[i] = current_label;
                
                int cluster_size = 0;
                
                while (!queue.empty()) {
                    int current_idx = queue.front();
                    queue.pop();
                    cluster_size++;
                    
                    // 搜索邻域
                    std::vector<int> neighbor_indices;
                    std::vector<float> distances;
                    kdtree.knnSearch(cloud.getPoints()[current_idx], k_neighbors,
                                   neighbor_indices, distances);
                    
                    for (int neighbor_idx : neighbor_indices) {
                        if (labels[neighbor_idx] == -1 && 
                            cloud.getPoints()[neighbor_idx].curvature < curvature_threshold) {
                            labels[neighbor_idx] = current_label;
                            queue.push(neighbor_idx);
                        }
                    }
                }
                
                if (cluster_size >= min_cluster_size) {
                    current_label++;
                } else {
                    // 标记为噪声
                    labels[i] = -1;
                }
            }
        }
        
        return labels;
    }
    
    // 计算曲率梯度
    std::vector<float> computeCurvatureGradient(const PointCloud& cloud) {
        std::vector<float> gradient(cloud.size(), 0.0f);
        
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            kdtree.knnSearch(cloud.getPoints()[i], 6,  // 使用6个最近邻
                           neighbor_indices, distances);
            
            float max_gradient = 0.0f;
            float center_curvature = cloud.getPoints()[i].curvature;
            
            for (int j = 0; j < neighbor_indices.size(); j++) {
                int neighbor_idx = neighbor_indices[j];
                if (neighbor_idx == i) continue;
                
                float neighbor_curvature = cloud.getPoints()[neighbor_idx].curvature;
                float distance = distances[j];
                
                if (distance > 1e-6f) {
                    float grad = std::abs(center_curvature - neighbor_curvature) / distance;
                    max_gradient = std::max(max_gradient, grad);
                }
            }
            
            gradient[i] = max_gradient;
        }
        
        return gradient;
    }
};

5. 应用示例

5.1 主程序示例 (main.cpp)

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "point_cloud.h"
#include "curvature_feature.h"

// 从PLY文件读取点云
bool readPLY(const std::string& filename, PointCloud& cloud) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        std::cerr << "无法打开文件: " << filename << std::endl;
        return false;
    }
    
    std::string line;
    int vertex_count = 0;
    bool in_header = true;
    
    // 读取PLY头
    while (in_header && std::getline(file, line)) {
        if (line.find("element vertex") != std::string::npos) {
            std::sscanf(line.c_str(), "element vertex %d", &vertex_count);
        } else if (line == "end_header") {
            in_header = false;
        }
    }
    
    if (vertex_count == 0) {
        std::cerr << "未找到顶点信息" << std::endl;
        return false;
    }
    
    // 读取顶点数据
    for (int i = 0; i < vertex_count; i++) {
        float x, y, z;
        if (!(file >> x >> y >> z)) {
            std::cerr << "读取顶点数据失败" << std::endl;
            return false;
        }
        cloud.addPoint(x, y, z);
        
        // 跳过可能的颜色、法向量等信息
        std::getline(file, line);
    }
    
    file.close();
    std::cout << "读取了 " << cloud.size() << " 个点" << std::endl;
    return true;
}

// 保存点云和曲率到文件
bool saveWithCurvature(const std::string& filename, const PointCloud& cloud) {
    std::ofstream file(filename);
    if (!file.is_open()) {
        std::cerr << "无法创建文件: " << filename << std::endl;
        return false;
    }
    
    // 写入PLY头
    file << "ply\n";
    file << "format ascii 1.0\n";
    file << "element vertex " << cloud.size() << "\n";
    file << "property float x\n";
    file << "property float y\n";
    file << "property float z\n";
    file << "property float nx\n";
    file << "property float ny\n";
    file << "property float nz\n";
    file << "property float curvature\n";
    file << "property float principal_k1\n";
    file << "property float principal_k2\n";
    file << "property float gaussian_k\n";
    file << "property float mean_k\n";
    file << "end_header\n";
    
    // 写入数据
    for (const auto& p : cloud.getPoints()) {
        file << p.x << " " << p.y << " " << p.z << " "
             << p.nx << " " << p.ny << " " << p.nz << " "
             << p.curvature << " "
             << p.principal_k1 << " " << p.principal_k2 << " "
             << p.gaussian_k << " " << p.mean_k << "\n";
    }
    
    file.close();
    std::cout << "已保存到: " << filename << std::endl;
    return true;
}

// 生成测试点云(球体)
void generateSphere(PointCloud& cloud, float radius = 1.0f, int points = 1000) {
    for (int i = 0; i < points; i++) {
        // 球面坐标
        float u = static_cast<float>(rand()) / RAND_MAX;
        float v = static_cast<float>(rand()) / RAND_MAX;
        
        float theta = 2.0f * M_PI * u;
        float phi = std::acos(2.0f * v - 1.0f);
        
        float x = radius * std::sin(phi) * std::cos(theta);
        float y = radius * std::sin(phi) * std::sin(theta);
        float z = radius * std::cos(phi);
        
        cloud.addPoint(x, y, z);
    }
}

// 生成测试点云(平面+球体)
void generateMixed(PointCloud& cloud) {
    int total_points = 2000;
    
    // 平面部分
    for (int i = 0; i < total_points / 2; i++) {
        float x = static_cast<float>(rand()) / RAND_MAX * 4.0f - 2.0f;
        float y = static_cast<float>(rand()) / RAND_MAX * 4.0f - 2.0f;
        float z = 0.0f;
        
        cloud.addPoint(x, y, z);
    }
    
    // 球体部分
    float radius = 1.0f;
    for (int i = 0; i < total_points / 2; i++) {
        float u = static_cast<float>(rand()) / RAND_MAX;
        float v = static_cast<float>(rand()) / RAND_MAX;
        
        float theta = 2.0f * M_PI * u;
        float phi = std::acos(2.0f * v - 1.0f);
        
        float x = radius * std::sin(phi) * std::cos(theta) + 3.0f;
        float y = radius * std::sin(phi) * std::sin(theta);
        float z = radius * std::cos(phi);
        
        cloud.addPoint(x, y, z);
    }
}

int main() {
    PointCloud cloud;
    
    // 生成测试点云
    std::cout << "生成测试点云..." << std::endl;
    generateMixed(cloud);
    
    // 计算曲率特征
    std::cout << "计算曲率特征..." << std::endl;
    CurvatureFeatureExtractor extractor(20, 0.2f, true);
    
    if (!extractor.computeFeatures(cloud)) {
        std::cerr << "曲率计算失败" << std::endl;
        return 1;
    }
    
    // 计算曲率直方图
    std::vector<float> histogram = extractor.computeCurvatureHistogram(cloud, 20);
    std::cout << "曲率直方图:" << std::endl;
    for (size_t i = 0; i < histogram.size(); i++) {
        std::cout << "Bin " << i << ": " << histogram[i] << std::endl;
    }
    
    // 基于曲率分割
    float curvature_threshold = 0.1f;
    std::vector<int> labels = extractor.segmentByCurvature(cloud, curvature_threshold, 10);
    
    int num_clusters = 0;
    for (int label : labels) {
        if (label > num_clusters) num_clusters = label;
    }
    std::cout << "找到 " << num_clusters + 1 << " 个簇" << std::endl;
    
    // 计算曲率梯度
    std::vector<float> gradient = extractor.computeCurvatureGradient(cloud);
    
    // 统计结果
    float min_curvature = std::numeric_limits<float>::max();
    float max_curvature = std::numeric_limits<float>::lowest();
    float avg_curvature = 0.0f;
    
    for (const auto& p : cloud.getPoints()) {
        min_curvature = std::min(min_curvature, p.curvature);
        max_curvature = std::max(max_curvature, p.curvature);
        avg_curvature += p.curvature;
    }
    avg_curvature /= cloud.size();
    
    std::cout << "\n曲率统计:" << std::endl;
    std::cout << "最小曲率: " << min_curvature << std::endl;
    std::cout << "最大曲率: " << max_curvature << std::endl;
    std::cout << "平均曲率: " << avg_curvature << std::endl;
    
    // 保存结果
    saveWithCurvature("output_with_curvature.ply", cloud);
    
    return 0;
}

6. CMake配置文件

cmake_minimum_required(VERSION 3.10)
project(PointCloudCurvature)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找Eigen3
find_package(Eigen3 REQUIRED)

# 包含目录
include_directories(${EIGEN3_INCLUDE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

# 源文件
set(SOURCES
    src/main.cpp
    src/point_cloud.cpp
    src/pca_curvature.cpp
    src/kd_tree.cpp
    src/curvature_feature.cpp
    src/normal_variation_curvature.cpp
    src/curvature_tensor.cpp
)

# 头文件
set(HEADERS
    include/point_cloud.h
    include/math_utils.h
    include/pca_curvature.h
    include/kd_tree.h
    include/curvature_feature.h
    include/normal_variation_curvature.h
    include/curvature_tensor.h
)

# 可执行文件
add_executable(point_cloud_curvature ${SOURCES} ${HEADERS})

# 链接Eigen
target_link_libraries(point_cloud_curvature Eigen3::Eigen)

# 启用优化
if(CMAKE_BUILD_TYPE STREQUAL "Release")
    add_compile_options(-O3 -march=native)
else()
    add_compile_options(-O0 -g)
endif()

7. 算法比较和选择

7.1 不同方法的比较

class CurvatureComparison {
public:
    // 比较不同算法的计算时间和精度
    static void compareMethods(const PointCloud& cloud, 
                              int k_neighbors = 20,
                              int iterations = 10) {
        
        PointCloud cloud_copy = cloud;
        KdTree kdtree(cloud);
        
        // 准备测试点
        std::vector<int> test_indices;
        for (int i = 0; i < std::min(100, (int)cloud.size()); i++) {
            test_indices.push_back(i);
        }
        
        // 测试PCA方法
        auto start = std::chrono::high_resolution_clock::now();
        for (int iter = 0; iter < iterations; iter++) {
            for (int idx : test_indices) {
                std::vector<int> neighbor_indices;
                std::vector<float> distances;
                kdtree.knnSearch(cloud.getPoints()[idx], k_neighbors,
                               neighbor_indices, distances);
                
                float curvature = PCACurvature::computePCACurvature(
                    cloud, neighbor_indices, idx);
            }
        }
        auto end = std::chrono::high_resolution_clock::now();
        auto pca_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
        
        // 测试法向量变化方法
        start = std::chrono::high_resolution_clock::now();
        for (int iter = 0; iter < iterations; iter++) {
            for (int idx : test_indices) {
                std::vector<int> neighbor_indices;
                std::vector<float> distances;
                kdtree.knnSearch(cloud.getPoints()[idx], k_neighbors,
                               neighbor_indices, distances);
                
                float curvature = NormalVariationCurvature::compute(
                    cloud, neighbor_indices, idx);
            }
        }
        end = std::chrono::high_resolution_clock::now();
        auto normal_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
        
        std::cout << "算法性能比较:" << std::endl;
        std::cout << "PCA方法: " << pca_time.count() << " ms" << std::endl;
        std::cout << "法向量变化方法: " << normal_time.count() << " ms" << std::endl;
    }
    
    // 计算不同邻域大小的影响
    static void analyzeNeighborhoodEffect(PointCloud& cloud) {
        std::vector<int> k_values = {5, 10, 20, 30, 50, 100};
        std::vector<float> avg_curvatures;
        
        for (int k : k_values) {
            CurvatureFeatureExtractor extractor(k, 0.1f, false);
            
            PointCloud test_cloud = cloud;
            extractor.computeFeatures(test_cloud);
            
            float total_curvature = 0.0f;
            for (const auto& p : test_cloud.getPoints()) {
                total_curvature += p.curvature;
            }
            avg_curvatures.push_back(total_curvature / test_cloud.size());
        }
        
        std::cout << "邻域大小对平均曲率的影响:" << std::endl;
        for (size_t i = 0; i < k_values.size(); i++) {
            std::cout << "K=" << k_values[i] << ": " << avg_curvatures[i] << std::endl;
        }
    }
};

参考代码 点云数据计算曲率的算法实现 www.youewnfan.com/contentcnv/71129.html

8. 实用工具函数

8.1 曲率滤波和增强

class CurvatureProcessor {
public:
    // 高斯滤波平滑曲率
    static void smoothCurvature(PointCloud& cloud, int k_neighbors = 20, 
                               float sigma = 0.5f) {
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            kdtree.knnSearch(cloud.getPoints()[i], k_neighbors,
                           neighbor_indices, distances);
            
            float sum_weight = 0.0f;
            float sum_curvature = 0.0f;
            
            for (size_t j = 0; j < neighbor_indices.size(); j++) {
                float dist = distances[j];
                float weight = std::exp(-dist * dist / (2.0f * sigma * sigma));
                
                int idx = neighbor_indices[j];
                sum_curvature += weight * cloud.getPoints()[idx].curvature;
                sum_weight += weight;
            }
            
            if (sum_weight > 1e-6f) {
                cloud.getPoints()[i].curvature = sum_curvature / sum_weight;
            }
        }
    }
    
    // 基于曲率的点云下采样
    static PointCloud downsampleByCurvature(const PointCloud& cloud,
                                          float curvature_threshold = 0.1f,
                                          float min_distance = 0.05f) {
        PointCloud result;
        std::vector<bool> keep_point(cloud.size(), false);
        
        // 优先保留高曲率点
        for (size_t i = 0; i < cloud.size(); i++) {
            if (cloud.getPoints()[i].curvature > curvature_threshold) {
                keep_point[i] = true;
            }
        }
        
        // 对低曲率区域进行下采样
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            if (keep_point[i]) {
                result.addPoint(cloud.getPoints()[i]);
                continue;
            }
            
            // 检查是否已经有附近的高曲率点
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            kdtree.radiusSearch(cloud.getPoints()[i], min_distance,
                              neighbor_indices, distances);
            
            bool has_high_curvature = false;
            for (int idx : neighbor_indices) {
                if (keep_point[idx]) {
                    has_high_curvature = true;
                    break;
                }
            }
            
            if (!has_high_curvature) {
                result.addPoint(cloud.getPoints()[i]);
            }
        }
        
        return result;
    }
    
    // 提取高曲率特征点
    static std::vector<int> extractFeaturePoints(const PointCloud& cloud,
                                               float curvature_threshold = 0.3f,
                                               int min_neighbors = 5) {
        std::vector<int> feature_indices;
        KdTree kdtree(cloud);
        
        for (size_t i = 0; i < cloud.size(); i++) {
            if (cloud.getPoints()[i].curvature < curvature_threshold) {
                continue;
            }
            
            // 检查是否为局部最大值
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            kdtree.knnSearch(cloud.getPoints()[i], min_neighbors + 1,
                           neighbor_indices, distances);
            
            bool is_local_max = true;
            float center_curvature = cloud.getPoints()[i].curvature;
            
            for (size_t j = 1; j < neighbor_indices.size(); j++) {  // 从1开始,跳过自身
                int idx = neighbor_indices[j];
                if (cloud.getPoints()[idx].curvature > center_curvature) {
                    is_local_max = false;
                    break;
                }
            }
            
            if (is_local_max) {
                feature_indices.push_back(i);
            }
        }
        
        return feature_indices;
    }
};

9. 注意事项和优化建议

9.1 参数选择指南

class ParameterGuide {
public:
    // 根据点云密度自动选择K值
    static int autoSelectK(const PointCloud& cloud) {
        if (cloud.size() < 100) return 10;
        
        // 计算点云密度
        Eigen::Vector3f min_pt, max_pt;
        cloud.computeBoundingBox(min_pt, max_pt);
        
        Eigen::Vector3f diag = max_pt - min_pt;
        float volume = diag[0] * diag[1] * diag[2];
        float density = cloud.size() / volume;
        
        // 根据密度选择K
        if (density < 100) return 10;      // 稀疏点云
        else if (density < 1000) return 20; // 中等密度
        else if (density < 10000) return 30; // 高密度
        else return 50;                     // 超高密度
    }
    
    // 根据噪声水平选择平滑参数
    static float autoSelectSigma(const PointCloud& cloud) {
        if (cloud.size() < 10) return 0.1f;
        
        // 计算平均最近邻距离
        KdTree kdtree(cloud);
        float total_distance = 0.0f;
        int count = 0;
        
        for (size_t i = 0; i < std::min(100, (int)cloud.size()); i++) {
            std::vector<int> neighbor_indices;
            std::vector<float> distances;
            
            kdtree.knnSearch(cloud.getPoints()[i], 2,  // 自身+最近邻
                           neighbor_indices, distances);
            
            if (distances.size() > 1) {
                total_distance += distances[1];  // 最近邻距离
                count++;
            }
        }
        
        float avg_distance = total_distance / count;
        return avg_distance * 2.0f;  // sigma设为平均距离的2倍
    }
};

10. 编译和使用

10.1 编译命令

# 创建构建目录
mkdir build
cd build

# 使用CMake配置
cmake ..

# 编译
make -j4

# 运行程序
./point_cloud_curvature

10.2 输入数据格式

程序支持以下格式:

  1. PLY格式(ASCII)
  2. XYZ格式(每行x y z)
  3. 程序内生成测试数据

10.3 输出结果

程序会输出:

  1. 每个点的曲率
  2. 主曲率(k1, k2)
  3. 高斯曲率和平均曲率
  4. 法向量
  5. 曲率直方图
  6. 基于曲率的分割结果

专注于matlab/simulink,电子电路,编程