C++实现ransac

目录

一、ransac算法原理

1.1、算法概念

1.2、图解

二、c++实现ransac

2.1、设置随机样本和离群点

2.2、随机抽取样本

2.3、内点计算

2.4、更新参数

2.2、完整代码


一、ransac算法原理

1.1、算法概念

        随机抽样一致性 (RANSAC) 是一种迭代方法,用于根据一组包含异常值的观测数据来估计数学模型的参数,此时异常值不会对估计值产生影响。 因此,它也可以解释为一种异常值检测方法,说白了就是能剔除异常的、离群的样本。 它是一种非确定性算法,因为它仅以一定的概率产生合理的结果,并且随着允许更多迭代,该概率会增加,说白了就是由于每次随机抽取样本,最终都会得到更好的结果,但是每次执行整个ransac算法,最终结果会有略微差异。 该算法最初由 Fischler 和 Bolles 在 SRI International 于 1981 年发布。他们使用 RANSAC 来解决位置确定问题 (LDP),其目标是确定空间中投影到图像上的点,形成一组地标 已知地点。

        一个基本假设是数据由“内点”和“离群值”组成,“内点”是指其分布可以通过某些模型参数集来解释的数据,尽管可能会受到噪声的影响;“离群值”是不适合模型的数据。 例如,异常值可能来自噪声的极值、错误的测量或有关数据解释的错误假设。 RANSAC 还假设,给定一组(通常很小的)内点,存在一个可以估计模型参数的过程,该模型可以最佳地解释或拟合该数据。

伪代码:

Given:
    data – a set of observations
    model – a model to explain observed data points
    n – minimum number of data points required to estimate model parameters
    k – maximum number of iterations allowed in the algorithm
    t – threshold value to determine data points that are fit well by model
    d – number of close data points required to assert that a model fits well to data

Return:
    bestFit – model parameters which best fit the data (or nul if no good model is found)

iterations = 0
bestFit = nul
bestErr = something really large
while iterations < k {
    maybeInliers = n randomly selected values from data
    maybeModel = model parameters fitted to maybeInliers
    alsoInliers = empty set
    for every point in data not in maybeInliers {
        if point fits maybeModel with an error smaller than t
             add point to alsoInliers
    }
    if the number of elements in alsoInliers is > d {
        % this implies that we may have found a good model
        % now test how good it is
        betterModel = model parameters fitted to all points in maybeInliers and alsoInliers
        thisErr = a measure of how well betterModel fits these points
        if thisErr < bestErr {
            bestFit = betterModel
            bestErr = thisErr
        }
    }
    increment iterations
}
return bestFit

1.2、图解

        如下图,以直线拟合为例子,假如给定N个点,求解直线方程y=kx+b。这里使用ransac算法求解直线方程参数k、b,主要思想如下:

  • step1:随机选取两个样本点,如图红点
  • step2:计算直线方程参数ki、bi,得到直线记为Li,此时有k=ki,b=bi
  • step3:计算其余所有点到直线Li的距离,距离Li足够近的点为内点(inliers),比较远的点为外点(outliers)
  • step4:执行step1、step2得到直线Lj(对应方程参数kj、bj);执行step3得到的inliers如果大于直线Li,那么更新参数为:k=kj,b=bj。

        依次类推,最多循环迭代100次,只要下一次计算得到直线比上一次更优,那就更新参数k、b;直到内点比例足够高或者循环到100次了为止。

二、c++实现ransac

依赖opencv的一点点数据结构,你也可以自己去掉。

2.1、设置随机样本和离群点

如下图,红色点就是样本点,绿色的就是离群点

// time seed
srand((unsigned int)time(nullptr));
// <1>产生随机数
vector<Point2d> points;
// 精确解
const int k = 1;
const int b = 10;

// x [0, 390]; y [10, 400] total_numbers = 40
for (int x = 0; x < 400; x += 10)
{
	points.push_back(Point2d(x, k * x + b + Ransac::getRand(0, 60)));
}
//添加离群点
points[35].y = 100;
points[36].y = 200;
points[37].y = 200;
points[38].y = 120;
points[39].y = 110;

2.2、随机抽取样本

随机选取两个样本点的索引。

// <1>、随机抽取两个样本
int index_first = Ransac::getRand(0, points.size() - 2);
int index_second = Ransac::getRand(0, points.size() - 2);
sample_first = points[index_first];
sample_second = points[index_second];

