Opencv源码之平面点集的最小包围圆

平面点集的最小包围圆

--Cracent整理 2016.5.28

目录

1、问题背景....
1

2、算法及原理....
1

3、算法(摘自OPENCV)...
1

4、基础数学知识....
7

三角形的外心....
7

三角形的三条垂直平分线必交于一点....
7

三角形的外心的性质....
7

三角形的内心....
8

证明....
8

性质....
8

三角形的垂心....
8

三角形的三条高必交于一点....
9

三角形的垂心的性质....
9

三角形的重心....
10

三角形的三条中线必交于一点....
10

三角形的重心的性质....
11

三角形的旁心....
11

介绍....
11

三角形旁心的性质....
11

欧拉线....
12

欧拉线的证法1..
12

欧拉线的证法2..
13

欧拉线的证法3..
13

1、问题背景

考察固定在工作平台上的一直机械手,要捡起散落在不同位置的多个零件,并送到别的地方。那么,这只机械手的底座应该选在哪里呢?根据直觉,应该选在机械手需够着的那些位置的“中心”。准确地讲,也就是包围这些点的那个最小圆的圆心----该位置的好处是,可使机械手的底座到它需要够着的那些点的最大距离最小化。于是可得如下问题:给定由平面上n个点所组成的一个集合P(对应于机械手需要够着的工作平台的那些位置),试找出P的最小包围圆(smallest enclosing disc)----亦即,包含P中所有点、半径最小的那个圆。这个最小包围圆必然是唯一的。

2、算法及原理

算法介绍:我们本次算法的设计是基于这样一个简单直观的性质:在既定的给定点条件下,如果引入一张新的半平面,只要此前的最优解顶点(即唯一确定最小包围圆的几个关键顶点)能够包含于其中,则不必对此最优解进行修改,亦即此亦为新点集的最优解;否则,新的最优解顶点必然位于这个新的半空间的边界上。

定理可以通过反证法证明。

于是,基于此性质,我们便可得到一个类似于线性规划算法的随机增量式算法。定义Di为相对于pi的最小包围圆。此算法实现的关键在于对于pi?Di-1时的处理。显然,如果pi∈Di-1,则Di= Di-1;否则,需要对Di另外更新。而且,Di的组成必然包含了pi;因此,此种情况下的最小包围圆是过pi点且覆盖点集{ p1,p2,p3……pi-1}的最小包围圆。则仿照上述处理的思路,Di={ p1,pi},逐个判断点集{ p2,p3……pi-1},如果存在pj?Di,则Di={pj,pi}。同时,再依次对点集{
p1,p2,p3……pj-1}判断是否满足pk∈Di,若有不满足,则Di={pk,pj,pi}。由于,三点唯一地确定一个圆,故而,只需在此基础上判断其他的点是否位于此包围圆内,不停地更新pk。当最内层循环完成时,退出循环,转而更新pj;当次内层循环结束时,退出循环,更新pi。当i=n时,表明对所有的顶点均已处理过,此时的Dn即表示覆盖了给定n个点的最小包围圆。

3、算法(摘自OPENCV)

主函数:minEnclosingCircle_jbl(InputArray
_points, Point2f&
_center, float&
_radius)

static float innerProduct(Point2f &v1,
Point2f &v2)

{

return
v1.x * v2.y - v1.y *
v2.x;

}

//三点定圆法

static void findCircle3pts(Point2f *pts,
Point2f &center,
float &radius)

