PCL之RANSAC实践

cpp 复制代码
#include <iostream>
#include <vector>
#include <random>
#include <cmath>
#include <limits>
#include <iomanip>

struct Point2D
{
    double x;
    double y;
};

struct LineModel
{
    // 直线一般式: ax + by + c = 0
    double a = 0.0;
    double b = 0.0;
    double c = 0.0;
    bool valid = false;
};

struct RansacResult
{
    LineModel bestModel;
    std::vector<int> bestInlierIndices;
};

static const int kNumInliers = 80;
static const int kNumOutliers = 30;
static const double kTrueSlope = 2.0;    // 理想直线方程
static const double kTrueIntercept = 1.0;
static const double kNoiseSigma = 0.15;

static const int kMaxIterations = 200;
static const double kDistanceThreshold = 0.20;
static const unsigned int kRandomSeed = 42;

// ========== 数学工具 ==========

double DistancePointToLine(const Point2D& p, const LineModel& line)
{
    double denom = std::sqrt(line.a * line.a + line.b * line.b);
    if (denom < 1e-12)
    {
        return std::numeric_limits<double>::max();
    }
    return std::fabs(line.a * p.x + line.b * p.y + line.c) / denom;
}

LineModel BuildLineFromTwoPoints(const Point2D& p1, const Point2D& p2)
{
    LineModel line;

    double dx = p2.x - p1.x;
    double dy = p2.y - p1.y;

    // 两点过近,认为退化
    if (std::sqrt(dx * dx + dy * dy) < 1e-12)
    {
        line.valid = false;
        return line;
    }

    line.a = p1.y - p2.y;
    line.b = p2.x - p1.x;
    line.c = p1.x * p2.y - p2.x * p1.y;
    line.valid = true;
    return line;
}

// 使用最小二乘拟合 y = kx + b
// 注意:这个版本适合非竖直直线,用来做本实验的精拟合足够了
bool FitLineLeastSquares(const std::vector<Point2D>& points, const std::vector<int>& indices, double& k, double& b)
{
    if (indices.size() < 2)
    {
        return false;
    }

    double sumX = 0.0;
    double sumY = 0.0;
    double sumXX = 0.0;
    double sumXY = 0.0;
    const int n = static_cast<int>(indices.size());

    for (int idx : indices)
    {
        const Point2D& p = points[idx];
        sumX += p.x;
        sumY += p.y;
        sumXX += p.x * p.x;
        sumXY += p.x * p.y;
    }

    double denom = n * sumXX - sumX * sumX;
    if (std::fabs(denom) < 1e-12)
    {
        return false;
    }

    k = (n * sumXY - sumX * sumY) / denom;
    b = (sumY - k * sumX) / n;
    return true;
}

// ========== 数据生成 ==========

std::vector<Point2D> GenerateTestData()
{
    std::vector<Point2D> points;
    points.reserve(kNumInliers + kNumOutliers);

    std::mt19937 rng(kRandomSeed);
    std::uniform_real_distribution<double> xDist(-5.0, 5.0);
    std::normal_distribution<double> noiseDist(0.0, kNoiseSigma);
    std::uniform_real_distribution<double> outlierDist(-8.0, 8.0);

    // 生成内点:落在真实直线附近
    for (int i = 0; i < kNumInliers; ++i)
    {
        double x = xDist(rng);
        double y = kTrueSlope * x + kTrueIntercept + noiseDist(rng);
        points.push_back({x, y});
    }

    // 生成外点:随机散布
    for (int i = 0; i < kNumOutliers; ++i)
    {
        double x = outlierDist(rng);
        double y = outlierDist(rng);
        points.push_back({x, y});
    }

    return points;
}

// ========== RANSAC 主体 ==========

RansacResult RunRansacLine2D(const std::vector<Point2D>& points)
{
    RansacResult result;

    if (points.size() < 2)
    {
        return result;
    }

    std::mt19937 rng(kRandomSeed + 123);
    std::uniform_int_distribution<int> idxDist(0, static_cast<int>(points.size()) - 1);

    int bestInlierCount = -1;

    for (int iter = 0; iter < kMaxIterations; ++iter)
    {
        // 1. 随机取两个不同点
        int idx1 = idxDist(rng);
        int idx2 = idxDist(rng);
        while (idx2 == idx1)
        {
            idx2 = idxDist(rng);
        }

        const Point2D& p1 = points[idx1];
        const Point2D& p2 = points[idx2];

        // 2. 构造候选直线
        LineModel candidate = BuildLineFromTwoPoints(p1, p2);
        if (!candidate.valid)
        {
            continue;
        }

        // 3. 统计内点
        std::vector<int> inliers;
        for (int i = 0; i < static_cast<int>(points.size()); ++i)
        {
            double dist = DistancePointToLine(points[i], candidate);
            if (dist < kDistanceThreshold)
            {
                inliers.push_back(i);
            }
        }

        // 4. 更新最优模型
        if (static_cast<int>(inliers.size()) > bestInlierCount)
        {
            bestInlierCount = static_cast<int>(inliers.size());
            result.bestModel = candidate;
            result.bestInlierIndices = inliers;
        }
    }

    return result;
}

// ========== 主函数 ==========

int main()
{
    std::vector<Point2D> points = GenerateTestData();

    RansacResult ransacResult = RunRansacLine2D(points);

    std::cout << std::fixed << std::setprecision(6);
    std::cout << "总点数: " << points.size() << std::endl;
    std::cout << "RANSAC 最佳内点数: " << ransacResult.bestInlierIndices.size() << std::endl;

    if (ransacResult.bestModel.valid)
    {
        std::cout << "\nRANSAC 粗模型 (ax + by + c = 0):" << std::endl;
        std::cout << "a = " << ransacResult.bestModel.a << std::endl;
        std::cout << "b = " << ransacResult.bestModel.b << std::endl;
        std::cout << "c = " << ransacResult.bestModel.c << std::endl;
    }

    double k = 0.0;
    double b = 0.0;
    bool ok = FitLineLeastSquares(points, ransacResult.bestInlierIndices, k, b);

    if (ok)
    {
        std::cout << "\n基于最佳内点集做最小二乘精拟合:" << std::endl;
        std::cout << "y = " << k << " * x + " << b << std::endl;
    }
    else
    {
        std::cout << "\n精拟合失败。" << std::endl;
    }

    std::cout << "\n真实直线:" << std::endl;
    std::cout << "y = " << kTrueSlope << " * x + " << kTrueIntercept << std::endl;

    return 0;
}
相关推荐
凤凰院凶涛QAQ25 分钟前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
w_zero_one2 小时前
算法--模拟
java·数据结构·算法
秋天的怀念_David3 小时前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
秋天的怀念_David3 小时前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
薛定e的猫咪3 小时前
(ICLR 2020)MAML 靠的不是快速学习,而是特征复用
人工智能·深度学习·学习·算法·机器学习
薛定e的猫咪4 小时前
(ICLR 2026)DF-META:面向 FJSP 的无导数元学习实例自适应调度
人工智能·深度学习·学习·算法·机器学习
小羊没烦恼!5 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
wanderist.6 小时前
202609CSP认证P3
算法
云淡风轻~窗明几净6 小时前
角谷猜想最接近严格证明的测试 Version1.1.1
人工智能·算法·机器学习·图论
机器学习之心6 小时前
PINN × SVM × NSGA-II × 熵权TOPSIS:一套跑通「建模—优化—决策」的多目标优化框架
算法·机器学习·支持向量机