随机抽样一致性算法(RANSAC)示例及源代码
作者:王先荣
大约在两年前翻译了《随机抽样一致性算法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(-, );
int b = random.Next(-, );
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=0
d = Math.Abs(1d * (A * p.X - p.Y + B) / Math.Sqrt(A * A + ));
}
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;
else
y = 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 != ? string.Format("{0:F02}x", A) : "", B != ? (B > ? string.Format("+{0:F02}", B) : string.Format("{0:F02}", B)) : "");
else
formula = 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 = ; //最佳模型的局内点数目
Random random = new Random();
for (int idx = ; idx < nudIterCount.Value; idx++)
{
int idx1, idx2;
GetRandomInliersPoints(random, out idx1, out idx2);
int inliersCount = ;
Line line = new Line(points[idx1], points[idx2]);
for (int i = ; 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);
}
else
lblFormula.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>b
public 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 < )
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 = ;
p1.Y = ;
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 == )
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, ) + Math.Pow(p.Y - B, )));
} /// <summary>
/// 返回圆方程
/// </summary>
/// <returns></returns>
public override string ToString()
{
return string.Format("{0}**2+{1}**2={2}",
A == ? "x" : (A > ? string.Format("(x-{0:F02})", A) : string.Format("(x+{0:F02})", Math.Abs(A))),
B == ? "y" : (B > ? string.Format("(y-{0:F02})", B) : string.Format("(y+{0:F02})", Math.Abs(B))),
R == ? "" : 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 = ; //最佳模型的局内点数目
Random random = new Random();
for (int idx = ; idx < nudIterCount.Value; idx++)
{
int idx1, idx2, idx3;
GetRandomInliersPoints(random, out idx1, out idx2, out idx3);
int inliersCount = ;
Circle circle;
try
{
circle = new Circle(points[idx1], points[idx2], points[idx3]);
}
catch
{
continue;
}
for (int i = ; 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);
}
else
lblFormula.Text = "没有获取到最佳圆。";
}
获取圆
五、本文源代码
感谢您阅读本文,希望对您有所帮助。
随机抽样一致性算法(RANSAC)示例及源代码的更多相关文章
- 随机抽样一致性算法(RANSAC)示例及源代码--转载
转载自王先荣 http://www.cnblogs.com/xrwang/p/SampleOfRansac.html 作者:王先荣 大约在两年前翻译了<随机抽样一致性算法RANSAC>,在 ...
- 随机抽样一致性算法(RANSAC)转载
这两天看<计算机视觉中的多视图几何>人都看蒙了,转载一些干货看看 转自王先荣 http://www.cnblogs.com/xrwang/archive/2011/03/09/ransac ...
- 随机抽样一致性(RANSAC)算法详解
随机抽样一致性(RANSAC)算法能够有效的剔除特征匹配中的错误匹配点. 实际上,RANSAC能够有效拟合存在噪声模型下的拟合函数.实际上,RANSAC算法的核心在于将点划分为“内点”和“外点”.在一 ...
- 随机抽样一致性算法(RANSAC)
本文翻译自维基百科,英文原文地址是:http://en.wikipedia.org/wiki/ransac,如果您英语不错,建议您直接查看原文. RANSAC是"RANdom SAmple ...
- PCL采样一致性算法
在计算机视觉领域广泛的使用各种不同的采样一致性参数估计算法用于排除错误的样本,样本不同对应的应用不同,例如剔除错误的配准点对,分割出处在模型上的点集,PCL中以随机采样一致性算法(RANSAC)为核心 ...
- Raft 一致性算法论文译文
本篇博客为著名的 RAFT 一致性算法论文的中文翻译,论文名为<In search of an Understandable Consensus Algorithm (Extended Vers ...
- 随机采样一致算法RANSAC
A project to learn line, circle and ellipse detection in 2d images: https://github.com/Yiphy/Ransac- ...
- 分布式一致性算法--Paxos
Paxos算法是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法.Paxos算法解决的问题是一个分布式系统如何就某个值(决议)达成一致.在工程实践意义上来说, ...
- 一致性算法RAFT详解
原帖地址:http://www.solinx.co/archives/415?utm_source=tuicool&utm_medium=referral一致性算法Raft详解背景 熟悉或了解 ...
随机推荐
- 在cocos2d里面如何使用Texture Packer和像素格式来优化spritesheet
免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作 ...
- python学习笔记:Day01
一.python 简介 1. python是Guido van Rossum在1989年圣诞节期间,为了打发无聊的假期而编写的一个编程语言 2. pyhton主要应用于数据分析.组件集成.网络 ...
- 图书馆管理系统UML建模
一. 业务描述 1.借阅者:借书.还书 2.图书馆管理员:书籍借出处理 书籍归还处理书籍预定 3.系统管理员:增加书目.删除或更新书目.预定信息处理.增加书籍减少书籍.增加借阅者账户信息.删 ...
- MySql和SQL Server数据类型 对比
My Sql 数据类型 SQL Server 数据类型 Yes/No bit Smallint(字节型) tinyint Integer(长整型) int Real(单精度浮点型) real F ...
- 基于Criminisi算法的栅格影像数据敏感地物隐藏
栅格影像数据敏感地物伪装是指通过计算机智能识别与计算,将影像数据中的敏感地物进行识别与提取,将敏感地物智能替换成公共地物,如草地.森林.湖泊.公园等.但目前该技术并不成熟,同时栅格影像数据敏感地物伪装 ...
- Hash哈希(二)一致性Hash(C++实现)
一致性Hash 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,经常用于分布式.负载均衡等. 原理 一致哈希是 ...
- autohotkey在运维中的应用
AutoHotkey是一个自由.开源的宏生成器和自动化软件工具,它让用户能够自动执行重复性任务.AutoHotkey可以修改任何应用程序的用户界面(例如,把默认的Windows按键控制命令替 ...
- cookie and session
Session is used to save the message for the hole period of user dialogue in web service.Such as the ...
- 1120练习,CSS制作网页
<title>智博星主页</title> <style type="text/css"> *{ margin:0px auto; padding ...
- atitit.团队建设总结fx O622
团队建设总结fx O622 大的title 2 建设团队文化 2 办公环境(3s+树+湖) 3 每人一个办公室 3 弹性工作制 3 重大的决定公投体制 3 每年一个kid经验日 3 做自己想到做的事. ...