{

// two edges of the triangle v1, v2

Point2f v1 =
pts[1] - pts[0];

Point2f v2 =
pts[2] - pts[0];

if (innerProduct(v1, v2) == 0.0f)

{

// v1, v2 colineation, can not determine a uniquecircle

// find the longtest distance as diameter line

float d1 = (float)norm(pts[0]
- pts[1]);

float d2 = (float)norm(pts[0]
- pts[2]);

float d3 = (float)norm(pts[1]
- pts[2]);

if (d1 >= d2 && d1 >= d3)

{

center
= (pts[0] +
pts[1]) / 2.0f;

radius = (d1 / 2.0f);

}

else
if (d2 >= d1 && d2 >= d3)

{

center
= (pts[0] +
pts[2]) / 2.0f;

radius = (d2 / 2.0f);

}

else
if (d3 >= d1 && d3 >= d2)

{

center
= (pts[1] +
pts[2]) / 2.0f;

radius = (d3 / 2.0f);

}

}

else

{

// center is intersection of midperpendicular linesof the two edges v1, v2

// a1*x + b1*y = c1 where a1 = v1.x, b1 = v1.y

// a2*x + b2*y = c2 where a2 = v2.x, b2 = v2.y

Point2f midPoint1 = (pts[0]
+ pts[1])
/ 2.0f;

float c1 = midPoint1.x * v1.x + midPoint1.y * v1.y;

Point2f midPoint2 = (pts[0]
+ pts[2])
/ 2.0f;

float c2 = midPoint2.x * v2.x + midPoint2.y * v2.y;

float det = v1.x * v2.y - v1.y * v2.x;

float cx = (c1 * v2.y - c2 * v1.y) / det;

float cy = (v1.x * c2 - v2.x * c1) / det;

center.x = (float)cx;

center.y = (float)cy;

cx -= pts[0].x;

cy -= pts[0].y;

radius = (float)(std::sqrt(cx *cx + cy * cy));

}

}

const float EPS = 1.0e-4f;

static voidfindEnclosingCircle3pts_orLess_32f(Point2f *pts,
int count,
Point2f &center,
float &radius)

{

switch (count)

{

case 1:

center
= pts[0];

radius = 0.0f;

break;

case 2:

center.x = (pts[0].x +
pts[1].x) / 2.0f;

center.y = (pts[0].y +
pts[1].y) / 2.0f;

radius = (float)(norm(pts[0]
- pts[1]) / 2.0);

break;

case 3:

findCircle3pts(pts,
center, radius);

break;

default:

break;

}

radius += EPS;

}

template<typename
PT>

static void findThirdPoint(const
PT *pts,
int i,
int j,
Point2f &center,
float &radius)

{

center.x = (float)(pts[j].x +
pts[i].x) / 2.0f;

center.y = (float)(pts[j].y +
pts[i].y) / 2.0f;

float dx = (float)(pts[j].x -
pts[i].x);

float dy = (float)(pts[j].y -
pts[i].y);

radius = (float)norm(Point2f(dx, dy)) / 2.0f + EPS;

for (int k = 0; k <
j; ++k)

{

dx = center.x - (float)pts[k].x;

dy = center.y - (float)pts[k].y;

if (norm(Point2f(dx, dy)) <
radius)

{

continue;

}

else

{

Point2f ptsf[3];

ptsf[0] = (Point2f)pts[i];

ptsf[1] = (Point2f)pts[j];

ptsf[2] = (Point2f)pts[k];

findEnclosingCircle3pts_orLess_32f(ptsf,3,
center, radius);

}

}

}

template<typename
PT>

void findSecondPoint(const
PT *pts,
int i,
Point2f &center,
float &radius)

{

center.x = (float)(pts[0].x +
pts[i].x) / 2.0f;

center.y = (float)(pts[0].y +
pts[i].y) / 2.0f;

float dx = (float)(pts[0].x -
pts[i].x);

float dy = (float)(pts[0].y -
pts[i].y);

radius = (float)norm(Point2f(dx, dy)) / 2.0f + EPS;

for (int j = 1; j <
i; ++j)

{

dx = center.x - (float)pts[j].x;

dy = center.y - (float)pts[j].y;

if (norm(Point2f(dx, dy)) <
radius)

{

continue;

}

else

{

findThirdPoint(pts,
i, j, center, radius);

}

}

}

template<typename
PT>

static voidfindMinEnclosingCircle(const
PT *pts,
int count,
Point2f &center,
float &radius)

{

center.x = (float)(pts[0].x +
pts[1].x) / 2.0f;

center.y = (float)(pts[0].y +
pts[1].y) / 2.0f;

float dx = (float)(pts[0].x -
pts[1].x);

float dy = (float)(pts[0].y -
pts[1].y);

radius = (float)norm(Point2f(dx, dy)) / 2.0f + EPS;

for (int i = 2; i <
count; ++i)

{

dx = (float)pts[i].x -
center.x;

dy = (float)pts[i].y -
center.y;

float d = (float)norm(Point2f(dx, dy));

if (d <
radius)

{

continue;

}

else

{

findSecondPoint(pts, i,
center, radius);

}

}

}

