C++实现ransac

news2024/11/20 20:44:33

目录

一、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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

225.用队列实现栈(LeetCode)

思路 思路&#xff1a;用两个队列实现栈后进先出的特性 &#xff0c;两个队列为空时&#xff0c;先将数据都导向其中一个队列。 当要模拟出栈时&#xff0c;将前面的元素都导入另一个空队列&#xff0c;再将最后一个元素移出队列 实现 实现&#xff1a; 因为C语言没有库可以…

【每日一题】—— D. Epic Transformation(Codeforces Round 710 (Div. 3))(找规律+贪心)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

Linux socket编程(3):利用fork实现服务端与多个客户端建立连接

上一节&#xff0c;我们实现了一个客户端/服务端的Socket通信的代码&#xff0c;在这个例子中&#xff0c;客户端连接上服务端后发送一个字符串&#xff0c;而服务端接收到字符串并打印出来后就关闭所有套接字并退出了。 上一节的代码较为简单&#xff0c;在实际的应用中&…

揭秘Vue中的nextTick:异步更新队列背后的技术原理大揭秘!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、N…

解码方法00

题目链接 解码方法 题目描述 注意点 s 只包含数字&#xff0c;并且可能包含前导零计算并返回 解码 方法的 总数 解答思路 使用动态规划解决本题&#xff0c;其思路为&#xff1a;从后往前遍历字符串&#xff0c;遍历到任一i位置的字符c时&#xff0c;有几种情况&#xff1…

react中间件的理解

一、是什么&#xff1f; 中间件&#xff08;Middleware&#xff09;在计算机中&#xff0c;是介于应用系统和系统软件之间的一类软件&#xff0c;它使用系统软件所提供的基础服务&#xff08;功能&#xff09;&#xff0c;衔接网络应用上的各个部分或不同的应用&#xff0c;能…

ubuntu20安装opencv4和opencv_contrib 多版本共存

openCV 卸载 openCV 安装后的源码尽可能保留&#xff0c;因为可以直接从build文件夹下卸载已经安装的openCV. 参考链接&#xff1a;视觉学习笔记10——opencv的卸载、安装与多版本管理 如果已经安装完openCV,后续想重新装&#xff0c;需要先卸载掉安装的openCV. 在ubuntu终端…

离散卡尔曼滤波器算法详解及重要参数(Q、R、P)的讨论

公开数据集中文版详细描述参考前文&#xff1a;https://editor.csdn.net/md/?not_checkout1&spm1011.2124.3001.6192神经元Spike信号分析参考前文&#xff1a;https://blog.csdn.net/qq_43811536/article/details/134359566?spm1001.2014.3001.5501神经元运动调制分析参考…

安防监控EasyCVR视频汇聚平台使用海康SDK播放出现花屏是什么原因?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

《洛谷深入浅出进阶篇》P3397 地毯————二维差分

上链接&#xff1a;P3397 地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P3397 上题干&#xff1a; 题目描述 在 nn 的格子上有 m 个地毯。 给出这些地毯的信息&#xff0c;问每个点被多少个地毯覆盖。 输入格式 第一行&#xff0c;两个…

Git | Git的基本操作以及原理介绍

文章目录 基本操作创建git仓库配置name和email .git目录的结构git add & git commit.git目录结构的变化 git追踪管理的数据git的版本回退回退的原理回退的三种情况 版本库中文件的删除git分支管理分支的删除合并分支时的冲突分支的合并模式分支策略git stash不要在master分…

实用篇-ES-DSL操作文档

一、mapping属性 mapping属性的官方文档: https://elastic.co/guide/en/elasticsearch/reference/current/index.html 下面的表格是介绍elasticsearch中的各个概念以及含义&#xff0c;看的时候重点看第二、三列&#xff0c;第一列是为了让你更理解第二列的意思&#xff0c;所…

嵌入式养成计划-54----ARM--异常处理流程

一百三十五、异常处理流程 135.1 arm处理器工作模式 135.2 异常源和异常模式关系 135.2.1 异常源 异常源就是引发处理器进入相应异常模式 135.2.2 对应关系 异常模式异常源FIQ模式FIQ类型异常源引发处理器进入FIQ模式IRQ模式IRQ类型异常源引发处理器进入IRQ模式SVC模式上电…

三、Eureka注册中心

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

14——2

这道题目前面看不懂可以看比如后面的 这里1/3是因为S100的长度n3&#xff08;100占3位&#xff09;&#xff0c;然后1出现的占比是1/3&#xff08;1在第一位&#xff09;&#xff0c;0出现的占比是2/3&#xff0c;因为0出现了2次&#xff0c;&#xff08;第二位&#xff0c;第…

【文章学习系列之模型】DAGMM

本章内容 文章概况模型结构损失函数实验结果实验分析总结 文章概况 《Deep Autoencoding Gaussian Mixture Model for Unsupervised Anomaly Detection》是2018年发表于ICLR的一篇论文&#xff0c;该论文提出一种端到端的无监督异常检测方法DAGMM&#xff0c;取得了不错的效果…

3.3 Linux 文件管理

1、查看系统信息 tty 命令 描述&#xff1a;查看当前系统在哪个终端语法&#xff1a;tty Linux默认情况下提供6个虚拟终端来让用户登录&#xff0c;系统将F1~F6定义为tty1~tty6。 ctrlalt(F1~F6) &#xff1a;从图形界面切换到命令行界面的第 n 个虚拟终端&#xff08;F1 是…

社区牛奶直供站:创新供应链,满足消费者需求

社区牛奶直供站&#xff1a;创新供应链&#xff0c;满足消费者需求 社区牛奶直供站模式彻底改变了传统的多级经销链条和流通加价环节&#xff0c;通过削减中间环节&#xff0c;以相对低价直接供应牛奶给消费者。这样做实现了从奶企源头直接供应到社区家庭的目标&#xff0c;精准…

字符串旋转结果

文章目录 题目解法1解法2 题目 字符串旋转结果 写一个函数&#xff0c;判断一个字符串是否为另外一个字符串旋转之后的字符串。 例如&#xff1a;给定s1 AABCD和s2 BCDAA&#xff0c;返回1 给定s1abcd和s2ACBD&#xff0c;返回0. AABCD左旋一个字符得到ABCDA AABCD左旋两个字…

人工智能基础_机器学习033_多项式回归升维_多项式回归代码实现_非线性数据预测_升维后的数据对非线性数据预测---人工智能工作笔记0073

然后我们来实际的操作一下看看,多项式升维的作用,其实就是为了,来对,非线性的数据进行拟合. 我们直接看代码 import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression X=np.linspace(-1,11,num=100) 从-1到11中获取100个数…