作者:王先荣

大约在两年前翻译了《随机抽样一致性算法RANSAC》,在文章的最后承诺写该算法的C#示例程序。可惜光阴似箭,转眼许久才写出来,实在抱歉。本文将使用随机抽样一致性算法来来检测直线和圆,并提供源代码下载。

一、RANSAC检测流程

在这里复述下RANSAC的检测流程,详细的过程见上一篇翻译文章:

RANSAC算法的输入是一组观测数据,一个可以解释或者适应于观测数据的参数化模型,一些可信的参数。

    RANSAC通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:
    1.有一个模型适应于假设的局内点,即所有的未知参数都能从假设的局内点计算得出。
    2.用1中得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点。
    3.如果有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。
    4.然后,用所有假设的局内点去重新估计模型,因为它仅仅被初始的假设局内点估计过。
    5.最后,通过估计局内点与模型的错误率来评估模型。
    这个过程被重复执行固定的次数,每次产生的模型要么因为局内点太少而被舍弃,要么因为比现有的模型更好而被选用。

二、得到观测数据

我们没有实验(测试)数据,这里用手工输入的数据来替代——记录您在PictureBox中的点击坐标,作为观测数据。

        /// <summary>/// 得到样本点/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void pbSample_Click(object sender, EventArgs e){MouseEventArgs me=(MouseEventArgs)e;txtRandomPoints.Text += string.Format("({0},{1}),", me.X, me.Y);DrawPoint(new Point(me.X, me.Y));}

得到样本点

三、检测直线

3.1 直线的相关知识

(1)平面上的任意两点可以确定一条直线;

(2)直线的通用数学表达形式为:ax+by+c=0。这种表达形式有三个未知数,需要提供三个点才能解出a,b,c三个参数。由于随机选择的三个点不一定在一条直线上,所以程序中放弃这种方式。

(3)直线可以用y=ax+b及x=c这两个式子来表示。这两种形式只有一个或者两个未知数,只需两个点就能解出a,b,c三个参数。随机选择的两个点即可得到直线,我们采用这种形式。

3.2 直线类

直线类(Line)封装了跟直线相关的一些属性及方法,列表如下:

(1)属性

A——y=ax+b中的a

B——y=ax+b中的b

C——x=c中的c

(2)构造函数

public Line(PointF p1, PointF p2)

提供两个点p1及p2,计算出直线的属性A,B,C。

(3)方法

GetDistance——获取点到直线之间的距离;

GetY——根据x坐标,获取直线上点的y坐标;

ToString——获取直线的方程式。

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;namespace Ransac
{/// <summary>/// 直线类:采用y=a*x+b或者x=c的形式表示直线。/// </summary>public class Line{/// <summary>/// y=ax+b中的a/// </summary>public double A { get; set; }/// <summary>/// y=ax+b中的b/// </summary>public double B { get; set; }/// <summary>/// x=c中的c/// </summary>public double C { get; set; }/// <summary>/// 构造函数(如果直线为y=ax+b形式,则C为Nan;如果直线为x=c形式,则A和B为Nan)/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <param name="c"></param>public Line(double a, double b, double c){if (!((double.IsNaN(a) && double.IsNaN(b) && !double.IsNaN(c)) || (!double.IsNaN(a) && !double.IsNaN(b) && double.IsNaN(c))))throw new ArgumentException("参数错误,无效的直线参数。");A = a;B = b;C = c;}/// <summary>/// 构造函数,由两个点确定直线/// </summary>/// <param name="p1"></param>/// <param name="p2"></param>public Line(PointF p1, PointF p2){if (p1.X == p2.X){A = double.NaN;B = double.NaN;C = p1.X;}else{A = 1d * (p1.Y - p2.Y) / (p1.X - p2.X);B = p1.Y - A * p1.X;C = double.NaN;}}/// <summary>/// 构造函数,由两个点确定直线/// </summary>/// <param name="p1"></param>/// <param name="p2"></param>public Line(Point p1, Point p2) :this(new PointF(p1.X, p1.Y), new PointF(p2.X, p2.Y)){}/// <summary>/// 生成一条随机的直线/// </summary>/// <returns></returns>public static Line GetRandomLine(){Random random = new Random();int a = random.Next(-10, 10);int b = random.Next(-10, 10);return new Line(a, b, double.NaN);}/// <summary>/// 获取点到直线的距离/// </summary>/// <param name="p">点</param>/// <returns>返回点到直线的距离;如果直线通过点,返回0。</returns>public double GetDistance(Point p){return GetDistance(new PointF(p.X, p.Y));}/// <summary>/// 获取点到直线的距离/// </summary>/// <param name="p">点</param>/// <returns>返回点到直线的距离;如果直线通过点,返回0。</returns>public double GetDistance(PointF p){double d = 0d;if (double.IsNaN(C)){//y=ax+b相当于ax-y+b=0d = Math.Abs(1d * (A * p.X - p.Y + B) / Math.Sqrt(A * A + 1));}else{d = Math.Abs(C - p.X);}return d;}/// <summary>/// 根据x坐标,得到直线上点的y坐标 /// </summary>/// <param name="x"></param>/// <returns></returns>public double GetY(double x){double y;if (double.IsNaN(C))y = A * x + B;elsey = double.NaN;return y;}/// <summary>/// 返回直线方程/// </summary>/// <returns></returns>public override string ToString(){string formula = "";if (double.IsNaN(C))formula = string.Format("y={0}{1}", A != 0 ? string.Format("{0:F02}x", A) : "", B != 0 ? (B > 0 ? string.Format("+{0:F02}", B) : string.Format("{0:F02}", B)) : "");elseformula = string.Format("x={0:F02}", C);return formula;}}
}

