RANSAC 直线拟合

article/2025/11/8 5:07:34

result: 

code:

#include "stdafx.h".
#include <opencv2\opencv.hpp>
#include <iostream>
#include <ctime>using namespace std;
using namespace cv;//生成[0,1]之间符合均匀分布的数
double uniformRandom(void)
{return (double)rand() / (double)RAND_MAX;
}//生成[0,1]之间符合高斯分布的数
double gaussianRandom(void)
{/* This Gaussian routine is stolen from Numerical Recipes and is theircopyright. */static int next_gaussian = 0;static double saved_gaussian_value;double fac, rsq, v1, v2;if (next_gaussian == 0) {do {v1 = 2 * uniformRandom() - 1;v2 = 2 * uniformRandom() - 1;rsq = v1*v1 + v2*v2;} while (rsq >= 1.0 || rsq == 0.0);fac = sqrt(-2 * log(rsq) / rsq);saved_gaussian_value = v1*fac;next_gaussian = 1;return v2*fac;}else {next_gaussian = 0;return saved_gaussian_value;}
}//根据点集拟合直线ax+by+c=0,res为残差
void calcLinePara(vector<Point2d> pts, double &a, double &b, double &c, double &res)
{res = 0;Vec4f line;vector<Point2f> ptsF;for (unsigned int i = 0; i < pts.size(); i++)ptsF.push_back(pts[i]);fitLine(ptsF, line, CV_DIST_L2, 0, 1e-2, 1e-2);a = line[1];b = -line[0];c = line[0] * line[3] - line[1] * line[2];for (unsigned int i = 0; i < pts.size(); i++){double resid_ = fabs(pts[i].x * a + pts[i].y * b + c);res += resid_;}res /= pts.size();
}//得到直线拟合样本,即在直线采样点集上随机选2个点
bool getSample(vector<int> set, vector<int> &sset)
{int i[2];if (set.size() > 2){do{for (int n = 0; n < 2; n++)i[n] = int(uniformRandom() * (set.size() - 1));} while (!(i[1] != i[0]));for (int n = 0; n < 2; n++){sset.push_back(i[n]);}}else{return false;}return true;
}//直线样本中两随机点位置不能太近
bool verifyComposition(const vector<Point2d> pts)
{cv::Point2d pt1 = pts[0];cv::Point2d pt2 = pts[1];if (abs(pt1.x - pt2.x) < 5 && abs(pt1.y - pt2.y) < 5)return false;return true;
}//RANSAC直线拟合
void fitLineRANSAC(vector<Point2d> ptSet, double &a, double &b, double &c, vector<bool> &inlierFlag)
{double residual_error = 2.99; //内点阈值bool stop_loop = false;int maximum = 0;  //最大内点数//最终内点标识及其残差inlierFlag = vector<bool>(ptSet.size(), false);vector<double> resids_(ptSet.size(), 3);int sample_count = 0;int N = 500;double res = 0;// RANSACsrand((unsigned int)time(NULL)); //设置随机数种子vector<int> ptsID;for (unsigned int i = 0; i < ptSet.size(); i++)ptsID.push_back(i);while (N > sample_count && !stop_loop){vector<bool> inlierstemp;vector<double> residualstemp;vector<int> ptss;int inlier_count = 0;if (!getSample(ptsID, ptss)){stop_loop = true;continue;}vector<Point2d> pt_sam;pt_sam.push_back(ptSet[ptss[0]]);pt_sam.push_back(ptSet[ptss[1]]);if (!verifyComposition(pt_sam)){++sample_count;continue;}// 计算直线方程calcLinePara(pt_sam, a, b, c, res);//内点检验for (unsigned int i = 0; i < ptSet.size(); i++){Point2d pt = ptSet[i];double resid_ = fabs(pt.x * a + pt.y * b + c);residualstemp.push_back(resid_);inlierstemp.push_back(false);if (resid_ < residual_error){++inlier_count;inlierstemp[i] = true;}}// 找到最佳拟合直线if (inlier_count >= maximum){maximum = inlier_count;resids_ = residualstemp;inlierFlag = inlierstemp;}// 更新RANSAC迭代次数,以及内点概率if (inlier_count == 0){N = 500;}else{double epsilon = 1.0 - double(inlier_count) / (double)ptSet.size(); //野值点比例double p = 0.99; //所有样本中存在1个好样本的概率double s = 2.0;N = int(log(1.0 - p) / log(1.0 - pow((1.0 - epsilon), s)));}++sample_count;}//利用所有内点重新拟合直线vector<Point2d> pset;for (unsigned int i = 0; i < ptSet.size(); i++){if (inlierFlag[i])pset.push_back(ptSet[i]);}calcLinePara(pset, a, b, c, res);
}void main()
{//demoint width = 640;int height = 320;//直线参数double a = 1, b = 2, c = -640;//随机获取直线上20个点int ninliers = 0;vector<Point2d> ptSet;srand((unsigned int)time(NULL)); //设置随机数种子while (true){double x = uniformRandom()*(width - 1);double y = -(a*x + c) / b;//加0.5高斯噪声x += gaussianRandom()*0.5;y += gaussianRandom()*0.5;if (x >= 640 && y >= 320)continue;Point2d pt(x, y);ptSet.push_back(pt);ninliers++;if (ninliers == 20)break;}int nOutliers = 0;//随机获取10个野值点while (true){double x = uniformRandom() * (width - 1);double y = uniformRandom() * (height - 1);if (fabs(a*x + b*y + c) < 10)  //野值点到直线距离不小于10个像素continue;Point2d pt(x, y);ptSet.push_back(pt);nOutliers++;if (nOutliers == 10)break;}//绘制点集中所有点Mat img(321, 641, CV_8UC3, Scalar(255, 255, 255));for (unsigned int i = 0; i < ptSet.size(); i++)circle(img, ptSet[i], 3, Scalar(255, 0, 0), 3, 8);double A, B, C;vector<bool> inliers;fitLineRANSAC(ptSet, A, B, C, inliers);B = B / A;C = C / A;A = A / A;//绘制直线Point2d ptStart, ptEnd;ptStart.x = 0;ptStart.y = -(A*ptStart.x + C) / B;ptEnd.x = -(B*ptEnd.y + C) / A;ptEnd.y = 0;line(img, ptStart, ptEnd, Scalar(0, 255, 255), 2, 8);cout << "A:" << A << " " << "B:" << B << " " << "C:" << C << " " << endl;imshow("line fitting", img);waitKey();
}

