RANSAC算法实现 + 直线拟合

article/2025/9/12 18:12:13

一、RANSAC算法

1.参考资料

[1]题目来源与解析:商汤科技SLAM算法岗的RANSAC编程题

[2]牛客网题目:[编程题]线性回归

[3]牛客网解答参考:商汤科技某算法岗的编程题有点过分了啊

[4]RANSAC算法原理:RANSAC翻译、经典RANSAC以及其相关的改进的算法小结

[5]参考代码(只进行两点的估计):RANSAC 直线拟合算法

[6]最小二乘拟合直线原理:最小二乘法直线拟合:Ax+By+C=0

[7]最小二乘拟合直线代码:Ax+By+C=0 直线一般式拟合 c++/python

[8]最小二乘原理推导:最小二乘法求回归直线方程的推导过程

 

2.题目要求

拟合二维平面中的带噪音直线, 
其中有不超过10%的样本点远离了直线,另外90%的样本点可能有高斯噪声的偏移
要求输出为 
ax+by+c=0的形式 
其中a > 0 且 a^2 + b^2 = 1

输入描述:
第一个数n表示有多少个样本点  之后n*2个数 每次是每个点的x 和y

输出描述:
输出a,b,c三个数,至多可以到6位有效数字

示例1
输入
5
3 4
6 8
9 12
15 20
10 -10

输出
-0.800000 0.600000 0.000000
说明
本题共有10个测试点,每个点会根据选手输出的参数计算非噪音数据点的拟合误差E,并根据E来对每个数据点进行评分0-10分
输入数据的范围在-10000

3.RANSAC算法伪代码(转自[4])

伪码形式的算法如下所示: 
输入: 
data —— 一组观测数据 
model —— 适应于数据的模型 
n —— 适用于模型的最少数据个数 
k —— 算法的迭代次数 
t —— 用于决定数据是否适应于模型的阀值 
d —— 判定模型是否适用于数据集的数据数目 
输出: 
best_model —— 跟数据最匹配的模型参数(如果没有找到好的模型,返回null) 
best_consensus_set —— 估计出模型的数据点 
best_error —— 跟数据相关的估计出的模型错误

iterations = 0
best_model = null
best_consensus_set = null
best_error = 无穷大
while ( iterations < k )maybe_inliers = 从数据集中随机选择n个点maybe_model = 适合于maybe_inliers的模型参数consensus_set = maybe_inliersfor ( 每个数据集中不属于maybe_inliers的点 )if ( 如果点适合于maybe_model,且错误小于t )将点添加到consensus_setif ( consensus_set中的元素数目大于d )已经找到了好的模型,现在测试该模型到底有多好better_model = 适合于consensus_set中所有点的模型参数this_error = better_model究竟如何适合这些点的度量if ( this_error < best_error )我们发现了比以前好的模型,保存该模型直到更好的模型出现best_model =  better_modelbest_consensus_set = consensus_setbest_error =  this_error增加迭代次数
返回 best_model, best_consensus_set, best_error

3.最小二乘求解直线

公共内容:

变量名计算公式
mX\overline{x}=\frac{1}{n}\sum\limits_{i=1}^{n}x_i
mY\overline{y}=\frac{1}{n}\sum\limits_{i=1}^{n}y_i
sXX\sum\limits_{i=1}^{n}(x_i-\overline{x})^2
sXY\sum\limits_{i=1}^{n}(x_i-\overline{x})(y_i-\overline{y})
sYY\sum\limits_{i=1}^{n}(y_i-\overline{y})^2

解法1:

参考资料[6]

解法2:

拟合直线 :

y=a+bx

最小化点到直线的平方和:

f=\sum\limits_{i=1}^{n}(y-a-bx)^2

函数f对参数a求导,并令其为0

\frac{\partial{f}}{\partial{a}}=-2\sum\limits_{i=1}^{n}(y_i-a-bx_i)=0

求得

\hat{a}=\overline{y}-b\overline{x}

将上式带入f,对参数b求导,并令其为0,有

\frac{\partial{f}}{\partial{b}}=-2\sum\limits_{i=1}^{n}((x_i-\overline{x})(y_i-\overline{y})-b(x_i-\overline{x})^2))=0