//see Welzl, Emo. Smallest enclosing disks (balls and ellipsoids). SpringerBerlin Heidelberg, 1991.

voidminEnclosingCircle_jbl(InputArray
_points, Point2f&
_center, float&
_radius)

{

Mat points =
_points.getMat();

int count = points.checkVector(2);

int depth = points.depth();

Point2f center;

float radius = 0.f;

CV_Assert(count >= 0 && (depth ==
CV_32F || depth ==
CV_32S));

_center.x =
_center.y = 0.f;

_radius = 0.f;

if (count == 0)

return;

bool is_float = depth ==
CV_32F;

const
Point* ptsi = points.ptr<Point>();

const
Point2f* ptsf = points.ptr<Point2f>();

// point count <= 3

if (count <= 3)

{

Point2f ptsf3[3];

for (int i = 0; i < count; ++i)

{

ptsf3[i] = (is_float) ? ptsf[i] :
Point2f((float)ptsi[i].x, (float)ptsi[i].y);

}

findEnclosingCircle3pts_orLess_32f(ptsf3,count, center, radius);

_center
= center;

_radius = radius;

return;

}

if (is_float)

{

findMinEnclosingCircle<Point2f>(ptsf, count,center, radius);

#if 0

for (size_t m = 0; m < count; ++m)

{

float d = (float)norm(ptsf[m] - center);

if (d > radius)

{

printf("error!\n");

}

}

#endif

}

else

{

findMinEnclosingCircle<Point>(ptsi, count,center, radius);

#if 0

for (size_t m = 0; m < count; ++m)

{

double dx = ptsi[m].x - center.x;

double dy = ptsi[m].y - center.y;

double d = std::sqrt(dx * dx + dy * dy);

if (d > radius)

{

printf("error!\n");

}

}

#endif

}

_center
= center;

_radius = radius;

}

4、基础数学知识

三角形的外心

三角形的外心是三角形三边的垂直平分线的交点(或三角形外接圆的圆心)

三角形的三条垂直平分线必交于一点

三角形的三条垂直平分线必交于一点

已知:△ABC中,AB,AC的垂直平分线DO,EO相交于点O

求证:O点在BC的垂直平分线上

证明:连结AO,BO,CO,∵DO垂直平分AB,∴AO=BO

∵EO垂直平分AC,∴AO=CO

∴BO=CO

即O点在BC的垂直平分线上

三角形的外心的性质

1.三角形三条边的垂直平分线的交于一点,该点即为三角形外接圆的圆心.

2三角形的外接圆有且只有一个,即对于给定的三角形,其外心是唯一的,但一个圆的内接三角形却有无数个,这些三角形的外心重合。

3.锐角三角形的外心在三角形内;钝角三角形的外心在三角形外;直角三角形的外心与斜边的中点重合

4.OA=OB=OC=R

5.∠BOC=2∠BAC,∠AOB=2∠ACB,∠COA=2∠CBA

6.S△ABC=abc/4R

三角形的内心

三角形的内心是三角形三条内角平分线的交点(或内切圆的圆心)。

证明

三角形的三条角平分线必交于一点

己知:在△ABC中,∠A与∠B的角平分线交于点O,连接OC

求证:OC平分∠ACB

三角形内心

证明:过O点作OD,OE,OF分别垂直于AC,BC,AB,垂足分别为D,E,F

∵AO平分∠BAC,∴OD=OF;∵BO平分∠ABC,∴OE=OF
;∴OD=OF

∴O在∠ACB角平分线上∴CO平分∠ACB

性质

1.三角形的三条角平分线交于一点,该点即为三角形的内心

2.三角形的内心到三边的距离相等,都等于内切圆半径r

3.r=2S/(a+b+c)

4.在Rt△ABC中,∠C=90°,r=(a+b-c)/2.

5.∠BOC = 90 °+∠A/2
∠BOA = 90 °+∠C/2
∠AOC = 90 °+∠B/2