Line类

3.3 检测直线的过程

(1)随机从观测点中选择两个点,得到通过该点的直线;

(2)用(1)中的直线去测试其他观测点,由点到直线的距离确定观测点是否为局内点或者局外点;

(3)如果局内点足够多,并且局内点多于原有“最佳”直线的局内点,那么将这次迭代的直线设为“最佳”直线;

(4)重复(1)~(3)步直到找到最佳直线。

细心的您估计已经发现我省略了标准RANSAC检测过程中重新估计模型的步骤,我是故意的,我觉得麻烦且没什么用处,所以咔嚓了,O(∩_∩)O~。

/// <summary>/// 尝试获取直线/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnGetLine_Click(object sender, EventArgs e){//用RANSAC方法获取最佳直线points = GetSamplePoints();Line bestLine = null;           //最佳直线double bestInliersCount = 0;    //最佳模型的局内点数目Random random = new Random();for (int idx = 0; idx < nudIterCount.Value; idx++){int idx1, idx2;GetRandomInliersPoints(random, out idx1, out idx2);int inliersCount = 2;Line line = new Line(points[idx1], points[idx2]);for (int i = 0; i < points.Count; i++){if (i != idx1 && i != idx2){if (line.GetDistance(points[i]) <= (double)nudMinDistance.Value)inliersCount++;}}if (inliersCount >= nudMinPointCount.Value){if (inliersCount > bestInliersCount){bestLine = line;bestInliersCount = inliersCount;}}}//显示最佳直线if (bestLine != null){lblFormula.Text = string.Format("方程:{0}\r\nA:{1}\r\nB:{2}\r\nC:{3}\r\n局内点数目:{4}",bestLine.ToString(), bestLine.A, bestLine.B, bestLine.C, bestInliersCount);DrawLine(bestLine);}elselblFormula.Text = "没有获取到最佳直线。";}

获取直线

四、检测圆

4.1 圆的相关知识

(1)平面内不在同一直线上的三个点可以确定一个圆;

(2)圆的数学表达形式为:(x-a)2+(y-b)2=r2

其中,(a,b)为圆心,r为半径。

4.2 圆类

圆类(Circle)封装了跟圆有关的属性及方法,列表如下:

(1)属性

A——圆心的x坐标

B——圆心的y坐标

R——圆的半径

(2)构造函数

public Circle(PointF p1, PointF p2, PointF p3)

提供三个点p1,p2和p3,计算出圆的属性A,B,R。

(3)方法

GetDistance——获取点到圆(周)之间的距离,表示点接近或者远离圆;