b = \frac{\sum\limits_{i=1}^{n}(x_i-\overline{x})(y_i-\overline{y})}{\sum\limits_{i=1}^{n}(x_i-\overline{x})^2}

或者f对参数b求导后,将\hat{a}=\overline{y}-b\overline{x}带入方程有

\frac{\partial{f}}{\partial{b}}=-2(\sum\limits_{i=1}^{n}x_iy_i-b\sum\limits_{i=1}^{n}x_i^2-n\mathop{\overline{x}\mathop{\overline{y}}}+nb\overline{x}^2)=0

b=\frac{\sum\limits_{i=1}^{n}x_iy_i-n\mathop{\overline{x}\mathop{\overline{y}}}}{\sum\limits_{i=1}^{n}x_i^2-n\overline{x}^2}

本文采用第一种形式

注意,采用同样的参数

    int k = 50;                //最大迭代次数
    int n = 2;                //适用于模型的最少数据个数
    double t = 0.01;        //用于决定数据是否适应于模型的阀值
    int d = data_size*0.5;    //判定模型是否适用于数据集的数据数目

解法1的通过率为100%

解法2的通过率为77.78%

暂时不明白为什么……

解法3:

根据参考资料[6],转换为二次型求最小值问题

第一种方法使用特征值分解,选取最小特征值对应的特征向量

第二种方法在二次型中,使用sin\alpha,cos\alpha替换待求量,求解参数方程

后续有时间的话会补上程序


4.算法实现

可以通过自定义模型,将该代码移植到其他程序中