6.S△=[(a+b+c)r]/2 (r是内切圆半径)

三角形的垂心

三角形的垂心是三角形三边上的高的交点(通常用H表示)。

三角形的三条高必交于一点

已知:△ABC中,AD、BE是两条高,AD、BE交于点O,连接CO并延长交AB于点F

三角形的三条高必交于一点

求证:CF⊥AB

证明:连接DE
∵∠ADB=∠AEB=90°,且在AB同旁,

∴A、B、D、E四点共圆 ∴∠ADE=∠ABE
(同弧上的圆周角相等)

∵∠EAO=∠DAC
∠AEO=∠ADC =90°

∴△AEO∽△ADC
∴AE/AD=AO/AC
即AE/AO=AD/AC

∴ΔEAD∽ΔOAC
∴∠ACF=∠ADE=∠ABE

又∵∠ABE+∠BAC=90°
∴∠ACF+∠BAC=90°
∴CF⊥AB

三角形的垂心的性质

1.锐角三角形的垂心在三角形内;直角三角形的垂心在直角顶点上;钝角三角形的垂心在三角形外

2.三角形的垂心是它垂足三角形的内心;或者说,三角形的内心是它旁心三角形的垂心

3. 垂心O关于三边的对称点,均在△ABC的外接圆圆上。

4.△ABC中,有六组四点共圆,有三组(每组四个)相似的直角三角形,且AO·OD=BO·OE=CO·OF

5. H、A、B、C四点中任一点是其余三点为顶点的三角形的垂心(并称这样的四点为一—垂心组)。

6.△ABC,△ABO,△BCO,△ACO的外接圆是等圆

7.在非直角三角形中,过O的直线交AB、AC所在直线分别于P、Q,则
AB/AP·tanB+AC/AQ·tanC=tanA+tanB+tanC

8.三角形任一顶点到垂心的距离,等于外心到对边的距离的2倍。

9.设O,H分别为△ABC的外心和垂心,则∠BAO=∠HAC,∠ABH=∠OBC,∠BCO=∠HCA。

10.锐角三角形的垂心到三顶点的距离之和等于其内切圆与外接圆半径之和的2倍。

11.锐角三角形的垂心是垂足三角形的内心;锐角三角形的内接三角形(顶点在原三角形的边上)中,以垂足三角形的周长最短。(施瓦尔兹三角形,最早在古希腊时期由海伦发现)

12.西姆松(Simson)定理(西姆松线):从一点向三角形的三边所引垂线的垂足共线的重要条件是该点落在三角形的外接圆上

13.设锐角△ABC内有一点P,那么P是垂心的充分必要条件是PB*PC*BC+PB*PA*AB+PA*PC*AC=AB*BC*CA。

14.设H为非直角三角形的垂心,且D、E、F分别为H在BC,CA,AB上的射影,H1,H2,H3分别为△AEF,△BDF,△CDE的垂心,则△DEF≌△H1H2H3。

15.三角形垂心H的垂足三角形的三边,分别平行于原三角形外接圆在各顶点的切线。

三角形的重心

三角形的重心是三角形三条中线的交点。

三角形的三条中线必交于一点

已知:△ABC的两条中线AD、CF相交于点O,连结并延长BO,交AC于点E。

三角形的三条中线必交于一点

求证:AE=CE

证明:延长OE到点G,使OG=OB

∵OG=OB,∴点O是BG的中点又∵点D是BC的中点∴OD是△BGC的一条中位线∴AD∥CG

∵点O是BG的中点,点F是AB的中点∴OF是△BGA的一条中位线 ∴CF∥AG

∵AD∥CG,CF∥AG,∴四边形AOCG是平行四边形 ∴AC、OG互相平分,∴AE=CE

三角形的重心的性质

1.重心到顶点的距离与重心到对边中点的距离之比为2:1。

2.重心和三角形3个顶点组成的3个三角形面积相等。

3.重心到三角形3个顶点距离的平方和最小。

4.在平面直角坐标系中,重心的坐标是顶点坐标的算术平均,即其坐标为((X1+X2+X3)/3,(Y1+Y2+Y3)/3);空间直角坐标系——横坐标:(X1+X2+X3)/3 纵坐标:(Y1+Y2+Y3)/3
竖坐标:(Z1+Z2+Z3)/3