ToString——获取圆的方程式。

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;namespace Ransac
{/// <summary>/// 圆类:用(x-a)**2+(y-b)**2=r**2形式表示/// </summary>bpublic class Circle{/// <summary>/// 圆心的X坐标/// </summary>public double A { get; set; }/// <summary>/// 圆心的Y坐标/// </summary>public double B { get; set; }/// <summary>/// 半径/// </summary>public double R { get; set; }/// <summary>/// 构造函数,提供圆心和半径。/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <param name="r"></param>public Circle(double a, double b, double r){A = a;B = b;if (r < 0)throw new ArgumentOutOfRangeException("r", "圆的半径必须大于0。");R = r;}/// <summary>/// 构造函数,提供三个点。/// 该算法来自csdn论坛,帖子地址是:http://bbs.csdn.net/topics/50383586,在此感谢5楼的privet。/// </summary>/// <param name="p1"></param>/// <param name="p2"></param>/// <param name="p3"></param>public Circle(PointF p1, PointF p2, PointF p3){float xMove = p1.X;float yMove = p1.Y;p1.X = 0;p1.Y = 0;p2.X = p2.X - xMove;p2.Y = p2.Y - yMove;p3.X = p3.X - xMove;p3.Y = p3.Y - yMove;float x1 = p2.X, y1 = p2.Y, x2 = p3.X, y2 = p3.Y;double m = 2.0 * (x1 * y2 - y1 * x2);if (m == 0)throw new ArgumentException("参数错误,提供的三点不能构成圆。");double x0 = (x1 * x1 * y2 - x2 * x2 * y1 + y1 * y2 * (y1 - y2)) / m;double y0 = (x1 * x2 * (x2 - x1) - y1 * y1 * x2 + x1 * y2 * y2) / m;R = Math.Sqrt(x0 * x0 + y0 * y0);A = x0 + xMove;B = y0 + yMove;}/// <summary>/// 构造函数,提供三个点。/// </summary>/// <param name="p1"></param>/// <param name="p2"></param>/// <param name="p3"></param>public Circle(Point p1, Point p2, Point p3) :this(new PointF(p1.X, p1.Y), new PointF(p2.X, p2.Y), new PointF(p3.X, p3.Y)){}/// <summary>/// 获取点到圆的距离(圆周,不是圆心)/// </summary>/// <param name="p"></param>/// <returns></returns>public double GetDistance(PointF p){return Math.Abs(R - Math.Sqrt(Math.Pow(p.X - A, 2) + Math.Pow(p.Y - B, 2)));}/// <summary>/// 返回圆方程/// </summary>/// <returns></returns>public override string ToString(){return string.Format("{0}**2+{1}**2={2}",A == 0 ? "x" : (A > 0 ? string.Format("(x-{0:F02})", A) : string.Format("(x+{0:F02})", Math.Abs(A))),B == 0 ? "y" : (B > 0 ? string.Format("(y-{0:F02})", B) : string.Format("(y+{0:F02})", Math.Abs(B))),R == 0 ? "0" : string.Format("{0:F02}**2", Math.Abs(R)));}}
}

Circle类

3.3 检测圆的过程

(1)随机从观测点中选择三个点,尝试得到通过这三个点的圆;

(2)用(1)中的圆去测试其他观测点,由点到圆的距离确定观测点是否为局内点或者局外点;

(3)如果局内点足够多,并且局内点多于原有“最佳”圆的局内点,那么将这次迭代的圆设为“最佳”圆;

(4)重复(1)~(3)步直到找到最佳圆。

/// <summary>/// 尝试获取圆/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnGetCircle_Click(object sender, EventArgs e){//用RANSAC方法获取最佳直线points = GetSamplePoints();Circle bestCircle = null;       //最佳圆double bestInliersCount = 0;    //最佳模型的局内点数目Random random = new Random();for (int idx = 0; idx < nudIterCount.Value; idx++){int idx1, idx2, idx3;GetRandomInliersPoints(random, out idx1, out idx2, out idx3);int inliersCount = 3;Circle circle;try{circle = new Circle(points[idx1], points[idx2], points[idx3]);}catch{continue;}for (int i = 0; i < points.Count; i++){if (i != idx1 && i != idx2 && i!=idx3){if (circle.GetDistance(points[i]) <= (double)nudMinDistance.Value)inliersCount++;}}if (inliersCount >= nudMinPointCount.Value){if (inliersCount > bestInliersCount){bestCircle = circle;bestInliersCount = inliersCount;}}}//显示最佳圆if (bestCircle != null){lblFormula.Text = string.Format("方程:{0}\r\nA:{1}\r\nB:{2}\r\nR:{3}\r\n局内点数目:{4}",bestCircle.ToString(), bestCircle.A, bestCircle.B, bestCircle.R, bestInliersCount);DrawCircle(bestCircle);}elselblFormula.Text = "没有获取到最佳圆。";}

获取圆

五、本文源代码

点击这里下载本文源代码。

感谢您阅读本文,希望对您有所帮助。