2.3、内点计算

y_error实在计算点到直线距离,如果小于y_threshold,也就是内点inliers。

// <2>、根据距离,来找出所有样本点中的内点,并统计数量
for (int i = 0; i < points.size(); i++)
{
	// delta = k * x  + b - y
	double y_error = abs(k_estimate * points[i].x + b_estimate - points[i].y) / sqrt((pow(k_estimate, 2) + 1));
	//cout << "y_error = " << y_error << endl;
	if (y_error < y_threshold)
	{
		count++;
	}
}

2.4、更新参数

如果当前迭代比上一轮内点要多,那直接当当前迭代对应参数更新为最优解

//  <3>、找出内点数量最多的那一组
if (count > total)
{
	total = count;

	best_sample_first = sample_first;
	best_sample_second = sample_second;

	best_parameters.x = k_estimate;
	best_parameters.y = b_estimate;
}

2.2、完整代码

需要说明:

  • ransac每次结果不一样,但是总是能计算得到好的解;
  • 以下代码在画直线的时候没有画得很长,并不影响算法,只是懒得画而已。

完整代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#include<opencv2/opencv.hpp>
using namespace cv;
// 功能:画点
void drawPoints(vector<Point2d> points, Mat& image, bool is_green)
{
    if (is_green)
    {
        for (int i = 0; i < points.size(); i++)
        {
            circle(image, Point2i(int(points[i].x), int(points[i].y)), 2, cv::Scalar(0, 255, 0), 2, LINE_AA);
        }
    }
    else
    {
        for (int i = 0; i < points.size(); i++)
        {
            circle(image, Point2i(int(points[i].x), int(points[i].y)), 2, cv::Scalar(0, 0, 255), 2, LINE_AA);
        }
    }

}
// 功能:画线
void drawLine(Point2d begin, Point2d end, Mat& image)
{
    line(image, Point2i(int(begin.x), int(begin.y)),
        Point2i(int(end.x), int(end.y)), Scalar(255, 0, 0), 1, LINE_AA);
}
// 功能:将一张图经行纵向镜像
void upDownMirror(Mat& image)
{
    Mat image_cpy = image.clone();
    for (int j = 0; j < image.rows; j++)
    {
        for (int i = 0; i < image.cols; i++)
        {
            image.ptr<cv::Vec3b>(j)[i] = image_cpy.ptr<cv::Vec3b>(image.rows - 1 - j)[i];
        }
    }
}
// 功能:最简单的直线拟合 Ransac 框架
class Ransac
{
public:
    Ransac(vector<double> x, vector<double> y);
    ~Ransac();
    static int getRand(int min, int max);//[min, min + max - 1]
public:
    vector<double> x_, y_;
};

Ransac::Ransac(vector<double> x, vector<double> y) :x_(x), y_(y)
{

}

Ransac::~Ransac()
{

}

int Ransac::getRand(int min, int max)//[min, min + max - 1]
{
    return rand() % max + min;
}