5.重心和三角形3个顶点的连线的任意一条连线将三角形面积平分。

6.重心是三角形内到三边距离之积最大的点。

三角形的旁心

介绍

三角形的一条内角平分线与另两个内角的外角平分线相交于一点,是旁切圆的圆心,称为旁心。

旁心常常与内心联系在一起,旁心还与三角形的半周长关系密切,三角形有三个旁心。

三角形旁心的性质

1、三角形一内角平分线和另外两顶点处的外角平分线交于一点,该点即为三角形的旁心。

2、每个三角形都有三个旁心

3、旁心到三边的距离相等。

如图,点A就是△BCD的一个旁心。三角形任意两角的外角平分线和第三个角的内角平分线的交点。一个三角形有三个旁心,而且一定在三角形外。

欧拉线

非等边三角形的外心、重心、垂心,依次位于同一直线上,这条直线就叫三角形的欧拉线。其中,重心到外心的距离是重心到垂心距离的一半。

欧拉线的证法1

作△ABC的外接圆,连结并延长BO,交外接圆于点D。连结AD、CD、AH、CH、OH。作中线AM,设AM交OH于点G’

∵ BD是直径

∴∠BAD、∠BCD是直角

∴ AD⊥AB,DC⊥BC

∵ CH⊥AB,AH⊥BC

∴ DA‖CH,DC‖AH

∴四边形ADCH是平行四边形

∴ AH=DC

∵ M是BC的中点,O是BD的中点

∴ OM= 1/2DC

∴ OM= 1/2AH

∵ OM‖AH

∴△OMG’
∽△HAG’

∴AG/GM=2/1

∴ G’是△ABC的重心

∴ G与G’重合

∴ O、G、H三点在同一条直线上

如果使用向量,证明过程可以极大的简化,运用向量中的坐标法,分别求出O
G H三点的坐标即可.

欧拉线的证法2

设H,G,O,分别为△ABC的垂心、重心、外心。连接AG并延长交BC于D,
则可知D为BC中点。

连接OD
,又因为O为外心,所以OD⊥BC。连接AH并延长交BC于E,因H为垂心,所以
AE⊥BC。所以OD//AE,有∠ODA=∠EAD。由于G为重心,则GA:GD=2:1。

连接CG并延长交BA于F,则可知F为AB中点。同理,OF//CM.所以有∠OFC=∠MCF

连接FD,有FD平行AC,且有DF:AC=1:2。FD平行AC,所以∠DFC=∠FCA,∠FDA=∠CAD,又∠OFC=∠MCF,∠ODA=∠EAD,相减可得∠OFD=∠HCA,∠ODF=∠EAC,所以有△OFD∽△HCA,所以OD:HA=DF:AC=1:2;又GA:GD=2:1所以OD:HA=GA:GD=2:1

又∠ODA=∠EAD,所以△OGD∽△HGA。所以∠OGD=∠AGH,又连接AG并延长,所以∠AGH+∠DGH=180°,所以∠OGD+∠DGH=180°。即O、G、H三点共线

欧拉线的证法3

设H,G,O,分别为△ABC的垂心、重心、外心.

向量OH=向量OA+向量+OB+向量OC

向量OG=(向量OA+向量OB+向量OC)/3,

向量OG*3=向量OH

所以O、G、H三点共线

时间: 2024-10-10 01:59:53

Opencv源码之平面点集的最小包围圆的相关文章

平面点集的最小包围圆 hdu 3932

最小覆盖圆算法地址:http://soft.cs.tsinghua.edu.cn/blog/?q=node/1066 平面点集的最小包围圆 1.           问题背景 考察固定在工作平台上的一直机械手,要捡起散落在不同位置的多个零件,并送到别的地方.那么,这只机械手的底座应该选在哪里呢?根据直觉,应该选在机械手需够着的那些位置的"中心".准确地讲,也就是包围这些点的那个最小圆的圆心----该位置的好处是,可使机械手的底座到它需要够着的那些点的最大距离最小化.于是可得如下问题:给

opencv源码分析:cvCreateTreeCascadeClassifier