/*************************************************
Author:	SayheyheyheyDate:2019-7-4Description:根据伪代码实现通用的RANSAC模板自定义线性模型,实现两种方式的直线拟合
**************************************************/#include <random>
#include <iostream>
#include <time.h>
#include <set>
#include <cassert>
#include <limits.h>using namespace std;
//数据点类型
struct st_point{st_point(){};st_point(double X, double Y) :x(X), y(Y){};double x;double y;
};
/*** @brief 线性模型** Ax+By+C = 0;
*/
class linearModel{
public://待估计参数double A, B, C;
public:linearModel(){};~linearModel(){};//使用两个点对直线进行初始估计void Update(vector<st_point> &data, set<int> &maybe_inliers){assert(maybe_inliers.size() == 2);		//初始化的点不为2个,报错//根据索引读取数据vector<int> points(maybe_inliers.begin(), maybe_inliers.end());st_point pts1 = data[points[0]];st_point pts2 = data[points[1]];//根据两个点计算直线参数(得到其中一组解,可以任意比例缩放)double delta_x = pts2.x - pts1.x;double delta_y = pts2.y - pts1.y;A = delta_y;B = -delta_x;C = -delta_y*pts2.x + delta_x*pts2.y;}//返回点到直线的距离double computeError(st_point point){double numerator = abs(A*point.x + B*point.y + C);double denominator = sqrt(A*A + B*B);return numerator / denominator;}//根据一致点的集合对直线进行重新估计double Estimate(vector<st_point> &data, set<int> &consensus_set){assert(consensus_set.size() >= 2);//求均值 meansdouble mX, mY;mX = mY = 0;for (auto &index : consensus_set){mX += data[index].x;mY += data[index].y;}mX /= consensus_set.size();mY /= consensus_set.size();//求二次项的和 sumdouble sXX, sYY, sXY;sXX = sYY = sXY = 0;for (auto &index : consensus_set){st_point point;point = data[index];sXX += (point.x - mX)*(point.x - mX);sYY += (point.y - mY)*(point.y - mY);sXY += (point.x - mX)*(point.y - mY);}/*//解法1:求y=kx+b的最小二乘估计,然后再转换成一般形式//参考 https://blog.csdn.net/hookie1990/article/details/91406309bool isVertical = sXY == 0 && sXX < sYY;bool isHorizontal = sXY == 0 && sXX > sYY;bool isIndeterminate = sXY == 0 && sXX == sYY;double k = NAN;double b = NAN;if (isVertical){A = 1;B = 0;C = mX;}else if (isHorizontal){A = 0;B = 1;C = mY;}else if (isIndeterminate){A = NAN;B = NAN;C = NAN;}else{k = (sYY - sXX + sqrt((sYY - sXX) * (sYY - sXX) + 4.0 * sXY * sXY)) / (2.0 * sXY);	//斜率b = mY - k * mX;															//截距//正则化项,使得A^2+B^2 = 1;double normFactor = 1 / sqrt(1 + k*k);A = normFactor * k;B = -normFactor;C = normFactor*b;}//返回残差if (isIndeterminate){return NAN;}double error = A*A*sXX + 2 * A*B*sXY + B*B*sYY;error /= consensus_set.size();return error;*///解法2:if(sXX == 0){A = 1;    B = 0;C = -mX;}else{A = sXY/sXX;B = -1;C = mY - A*mX;//归一化令A^2+B^2 = 1;double normFactor = sqrt(A*A+B*B);A /= normFactor;B /= normFactor;C /= normFactor;}double error = A*A*sXX + 2 * A*B*sXY + B*B*sYY;error /= consensus_set.size();    //求平均误差return error;}
};/**
* @brief 运行RANSAC算法
*
* @param[in]	data	一组观测数据
* @param[in]	n		适用于模型的最少数据个数
* @param[in]	k		算法的迭代次数
* @param[in]	t		用于决定数据是否适应于模型的阀值
* @param[in]	d		判定模型是否适用于数据集的数据数目 
* @param[in&out]	model	自定义的待估计模型,为该函数提供Update、computeError和Estimate三个成员函数
*							运行结束后,模型参数被设置为最佳的估计值
* @param[out]	best_consensus_set	输出一致点的索引值
* @param[out]	best_error	输出最小损失函数
*/
template<typename T, typename U>
int ransac(vector<T> &data, int n, int k, double t, int d,U &best_model,set<int> &best_consensus_set, double &best_error){//1.初始化int  iterations = 0;	//迭代次数U maybe_model;			//使用随机选点初始化求得的模型U better_model;			//根据符合条件的一致点拟合出的模型int isFound = 0;					//算法成功的标志set<int> maybe_inliers;				//初始随机选取的点(的索引值)//best_error = DBL_MAX;	//初始化为最大值best_error = 1.7976931348623158e+308;default_random_engine rng(time(NULL));					//随机数生成器uniform_int_distribution<int> dist(0, data.size()-1);	//采用均匀分布//2.主循环while (iterations < k){//3.随机选点maybe_inliers.clear();	while (1){int index = dist(rng);maybe_inliers.insert(index);if (maybe_inliers.size() == n){break;}}//4.计算初始值maybe_model.Update(data, maybe_inliers);								//自定义函数,更新模型set<int> consensus_set(maybe_inliers.begin(),maybe_inliers.end());		//选取模型后,根据误差阈值t选取的内点(的索引值)//5.根据初始模型和阈值t选择内点	for (int i = 0; i < data.size(); i++){double error_per_item = maybe_model.computeError(data[i]);if (error_per_item < t){consensus_set.insert(i);}}//6.根据全部的内点重新计算模型if (consensus_set.size() > d){double this_error = better_model.Estimate(data, consensus_set);		//自定义函数,(最小二乘)更新模型,返回计算出的误差//7.若当前模型更好,则更新输出量if (this_error < best_error){best_model = better_model;best_consensus_set = consensus_set;best_error = this_error;}isFound = 1;}++iterations;}return isFound;
}int main(){//1.读入数据int data_size;		//输入第一行表示数据大小cin >> data_size;	vector<st_point> Points(data_size);for (int i = 0; i < data_size; i++){cin >> Points[i].x >> Points[i].y;}//测试用//vector<st_point> Points{ st_point(3, 4), st_point(6, 8), st_point(9, 12), st_point(15, 20), st_point(10,-10)};//int data_size = Points.size();//2.设置输入量int k = 50;				//最大迭代次数int n = 2;				//适用于模型的最少数据个数double t = 0.01;		//用于决定数据是否适应于模型的阀值int d = data_size*0.5;	//判定模型是否适用于数据集的数据数目 //3.初始化输出量linearModel best_model;			//最佳线性模型set<int> best_consensus_set;	//记录一致点索引的setdouble best_error;				//最小残差//4.运行RANSAC			int status = ransac(Points, n, k, t, d, best_model, best_consensus_set, best_error);//5.输出cout << best_model.A << " " << best_model.B << " " << best_model.C << endl;return 0;
}

运行结果:

k = 50, n=2, t=0.01, d = data_size*0.5时

解法1:

您的代码已保存
答案正确:恭喜!您提交的程序通过了所有的测试用例

解法2:

您的代码已保存
答案错误:您提交的程序没有通过所有的测试用例
case通过率为77.78%

简单解法:

可将Estimate的步骤注释掉进行实验,根据consensus_set计算this_error

您的代码已保存
答案正确:恭喜!您提交的程序通过了所有的测试用例

 


http://chatgpt.dhexx.cn/article/MGifMX6u.shtml

相关文章

精匹配——Opencv实现RANSAC算法进行误匹配对剔除,并和最小二乘法对比

精匹配——RANSAC算法思想及优缺点 目录 精匹配——RANSAC算法思想及优缺点前言一、RANSAC简介二、RANSAC基本思想1.步骤2.迭代次数的公式3.举例&#xff08;拟合直线&#xff0c;拟合最佳单应性矩阵&#xff09; 三、最小二乘法1、最小二乘法的主要思想2、最小二乘解3、仿射变…

ransca算法详细介绍

1、算法概述&#xff1a; RANSAC算法的基本假设是样本中包含正确数据(inliers&#xff0c;可以被模型描述的数据)&#xff0c;也包含异常数据(outliers&#xff0c;偏离正常范围很远、无法适应数学模型的数据)&#xff0c;即数据集中含有噪声。这些异常数据可能是由于错误的测…

RANSAC 特征匹配算法解析

一、RANSAC特征匹配算法简介   RANSAC算法是RANdom SAmple Consensus的缩写&#xff0c;意为随机抽样一致。表面上的意思就是从匹配样本中随机取样&#xff0c;寻找一致的样本点。RANSAC算法是根据一组包含异常数据的样本数据集&#xff0c;计算出数据的数学模型参数&#x…

RANSAC算法(原理及代码实现+迭代次数参数自适应)

RANSAC算法 前言算法流程Python代码RANSAC算法迭代参数的自适应 前言 随机样本一致性 (RANSAC) 是一种迭代方法&#xff0c;用于从一组包含异常值的观察数据中估计数学模型的参数&#xff0c;此时异常值不会对估计值产生影响。简言之&#xff0c;RANSAC是一种滤除异常值的常用算…

RANSAC算法简介

文章目录 1 算法简介2 基本思想3 参数4 应用案例&#xff08;直线拟合&#xff09; 1 算法简介 RANSAC算法的基本假设是样本中包含正确数据(inliers&#xff0c;可以被模型描述的数据)&#xff0c;也包含异常数据(outlies&#xff0c;偏离正常范围很远、无法适应数学模型的数据…

关于RANSAC的理解

先说最小二乘。 ok&#xff0c;你手头有一堆数据&#xff0c;比如这些蓝点&#xff1a; 那么我们假设它符合一个直线模型&#xff1a;yaxb&#xff0c;用最小二乘就可以很容易求解出未知参数a和b。最小二乘大法确实好哇&#xff0c;毕竟高斯用它来估计谷神星的轨道&#xff08…

RANSAC基本原理

计算机视觉基本原理——RANSAC 1. RANSAC简介2. 基本思想3. 范例4. 迭代次数推导 Reference&#xff1a; 1.计算机视觉基本原理——RANSAC 1. RANSAC简介 RANSAC(RAndom SAmple Consensus,随机采样一致)算法是从一组含有“外点”(outliers)的数据中正确估计数学模型参数的迭代…

RANSAC算法的理解与使用

随机抽样一致算法(random sample consensus, RANSAC)&#xff0c;其实就是采用迭代的方法从一组包含离群的被观测数据中估算出数学模型的参数。(比如通过一群点拟合一条直线等) 基本假设 模型假设&#xff1a;事先知道真实数据满足的数学模型&#xff0c;不知道的只是模型的具…

【特征匹配】RANSAC算法原理与源码解析

转载请注明出处&#xff1a;http://blog.csdn.net/luoshixian099/article/details/50217655 勿在浮沙筑高台 随机抽样一致性&#xff08;RANSAC&#xff09;算法&#xff0c;可以在一组包含“外点”的数据集中&#xff0c;采用不断迭代的方法&#xff0c;寻找最优参数模型&…