随机抽样一致性算法(RANSAC)示例及源代码相关推荐

  1. java随机抽样算法_随机抽样一致性(RANSAC)算法详解

    随机抽样一致性(RANSAC)算法能够有效的剔除特征匹配中的错误匹配点. 实际上,RANSAC能够有效拟合存在噪声模型下的拟合函数.实际上,RANSAC算法的核心在于将点划分为"内点&quo ...

  2. Python-pcl 随机采样一致性算法

    RANSAC 随机采样一致性算法 RANSAC是一种随机参数估计算法.RANSAC从样本中随机抽选出一个样本子集,使用最小方差估计算法对这个子集计算模型参数,然后计算所有样本与该模型的偏差,在使用一个 ...

  3. 点云PCL学习笔记-分割segmentation-RANSAC随机采样一致性算法欧式聚类提取

    随机采样一致性算法RANSAC 程序实例参考网址: https://pcl.readthedocs.io/projects/tutorials/en/latest/random_sample_cons ...

  4. 随机抽样一致算法(RANSAC)理论介绍和程序实现

    1.随机抽样一致算法(Random sample consensus,RANSAC)理论介绍 Ordinary LS是保守派:在现有数据下,如何实现最优.是从一个整体误差最小的角度去考虑,尽量谁也不得 ...

  5. PCL学习:随机采样一致性算法(RANSAC)

    此文是在他人的文章上进行了补充完善.另外代码部分是在Ziv Yaniv的c++实现上重新实现了一次,加了中文注释,修正了一个错误.便于理解算法实现. 随机采样一致性算法,RANSAC是"RA ...

  6. PCL采样一致性算法

    在计算机视觉领域广泛的使用各种不同的采样一致性参数估计算法用于排除错误的样本,样本不同对应的应用不同,例如剔除错误的配准点对,分割出处在模型上的点集,PCL中以随机采样一致性算法(RANSAC)为核心 ...

  7. RAFT 寻找一种易于理解的一致性算法(扩展版)

    摘要 Raft 是一种为了管理复制日志的一致性算法.它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统.为了提 ...

  8. 分布式一致性算法Raft简介(下)

    slide 15: 这一节开始讲leader changes,即leader的变更过程中如何保证log的一致性: 1)需要明白的是,新leader上任后,各个server的log状态很可能是不一致的: ...

  9. 分布式一致性算法Raft简介(上)

    最近看了Ongaro在2014年的博士论文<CONSENSUS: BRIDGING THEORY AND PRACTICE>的部分章节,对raft有了初步的理解.其中论文中提到用于教学的u ...

  10. Raft 一致性算法论文译文

    本篇博客为著名的 RAFT 一致性算法论文的中文翻译,论文名为<In search of an Understandable Consensus Algorithm (Extended Vers ...

最新文章

  1. python2 安装faiss-gpu 报错 faiss/faiss/python/swigfaiss.i:241: Error: Unable to find ‘faiss/impl/platfo
  2. (转)iPhone开发经典语录集锦
  3. C语言经典例96-计算字符串中子串出现的次数
  4. ESB文件调用,windows服务
  5. C#UDP通讯UdpClient
  6. Android 第二十课 广播机制(大喇叭)----发送自定义广播(包括发送标准广播和发送有序广播)
  7. 单片机串行通信全解析
  8. python 代码排布_python实现经典排序算法的示例代码
  9. linux parted命令,Linux分区之parted命令详解
  10. ES6学习(七)—Set 和 Map 数据结构
  11. k近邻算法matlab实现_k近邻算法
  12. qml demo分析(maskedmousearea-异形窗口)
  13. 本特利330505-02-02-02-00速度传感器
  14. vue2项目中给echarts地图设置背景图和打点
  15. 自学者福利!腾讯Java开发面试记录,砥砺前行!
  16. IOS移动应用跳转微信小程序
  17. 怎么记住计算机快捷键,快速记忆电脑快捷键的方法
  18. OneNote如何同步到OneDrive,并且如何在PC和手机上完美使用OneNote
  19. R学习 第九篇:因子
  20. Interlocked.Increment 方法 和Interlocked.Decrement 方法作用

热门文章

  1. paip.c++ gui 库HtmLayout 的优缺点 QT优点
  2. PAIP.MYSQL设置远程账户登陆总结
  3. 我们来做做公募基金数量的“人口”大普查
  4. Rust : utf8与unicode码
  5. 汇添富基金总经理张晖:做选股专家,更以“选股专家”的视角管理公司
  6. 银行理财子公司的“超级”玩法
  7. (转)【特征工程】特征工程技术与方法
  8. 【通信仿真】基于matlab蒙特卡罗算法2FSK系统抗噪声性能仿真【含Matlab源码 1632期】
  9. 【元胞自动机】基于matlab六边形网格六方元胞自动机【含Matlab源码 1362期】
  10. 【优化充电】基于matlab蒙特卡洛算法求解电动汽车充电优化问题【含Matlab源码 1164期】