我使用的是opencv2.4.9,cvCreateTreeCascadeClassifier的源码在......opencv\sources\apps\haartraining\cvhaartraining.cpp之中,这中间用到很多结构体,指针函数,宏等各方面的内容,请参考我博客中的文章opencv源码详细解读目录.如需转载请注明本博网址http://blog.csdn.net/ding977921830?viewmode=contents.具体内容如下: /* * cvCreateCasca

OpenCV源码之内存分配-指针对齐

首先,为什么要指针对齐(Pointer Alignment)? 指针对齐有时候非常重要,因为许多硬件相关的东西在对齐上存在限制.在有些系统中,某种数据类型只能存储在偶数边界的地址处. 例如,在经典的 SPARC架构(以及经典的ARM)上,你不能从奇数地址读取一个超过1字节的整型数据.尝试这么做将会立即终止程序,并伴随着总线错误.而在X86架构上,CPU硬件处理了这个问题,只是这么做将会花费更多时间:通常RISC架构是不会为你做这些.举例如下: char c; char *Pc = &c; int

OpenCV源码解析

OpenCV K-means源码解析 OpenCV 图片读取源码解析 OpenCV 视频播放源码解析 读懂OpenCV源码需要哪些基础? 通读过C++ Primer,目前C++属于入门级, 数字图像处理基础也有.目前看不懂OpenCV源码,请问还需要哪些基础, 从哪些方面入手源码比较好呢?谢谢 回答: 半年前实习的时候,在那个公司参与用OpenCV c++版本的开发公司自己的CV库.体会还比较深,现在回想起来,大概会有这么一些建议: 1. C++需要多了解一下,建议看看Bjarne大神的书 Op

opencv源码编译

环境: 主机:Linux alvin-Lenovo-V310-14ISK 4.13.0-46-generic #51-Ubuntu SMP Tue Jun 12 12:36:29 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux linux下编译opencv源码步骤: 1.在官网 https://opencv.org/ 里面的Release里选择需要的版本下载,例如本次作者选择的是OpenCV – 4.1.0 2.解压源码包: unzip opencv-4.1.0

opencv源码交叉编译

环境: 主机:Linux alvin-Lenovo-V310-14ISK 4.13.0-46-generic #51-Ubuntu SMP Tue Jun 12 12:36:29 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux linux下交叉编译arm平台opencv源码步骤: 1.在官网 https://opencv.org/ 里面的Release里选择需要的版本下载,例如本次作者选择的是OpenCV – 4.1.0 2.解压源码包: unzip opencv

matlab练习程序(Ritter‘s最小包围圆)

原始算法是sphere,我这里简化为circle了. Ritter's求最小包围圆为线性算法,因为非常简单,所以应用非常广泛. 该算法求出的圆比最优圆大概会大个5%到20%左右,求最优圆应该可以用Bouncing Bubble算法,以后有机会可以尝试一下. Ritter's算法如下: 1.从点集中随机选出两个点作为直径对圆进行初始化. 2.判断下一个点p是否在圆中,如果在则继续本步骤,如果不在则进行步骤3. 3.使用p作为新圆的一个边界点,另一个边界点为距离p最远的圆上的点,使用这两个点作为直径

OpenCV 源码中分水岭算法 watershed 函数源码注解

为了研究分水岭算法,阅读了OpenCV 2.4.9 中watershed函数的源码实现部分,代码位于 opencv\sources\modules\imgproc\src\segmentation.cpp 文件中.先贴出加了注解的代码,以后补充对分水岭算法的解释. #include "precomp.hpp" /******************************************************* Watershed **********************

全景视频拼接(二)--OpenCV源码解析

本文参考:http://blog.csdn.net/skeeee/article/details/19480693,做了一定修改和补充. 一.stitching_detail程序运行流程 1.命令行调用程序,输入源图像以及程序的参数 2.特征点检测,判断是使用surf还是orb,默认是surf. 3.对图像的特征点进行匹配,使用最近邻和次近邻方法,将两个最优的匹配的置信度保存下来. 4.对图像进行排序以及将置信度高的图像保存到同一个集合中,删除置信度比较低的图像间的匹配,得到能正确匹配的图像序列