RANSAC算法详解+Python实现

1.算法描述 RANSAC的基本假设是&#xff1a; &#xff08;1&#xff09;数据由“局内点”组成&#xff0c;例如&#xff1a;数据的分布可以用一些模型参数来解释&#xff1b; &#xff08;2&#xff09;“局外点”是不能适应该模型的数据&#xff1b; &#xff08;3&#xff0…

RANSAC算法与原理(一)

目录 算法介绍举例 算法的基本思想和流程算法的实现流程&#xff1a;迭代次数的推导算法的实现 python代码实现References 算法介绍 首先我们从Random sample consensus - Wikipedia上找到RANSAC原理的介绍。 RANSAC算法的中文名称是随机抽样一致算法(Random Sample Consenus…

RANSAC算法——看完保证你理解

目录 1 最小二乘算法的缺陷2 RANSAC算法2.1 原理2.2 实例2.3 参数 参考感谢阅读 RANSAC全程Random sample consensus&#xff0c;中文即“随机抽样一致算法”&#xff0c;该方法采用迭代的方式从一组包含离群&#xff08;outlier或者错误数据&#xff09;的被观测数据中估算出数…

RANSAC算法介绍

一、RANSAC介绍 随机抽样一致算法&#xff08;RANdom SAmple Consensus,RANSAC&#xff09;,采用迭代的方式从一组包含离群的被观测数据中估算出数学模型的参数。RANSAC算法假设数据中包含正确数据和异常数据&#xff08;或称为噪声&#xff09;。正确数据记为内点&#xff08;…

RANSAC算法讲解

RANSAC是“RANdom SAmple Consensus&#xff08;随机抽样一致&#xff09;”的缩写。它可以从一组包含“局外点”的观测数据集中&#xff0c;通过迭代方式估计数学模型的参数。它是一种不确定的算法——它有一定的概率得出一个合理的结果&#xff1b;为了提高概率必须提高迭代次…

RANSAC介绍(Matlab版直线拟合+平面拟合)

RANSAC介绍&#xff08;Matlab版直线拟合平面拟合&#xff09; 本人邮箱&#xff1a;sylvester0510163.com&#xff0c;欢迎交流讨论&#xff0c; 欢迎转载&#xff0c;转载请注明网址http://blog.csdn.net/u010128736/ 一、RANSAC介绍 随机抽样一致算法&#xff08;RANdom SA…

RANSAC算法(仅供学习使用)

1.定义 RANSAC&#xff08;Random Sample Consensus&#xff09;算法是一种基于随机采样的迭代算法&#xff0c;用于估计一个数学模型参数。它最初由Fischler和Bolles于1981年提出&#xff0c;主要用于计算机视觉和计算机图形学中的模型拟合和参数估计问题。 RANSAC算法的基本…

前端调试手机app

有时候应用在电脑网页端显示是正常的&#xff0c;但是一安装到手机上或者在手机浏览器上打开&#xff0c;就会显示各种问题问题。 在网上找了很长时间&#xff0c;最方便的就是利用在电脑上利用谷歌浏览器进行调试&#xff0c;输入网址chrome://inspect/#devices&#xff0c;进…

跨平台应用开发进阶(三十七)uni-app前端监控方案 Sentry 探究

文章目录 一、前言二、Sentry 简介三、Sentry 部署3.1 docker 的部署 (mac版)3.2 部署 sentry3.3 创建项目3.4 前端部署&#xff0c;注入监控代码 四、sentry 操作界面介绍五、拓展阅读 一、前言 在日益发达的网络时代&#xff0c;web应用也是越来越复杂&#xff0c;尤其是前端…

uni-app 开发跨平台应用前端框架

前言 uni-app 是一个使用 vue.js 开发跨平台应用的前端框架&#xff0c;由于它具备"编写一次代码可发布到多个平台"的特点&#xff0c;大大的节省了开发成本&#xff0c;极速提升了开发效率。 一、uni-app 简介 uni-app 是一个使用 Vue.js 开发所有前端应用的框架。…

uni-app开发所有前端应用的框架

uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、H5、以及各种小程序&#xff08;微信/支付宝/百度/头条/QQ/钉钉&#xff09;等多个平台。 DCloud公司拥有420万开发者&#xff0c;几十万应用案例、6.5亿手…