思考:

正在考虑将部分图像像素坐标点进行直线拟合,正在写这部分代码,今天只是参考原始代码,将原始代码跑通了。

  • I am considering fitting some image pixel coordinate points with straight line. I am writing this part of code. Today, I just refer to the original code and run through the original code.

参考:https://blog.csdn.net/YunlinWang/article/details/78147026?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param


http://chatgpt.dhexx.cn/article/3UctYNbb.shtml

相关文章

OpenCV——直线拟合

相比于直线检测&#xff0c;直线拟合的最大特点是将所有数据只拟合出一条直线 void fitLine( InputArray points, OutputArray line, int distType,double param, double reps, double aeps ); points&#xff1a;输入待拟合直线的2D或者3D点集。line&#xff1a;输出描述直线…

直线拟合

在进行直线拟合算法中&#xff0c;一直使用最小二乘法&#xff0c;使用时间长了&#xff0c;也比较熟练了&#xff0c;但是在最近一次使用中&#xff0c;最小二乘法在拟合垂直或者接近垂直的直线时&#xff0c;效果不好&#xff1b;斜率很大&#xff0c;使用稳定性不好。查阅《…

Android 冷启动 热启动 测试

一、应用的启动 启动方式 通常来说&#xff0c;在安卓中应用的启动方式分为两种&#xff1a;冷启动和热启动。 1、冷启动&#xff1a;当启动应用时&#xff0c;后台没有该应用的进程&#xff0c;这时系统会重新创建一个新的进程分配给该应用&#xff0c;这个启动方式就是冷启动…

Spring Boot 热启动

目的&#xff1a;修改类文件可以马上编译发布&#xff0c;提高了工作效率 步骤&#xff1a; 第一步&#xff1a; 修改pom.xml <!-- 热启动 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools&l…

APP冷热启动专项测试

一、冷热启动的概念 冷启动&#xff1a;当启动应用时&#xff0c;后台没有该应用的进程&#xff0c;这时系统会重新创建一个新的进程分配给该应用&#xff0c;这个启动方式就是冷启动。 热启动&#xff1a;当启动应用时&#xff0c;后台已有该应用的进程&#xff08;例&#…

嵌入式linux热启动和冷启动,使用keil判断ARM的冷启动和热启动的方法

微处理器:LPC2114 编译环境:Keil MDK V4.10 思路: 常把单片机系统的复位分为冷启动和热启动。所谓冷启动&#xff0c;也就是一般所说的上电复位&#xff0c;冷启动后片内外RAM的内容是随机的&#xff0c;通常是0x00或0xFF&#xff1b;单片机的热启动是通过外部电路给运行中的单…

Android冷启动和热启动以及冷启动优化方案

1、什么是冷启动和热启动 冷启动&#xff1a; 当启动应用时&#xff0c;后台没有该应用的进程&#xff0c;这时系统会重新创建一个新的进程分配给该应用&#xff0c;这个启动方式就是冷启动&#xff0c;也就是先实例化Application。热启动&#xff1a; 当启动应用时&#xff0…

冷启动和热启动的区别android,app冷启动和热启动的区别(详解两者定义及区别)...