int main()
{
    // time seed
    srand((unsigned int)time(nullptr));

    // <1>产生随机数
    vector<Point2d> points;
    // 精确解
    const int k = 1;
    const int b = 10;

    // x [0, 390]; y [10, 400] total_numbers = 40
    for (int x = 0; x < 400; x += 10)
    {
        points.push_back(Point2d(x, k * x + b + Ransac::getRand(0, 60)));
    }
    //添加离群点
    points[35].y = 100;
    points[36].y = 200;
    points[37].y = 200;
    points[38].y = 120;
    points[39].y = 110;
    const int itr_nums = 300;
    double k_estimate = 0;
    double b_estimate = 0;
    Point2d best_parameters; // [k, b]
    // 统计纵向距离 y
    int count = 0; // 内点计数器
    int y_threshold = 50; // 判定内点的阈值
    int total = 0; // 内点总数
    //样本点
    Point2d sample_first;
    Point2d sample_second;
    // 最佳样本点
    Point2d best_sample_first;
    Point2d best_sample_second;
    for (int i = 0; i < itr_nums; i++)
    {
        // <1>、随机抽取两个样本
        int index_first = Ransac::getRand(0, points.size() - 2);
        int index_second = Ransac::getRand(0, points.size() - 2);
        sample_first = points[index_first];
        sample_second = points[index_second];

        if (sample_first == sample_second)
        {
            continue;
        }
        // 计算斜率 k = (y2 - y1)/(x2 - x1)
        k_estimate = (sample_second.y - sample_first.y) / (sample_second.x - sample_first.x);
        // 计算截距 b = y1 - k * x1
        b_estimate = sample_first.y - k_estimate * sample_first.x;
        // <2>、根据距离,来找出所有样本点中的内点,并统计数量
        for (int i = 0; i < points.size(); i++)
        {
            // delta = k * x  + b - y
            double y_error = abs(k_estimate * points[i].x + b_estimate - points[i].y) / sqrt((pow(k_estimate, 2) + 1));
            //cout << "y_error = " << y_error << endl;
            if (y_error < y_threshold)
            {
                count++;
            }
        }
        //  <3>、找出内点数量最多的那一组
        if (count > total)
        {
            total = count;

            best_sample_first = sample_first;
            best_sample_second = sample_second;

            best_parameters.x = k_estimate;
            best_parameters.y = b_estimate;
        }
        count = 0;
    }
    cout << "内点数 = " << total << endl;
    cout << "斜率 k = " << best_parameters.x << "截距 b = " << best_parameters.y << endl;

    // 统计内点
    vector<Point2d> inliners_points;
    for (int i = 0; i < points.size(); i++)
    {
        // delta = k * x  + b - y
        double y_error = abs(best_parameters.x * points[i].x + best_parameters.y - points[i].y) / sqrt((pow(k_estimate, 2) + 1));
        //cout << "y_error = " << y_error << endl;
        if (y_error < y_threshold)
        {
            inliners_points.push_back(points[i]);
        }
    }
    Mat image = Mat::zeros(500, 500, CV_8UC3);
    drawLine(best_sample_first, best_sample_second, image);
    drawPoints(points, image, true);
    drawPoints(inliners_points, image, false); //画内点
    upDownMirror(image);
    imshow("image", image);
    waitKey(0);
    return 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/146920.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java 进阶篇】JQuery DOM操作:CRUD操作的前端魔法

在前端开发的舞台上&#xff0c;CRUD&#xff08;Create, Read, Update, Delete&#xff09;操作是一种极为重要的技能&#xff0c;它涉及对页面元素的增删改查。而JQuery&#xff0c;这位前端开发的魔法师&#xff0c;为我们提供了便捷而强大的方法&#xff0c;使得CRUD操作变…

IP地址如何实现定位功能?

网络犯罪、保护网络安全的重要手段。近日&#xff0c;一则新闻引起了广大网友的关注&#xff1a;IP也能实现定位功能&#xff0c;这是如何做到的呢&#xff1f;本文将对此进行深入解析。 首先&#xff0c;我们需要了解什么是IP地址定位。IP地址定位是通过IP地址确定网络用户所在…

【Windows 开发环境配置——NVIDIA 篇】CUDA、cuDNN、TensorRT 三件套安装

CUDA 从CUDA Toolkit Archive下载相应版本的离线安装包&#xff0c;这里以11.7为例。 打开安装包&#xff0c;在安装选项选择自定义模式&#xff0c;点击下一步。 在自定义安装选项中&#xff0c;仅选择CUDA组件&#xff08;其中Nsight相关组件用于代码调试与性能分析&#xff…

Linux--线程概念+线程控制

1.什么是线程 相对于进程而言&#xff0c;进程是承担资源调度的实体&#xff0c;线程在进程内部运行&#xff0c;是操作系统调度的基本单位。 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列…

Qt QWebEngine 加载网页及交互,实现C++与JS 相互调用

目录 前言1、QtWebEngine介绍2、安装3、核心类介绍3.1 QWebEngineView3.2 QWebEnginePage3.3 QWebEngineProfile3.4 QWebEngineHistory3.5 QWebEngineSettings 4、加载网页5、C调用JS5.1 无返回值5.2 有返回值 6、JS调用C6.1 新建WebObject 类继承自QObject。6.2 将WebObject对…

Day30力扣打卡

打卡记录 最长回文子序列&#xff08;区间DP&#xff09; 链接 class Solution:def longestPalindromeSubseq(self, s: str) -> int:n len(s)f [[0] * n for _ in range(n)]max lambda x, y: x if x > y else yfor i in range(n - 1, -1, -1):f[i][i] 1for j in ra…

数据结构第三课 -----线性表之双向链表

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

用Java开发一个扫雷游戏

以下是用Java开发一个扫雷游戏的基本步骤&#xff1a; 创建一个窗口和画布&#xff0c;用来显示游戏界面。 创建一个游戏逻辑类&#xff0c;包含一些基本的操作&#xff0c;如生成地雷、计算周围地雷数量、打开方格等。 在画布上绘制游戏界面&#xff0c;包括格子、数字、地雷…

人工智能与大数据:驱动现代业务转型的双引擎

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;和大数据已成为驱动业务和技术创新的关键力量。它们的结合不仅重塑了传统行业&#xff0c;也催生了新的商业模式和服务方式。 AI与大数据在零售行业的应用 在零售行业&#xff0c;AI和大数据的应用已经成为提…

《洛谷深入浅出进阶篇》 P2367语文成绩——差分

上链接&#xff1a;P2367 语文成绩 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P2367 上题干&#xff1a; 题目背景 语文考试结束了&#xff0c;成绩还是一如既往地有问题。 题目描述 语文老师总是写错成绩&#xff0c;所以当她修改成绩的…

三、Eureka注册中心

目录 一、作用及调用方式 二、搭建eureka注册中心 三、注册user-service和order-service 四、新增实例 五、服务拉取 六、总结 一、作用及调用方式 在服务提供者启动时&#xff0c;它会向eureka注册中心提供自己的信息&#xff0c;并每30秒进行一次刷新eureka注册中心保存…

golang中context使用总结

一、context使用注意事项 在使用context时&#xff0c;有一些需要注意的事项&#xff0c;以及一些与性能优化相关的建议&#xff1a; 避免滥用context传递数据&#xff1a;context的主要目的是传递请求范围的数据和取消信号&#xff0c;而不是用于传递全局状态或大量数据。滥用…

SARAS算法

SARAS算法 代码仓库:https://github.com/daiyizheng/DL/tree/master/09-rl Sarsa算法是一种强化学习算法&#xff0c;用于解决马尔可夫决策过程&#xff08;MDP&#xff09;问题。它是一种基于值函数的方法&#xff0c;可以用于学习最优策略。本文将介绍Sarsa算法的流程。 S…

汽车以太网IOP测试新利器

IOP测试目的 汽车以太网物理层IOP&#xff08;Interoperability &#xff09;测试&#xff0c;即测试被测对象以太网物理层之间的互操作性。用于验证车载以太网PHY能否在有限时间内建立稳定的链路&#xff1b;此外&#xff0c;还用于验证车载以太网PHY可靠性相关的诊断特性&am…

滚雪球学Java(63):Java高级集合之TreeSet:什么是它,为什么使用它?

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Java Elasticsearch 按一定时间间隔(timeInterval)循环查询数据

最近有个需求&#xff0c;前端传入时间间隔&#xff0c;去elasticsearch按照时间间隔统计每个时间间隔内数据量。 public List<HashMap<String,Object>> getCount(RequestParam Integer time, RequestParam String selectedDatedTime) {SimpleDateFormat format n…

Karmada调度器

调度器就像一个发动机&#xff0c;如果没有了发动机输入动力&#xff0c;是无法正常运行的。就像 Kubernetes 的调度器&#xff0c;它会负责根据节点的资源状态、Pod 的运行状态&#xff0c;判断 Pod 是调度到怎样的集群节点上去。对于 Karmada 这样的多云能力的调度器来说&…

Webpack 性能优化 二次编译速度提升3倍!

本文作者为 360 奇舞团前端开发工程师 Rien. 本篇文章主要记录 webpack 的一次性能优化。 现状 随着业务复杂度的不断增加&#xff0c;项目也开始变得庞大&#xff0c;工程模块的体积也不断增加&#xff0c;webpack 编译的时间也会越来越久&#xff0c;我们现在的项目二次编译的…

【fbtft】如何添加fbtft驱动

获取lcd ic的datasheet&#xff0c;或者直接找到其他平台&#xff08;linux&#xff0c;stm32&#xff0c;esp32&#xff09;的驱动 我用的是合宙的esp32驱动&#xff0c;注意是c语言的&#xff0c;合宙上层用lua封装了&#xff0c;需要找到sdk源码。 源码路径&#xff1a; …

2021年09月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 如下图所示,小明想要做一个文字逐字出现的动画效果,他画出了程序的流程图,以下哪个程序可以实现? A: B: C: D: 答案&#