您的位置:首页 > 运维架构

对Open CV 中的平面划分相关函数使用探索

2014-11-12 16:44 183 查看
任意点集的三角网格化(triangulation)问题一直是人们密切关注的问题。三角网格化问题在许多领域有广泛应用。Delaunay 三角剖分是目前研究应用最广的一种剖分方法,因其具备很多优点,以下简单列举两条:

空外接圆性质:在由点集V-生成的D-三角网中,每个三角形的外接圆均不包含该点集的其他任意点。

最大最小角度性质:在由点集V-生成的D-三角网中,所有三角形中的最小角度是最大的。

Open CV中有Delaunay的实现,极大地方便了广大科研工作者。尽管Open CV提供了详细的文档,并且提供了相关sample,但是由于对原文档及参考书籍[1,3,4]的理解上的不足,笔者在使用过程中仍然遇到很多问题,下面将自己的一些理解及探索进行总结,谬误之处望大家批评指正。请注意,这里并不会详细介绍Open CV如何进行delaunay划分,请参考Open CV自带的示例程序delaunay.c.

1. 也说“四方边缘(Quad-edge)”结构



图1 边e以及与边e相关的边(该图来自Open CV文档)

这个结构图非常难懂(对我而言),但是非常关键,是Open CV 平面划分的最基本元素,数据结构如下:

01.
/*
quad-edge structure fields */


02.
#define
CV_QUADEDGE2D_FIELDS()     \


03.
int
flags;
\


04.
struct
CvSubdiv2DPoint*
pt[4];\


05.
CvSubdiv2DEdge
next[4];


06.


07.
typedef
struct
CvQuadEdge2D


08.
{


09.
CV_QUADEDGE2D_FIELDS()


10.
}


11.
CvQuadEdge2D;


这个结构的关键数据是数组next[4],其按顺序存放四条边(代码):e,eRot以及它们的反向边。注:我这里用“边代码”,原因是Open CV是用long型的代码来表示平面划分的一条边。

eLnext: e的左方区域(或上方区域)是一个多边形,e是其中一条边,eLnext是指向这个区域的另一条边,这个描述有点类似于数据结构中的十字链表的表示法,与e是连接在一起的;

eRnext: 理解同eLnext,只不过是指向e的右方区域(或下方区域)的另一条边,与e是连接在一起的;

eDnext: 与e共“目的点”的另一条边;

eOnext: 与e共“出发点”的另一条边;

eRot: e的对偶边,这就没什么好解释的了。

在了解这些知识点后,我们可以获得如下的应用:

2. 应用1-在Delaunay划分结束后获取三角形连接关系

(1) 首先,以边e开始循环查找与其相连的两条边就可以找到一个三角形,对所有边进行相同操作,就可以找到许多三角形,注意,这其中有许多重复的边,需要进行判断。主要代码如下:

01.
for
(
i = 0;i <total;i++)
//
total是边数目,可以参考Open CV示例程序delaunay.c


02.
{


03.
CvQuadEdge2D*
edge = (CvQuadEdge2D*)(reader.ptr);


04.


05.
if
(
CV_IS_SET_ELEM( edge ))


06.
{


07.
CvSubdiv2DEdge
e = (CvSubdiv2DEdge)edge;


08.
CvSubdiv2DEdge
t = e;


09.
CvPoint
buf[3];


10.
int
iPointNum
= 3;


11.


12.
for
(
int
j
= 0;j <iPointNum;j++){


13.
CvSubdiv2DPoint*
pt = cvSubdiv2DEdgeOrg( t );


14.
if
(
!pt )
break
;


15.
buf[j]
= cvPoint( cvRound(pt->pt.x), cvRound(pt->pt.y));


16.
t
= cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );


17.
}


18.
if
(j
== iPointNum) {


19.


20.
AddTriangle(buf);
//
添加三角形


21.
}


22.


23.
CV_NEXT_SEQ_ELEM(
elem_size, reader );


24.
}


(2) 其次,因为在Delaunay划分中,所有边是有方向的,光通过e进行轮循可能会遗失部分三角形,因此同时还得以e的反向边进行轮循,上面的代码可以改为如下:

01.
Bool
FindTriangleFromEdge(CvSubdiv2DEdge e)


02.
{


03.
CvSubdiv2DEdge
t = e;


04.
CvPoint
buf[3];


05.
CvPoint
*pBuf = buf;


06.
int
iPointNum
= 3;


07.


08.
for
(
int
j
= 0;j <iPointNum;j++){


09.
CvSubdiv2DPoint*
pt = cvSubdiv2DEdgeOrg( t );


10.
if
(
!pt )
break
;


11.
buf[j]
= cvPoint( cvRound(pt->pt.x), cvRound(pt->pt.y));


12.
t
= cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );


13.
}


14.
if
(j
== iPointNum) {


15.


16.
AddTriangle(buf);
//
添加三角形


17.
return
true
;


18.
}


19.


20.
return
false
;


21.
}


22.


23.


24.
//
调用代码如下


25.


26.
for
(
i = 0;i <total;i++)


27.
{


28.
CvQuadEdge2D*
edge = (CvQuadEdge2D*)(reader.ptr);


29.


30.
if
(
CV_IS_SET_ELEM( edge ))


31.
{


32.
CvSubdiv2DEdge
e = (CvSubdiv2DEdge)edge;


33.
FindTriangleFromEdge(e);


34.


35.
CvSubdiv2DEdge
e1 = (CvSubdiv2DEdge)edge+2;
//即next[2]


36.


37.
FindTriangleFromEdge(e1);


38.
}


39.
CV_NEXT_SEQ_ELEM(
elem_size, reader );


40.
}


在上面的代码中,是直接采用数组位移法进行各种边的对应的(即edge+2),不过Open CV已经有了自己的实现函数:cvSubdiv2DRotateEdge,上面用红色粗体标注的语句可以换为:

1.
CvSubdiv2DEdge
e1 = cvSubdiv2DRotateEdge((CvSubdiv2DEdge)edge,2);


对于16个点的输入,Delaunay分割的结果如图2所示。

3. 应用2-在Vonoroi划分结束后获取多边形

参考Delaunay.c中的函数:void paint_voronoi( CvSubdiv2D* subdiv, IplImage* img );结果如图3所示。

有了应用1的分析,理解这段代码也很容易。


图2. 16个点的Delaunay三角剖分结果图3. 相应的Voronoi划分结果
注:读者要编译附带源程序,请安装Open CV库1.0正式版(最好安装在默认目录,否则需要修改工程配置路径)。

参考文献

1.刘瑞祯,于仕琪. Open CV教程. 北京航空航天大学出版社. 2007年6月

2.高晓

代码地址:http://download.csdn.net/detail/u011206733/8149671
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  计算机视觉 opencv