介绍一下 app 冷启动和热启动方式来实现 app 秒开的效果。那么,先来看看什么叫冷启动和热启动。 冷启动:指 app 被后台杀死后,在这个状态打开 app,这种启动方式叫做冷启动。 热启动:指 app 没有被后台杀死,仍然在后台运行,通常我们再次去打开这个 app,这种启动方式叫热…

热启动和冷启动以及复位启动的小知识

热启动和冷启动的区别&#xff1a; 1、重启是热启动。开机是冷启动。 2、热启动是通过开始菜单、任务管理器或者快捷键&#xff0c;重新启动计算机&#xff0c;叫热启动。冷启动是在关机状态下按POWER启动计算机&#xff0c;叫做冷启动 。 3、热启动是在计算机已经开启的状态…

dimen属性报错

<TextViewandroid:id"id/tvQuote1"android:layout_width"fill_parent"android:layout_height"wrap_content"android:textColor"#FFFFFF"android:textSize"dimen/text_size" /> 发现问题是dimen属性出现错误 别的手机上…

解决android:padding=“@dimen/activity_vertical_margin“> 标红问题

解决android:padding“dimen/activity_vertical_margin”> 标红问题 如果有这个问题&#xff0c;可以在values中新建一个dimen.xml文件 在dimen.xml里增加如下代码&#xff1a; <?xml version"1.0" encoding"utf-8"?><resources><…

android:paddingTop=“@dimen/activity_horizontal_margin“报错(报红)解决方法

android:paddingTop"dimen/activity_horizontal_margin"报错&#xff08;报红&#xff09;解决方法 在app----->res----->values------>dimens.xml中添加如下代码&#xff1a; <dimen name“activity_horizontal_margin” / (后面还有一个尖括号)&#…

android 手机适配之values适配dimen值

android 适配屏幕的方式有很多,最方便最直接的无非就是适配values里的dimens文件值来进行适配. 张鸿洋大神已经写过一篇适配的文章,很详细 但是我在阅读的时候还是有点疑问,这个values-1920x1080到底是dp值还是手机分辨率,因为我在实际操作中发现这样一句话. 很明显可以看…

Android开发——AS插件批量解决XML中的String/Color/Dimen硬编码

1. 问题抛出 1.1 开发方面 对于日常开发中&#xff0c;每写一个"#333"&#xff0c;都要手动的在当前xml与colors.xml中来回切换&#xff0c;查看是否已经定义过&#xff0c;如果定义过则拿过来复用&#xff0c;如果没有就要新定义一个叫"#333"的资源名&a…

dimen.xml浅析

转自&#xff1a;http://blog.csdn.net/kazeik/article/details/8268721 有时候我们为了维护一个工程&#xff0c;或者想定义一个button样式&#xff0c;或textView样式&#xff0c;这些样式中包含着文字的大小&#xff0c;背景图片&#xff0c;前置图片等一些资源。而且这个…

android:屏幕自适应之dimen使用

From&#xff1a;4种必须知道的Android屏幕自适应解决方案&#xff08;求投票支持&#xff09; demo下载&#xff1a;http://www.eoeandroid.com/forum.php?modattachment&aidNjE0Njh8ZTIyZDA2M2N8MTMzODgyOTQxN3w1NzAwOTV8MTczOTcz 以下是Demo首页的预览图 一、细说 layo…

android屏幕适配,生成不同分辨率的dimen.xml文件

1.在项目下新建moudle&#xff0c;选择Java Library&#xff0c;如图&#xff1a; 2.编写工具类&#xff1a; public class DimenUtils {//文件保存的路径 是在该项目下根路径下创建 比如该项目创建的路径是C:\MyProject\&#xff0c;// 则保存的文件路径是C:\MyProject\Di…

Android 屏幕适配之屏幕分辨率(创建不同dimen)适配

由于目前流行的Android屏幕分辨率太多&#xff0c;再加上UI同事设计时经常使用px单位来设计&#xff0c;所以导致编写Android代码时适配是一个很大的问题。 以下是本人开发过程中的适配方案 仅供参考 附&#xff1a;目前手机主流分辨率列表&#xff1a;http://screensiz.es/pho…

Android dimen

转自&#xff1a;Android&#xff1a;dimen尺寸资源文件的使用 dimen.xml在values文件夹下面 <resources><!-- Default screen margins, per the Android Design guidelines. --><dimen name"btn_width">16dp</dimen><dimen name"bt…

Android开发-手机适配之dimen

1.dimen概念 dimen是用来定义尺寸的资源文件&#xff0c;默认路径&#xff1a;工程的res\values\dimens.xml&#xff0c;如下图所示&#xff1a; 做好dimens适配后的项目结构如下图所示&#xff1a; 2.dimen定义的尺寸资源作用 可以在res下创建不同分辨率的values目录&#xff…