Deluanary三角细分及voronoi图绘制的Opencv实现(未完待续)

一、预备知识:

Deluanary三角细分及voronoi图概念。

Delaunay三角剖分几种生成方法。


二、Opencv中delaunary三角细分和voronoi图的实现方法

    opencv使用四方边缘结构来存储deluanary三角细分和voronoi图的边缘信息,数据类型为CvQuadEdge2D,而存储CvQuadEdge2D的数据结构为CvSubdiv2D。CvSubdiv2D包含delaunary三角细分和voronoi剖分的所有边的信息。

2.1、四方边缘结构(QE)

    每个QE单元存储四条有向边,其中两条为主边(Primal Edge):e0和e2,描述当前的二维平面剖分S;另外两条为对偶边(Dual Edge):e1和e3,描述S的对偶图。

四方边缘结构

每条Edge定义有三个基本行为:
    Org:返回当前有向边的起点。
    Rot:返回当前QE单元中逆时针旋转90度后的有向边,也即当前Edge的对偶边。
    Onext:返回S中以当前有向边Org为基点逆时针旋转到的下一条Edge。

    可以选取QE单元的任意一条Edge作为起始边来表示QE中的四条Edge。设起始边为e0。,则任意的有向边可以表示为(e, r),有如下关系:

这里写图片描述

基于基本行为还可以派生出更多的行为:
这里写图片描述

    边e的起点和终点是给定点集中的两个点,边e是Deluanary三角形某个三角形的边,eRot是边e对应Voronoi图中的一条边。一个四方边缘结构包含点集中某两点的Delauanry三角形边信息和Voronoi图边信息,eLnext、eDnext,eOnext、eRnext是属于其他边缘四方结构的Delauanry边和Voronoi边,不同边缘四方结构通过eLnext、eDnext,eOnext、eRnext产生空间联系。所有的边缘四方结构存储在一个CvSubdiv2D数据结构中。

    CvSubdiv2D数据结构有点复杂,也许需要有点相关专业基础才能理解,至少本人是没有理解透,虽然不能理解,但只要知道怎么用就 行了。下面例子详细说明了如何操作该数据结构。

三、实现代码

    新建一个工程,添加一下两个文件:
1. delaunayfunc.h
2. delaunayfunc.cpp

delaunayfunc.h 代码

/* delaunayfunc.h */
#include <opencv2/legacy/legacy.hpp>  
#include<opencv2\opencv.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<opencv2/nonfree/nonfree.hpp>
#include<iostream> 
using namespace std;  
using namespace cv; 

 CvSubdiv2D* init_delaunay( CvMemStorage* storage,CvRect rect ) ;
 inline void draw_edge( IplImage* img, CvSubdiv2DEdge edge, CvScalar color );
 void draw_delaunay( IplImage* img, CvSubdiv2D* subdiv,CvScalar delaunay_color);
 void draw_voronoi( CvSubdiv2D* subdiv, IplImage* img );
 void draw_facet( IplImage* img, CvSubdiv2DEdge edge );
 inline void draw_point( IplImage* img, CvPoint2D32f fp, CvScalar color );
 void locate_pointInDelaunay( CvSubdiv2D* subdiv, CvPoint2D32f fp, IplImage* img, CvScalar active_color );
 void locate_pointInVoronoi(CvSubdiv2D* subdiv,CvPoint2D32f fp,IplImage* img,CvScalar color);

delaunayfunc.cpp 代码

/* delaunayfunc.cpp */
#include "delaunayfunc.h"

//初始化初始三角形
CvSubdiv2D* init_delaunay( CvMemStorage* storage,CvRect rect )  
{  
    CvSubdiv2D* subdiv;//三角剖分的数据单元  

    subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*subdiv),  
        sizeof(CvSubdiv2DPoint),  
        sizeof(CvQuadEdge2D),  
        storage );  
    cvInitSubdivDelaunay2D( subdiv, rect );  

    return subdiv;  
}

//画出三角剖分的某一条边
 inline void draw_edge( IplImage* img, CvSubdiv2DEdge edge, CvScalar color ) 
{  
    CvSubdiv2DPoint* org_pt;//源顶点  
    CvSubdiv2DPoint* dst_pt;//目地顶点  
    CvPoint2D32f org;  
    CvPoint2D32f dst;  
    CvPoint iorg, idst;  

    org_pt = cvSubdiv2DEdgeOrg(edge);//通过边获取顶点  
    dst_pt = cvSubdiv2DEdgeDst(edge);  

    if( org_pt && dst_pt )//如果两个端点不为空  
    {  
        org = org_pt->pt;  
        dst = dst_pt->pt;  

        iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));  
        idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));  

        cvLine( img, iorg, idst, color, 1, CV_AA, 0 );
    }
}

//画出Deluanary细分
 void draw_delaunay( IplImage* img, CvSubdiv2D* subdiv, CvScalar delaunay_color  )  
{  
    CvSeqReader  reader;  
    int i, total = subdiv->edges->total;//边的数量  
    int elem_size = subdiv->edges->elem_size;//边的大小  

    cvStartReadSeq( (CvSeq*)(subdiv->edges), &reader, 0 );//使用CvSeqReader遍历Delaunay或者Voronoi边  

    for( i = 0; i < total; i++ )  
    {  
        CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);  
        //CvSubdiv2DEdge edge1 = (CvSubdiv2DEdge)(reader.ptr);  //这样赋值也可以,其实edge1和edge指向的是同一片内存,因为edge的最低2位是00,可以使用下面的方式验证
        //size_t t = (size_t)edge;size_t t1 = (size_t)edge1;if (t == t1) printf("edge1 == edge\n");

        if( CV_IS_SET_ELEM( edge ))  
        {  
            draw_edge( img, (CvSubdiv2DEdge)edge, delaunay_color );  
        }  

        CV_NEXT_SEQ_ELEM( elem_size, reader );  
    }  

} 


//画出voronoi面  
 void draw_voronoi( CvSubdiv2D* subdiv, IplImage* img )
{  
    CvSeqReader  reader;  
    int i, total = subdiv->edges->total;//边缘总数  
    int elem_size = subdiv->edges->elem_size;//边缘的大小 

    cvStartReadSeq( (CvSeq*)(subdiv->edges), &reader, 0 );  

    for( i = 0; i < total; i++ )  
    {  
        CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);//获取四方边缘  

        if( CV_IS_SET_ELEM( edge ))//判断边缘是否在边缘集中  
        {  
            CvSubdiv2DEdge e = (CvSubdiv2DEdge)edge;//edge是四方边缘的指针,而CvSubdiv2DEdge高位表示四方边缘的指针。  
            //cout<<(e&3)<<endl;//通过测试e低2位即索引值应该设置为0了,即输入边缘  
            // left  逆时针遍历小区域
            draw_facet( img, cvSubdiv2DRotateEdge( e, 1 ));//e为Delaunay边,获得Delaunay边对应的voronoi边,即e的旋转边缘  

            // right  顺时针遍历小区域
            //draw_facet( img, cvSubdiv2DRotateEdge( e, 3 ));//反向的旋转边缘  
        }  

        CV_NEXT_SEQ_ELEM( elem_size, reader );//移动到下一个位置  
    }  
}


//画出voronoi中某点占有的小区域 
 void draw_facet( IplImage* img, CvSubdiv2DEdge edge )
{  
    //cout<<edge<<endl;//edge低两位表示表示索引,高位表示四方边缘指针。  
    //cout<<(edge&3)<<endl;  
    CvSubdiv2DEdge t = edge;//当我们按上面的调用形式时,edge为eRot。  
    int i, count = 0;  
    CvPoint* buf = 0;  
    Point2d *buf1=0;  

    // count number of edges in facet //面内边的计数  
    do  
    {  
        count++;  
        t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );  
    } while (t != edge );//我们绕着一个voronoi单元一周,遍历该vornonoi边缘所拥有的边缘数。  

    buf = (CvPoint*)malloc( count * sizeof(buf[0]));  
    buf1=(Point2d*)malloc(count*sizeof(buf1[0]));  

    // gather points  
    t = edge;  
    for( i = 0; i < count; i++ )  
    {  
        CvSubdiv2DPoint* pt = cvSubdiv2DEdgeOrg( t );//第一次获取eRot边缘的起始点  
        if( !pt ) break;//如果得不到该源点,则退出循环  
        buf[i] = cvPoint( cvRound(pt->pt.x), cvRound(pt->pt.y));//将该点转换为cvPoint类型点,存储在buf中  
        t = cvSubdiv2DGetEdge( t, CV_NEXT_AROUND_LEFT );//然后绕着vornonoi单元,左旋转。  
    }  

    if( i == count )//如果所有的点都存储起来了。  
    {  
        CvSubdiv2DPoint* pt = cvSubdiv2DEdgeDst( cvSubdiv2DRotateEdge( edge, 1 ));//这里eRot的旋转边缘应该是reversed e,那么目的点,就是e的源点。  
        cvFillConvexPoly( img, buf, count, CV_RGB(rand()&255,rand()&255,rand()&255), CV_AA, 0 );//颜色随机填充凸多边形  
        for(i=0;i<count;i++)  
        {  
            buf1[i].x=buf[i].x;  
            buf1[i].y=buf[i].y;  
        }  
        Mat mat_img(img);  

        cvPolyLine( img, &buf, &count, 1, 1, CV_RGB(0,200,0), 1, CV_AA, 0);//画出线。   
        draw_point( img, pt->pt, CV_RGB(255,0,0));//用红色画出画出剖分顶点。  
    }  
    free( buf );  
}

//定位给定点所在Delaunary三角型的三边,关键在于确定给定点与定位边的位置关系
 void locate_pointInDelaunay( CvSubdiv2D* subdiv, CvPoint2D32f fp, IplImage* img,
    CvScalar active_color )                                                         
{  
    CvSubdiv2DEdge e;  
    CvSubdiv2DEdge e0 = 0;  
    CvSubdiv2DPoint* p = NULL;

    CvSubdiv2DPoint* org_pt;//源顶点  
    CvSubdiv2DPoint* dst_pt;//目地顶点  
    CvPoint2D32f org;  
    CvPoint2D32f dst;  
    //CvPoint iorg, idst;

    cvSubdiv2DLocate( subdiv, fp, &e0, &p );    //定位点的位置

    org_pt = cvSubdiv2DEdgeOrg(e0);//通过边获取顶点  
    dst_pt = cvSubdiv2DEdgeDst(e0); 
    if( org_pt && dst_pt )//如果两个端点不为空  
    {  
        org = org_pt->pt;  
        dst = dst_pt->pt;  
    //  iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));  
    //  idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));  
    }
    if( e0 )  
    {  
        e = e0;  
        do  
        {  
            draw_edge( img, e, active_color );  
            e = cvSubdiv2DGetEdge(e,CV_NEXT_AROUND_RIGHT);  
            cvSaveImage("i.jpg",img);
        }  
        while( e != e0 );  
    }  
    cvSaveImage("i.jpg",img);
    //draw_point( img, fp, active_color );  
}  


 void locate_pointInVoronoi(CvSubdiv2D* subdiv,CvPoint2D32f fp,IplImage* img,CvScalar color)
{
    CvSubdiv2DPoint* pt = cvFindNearestPoint2D(subdiv,fp);
    CvSubdiv2DEdge e = 0;
    CvSubdiv2DEdge e0 = 0;  
    CvSubdiv2DPoint* p = NULL;
    CvSubdiv2DPoint* org_pt,*dst_pt;
    CvPoint2D32f org;
    CvPoint iorg;

    cvSubdiv2DLocate( subdiv, fp, &e0, &p );    //定位点的位置
    draw_facet(img,cvSubdiv2DRotateEdge(e0,1));
    //*
    org_pt = cvSubdiv2DEdgeOrg(e0);
    org = org_pt->pt;
    iorg = cvPoint(cvRound(org.x),cvRound(org.y));

    if( e0 )  
    {  
        e = e0;  
        do  
        {  
        //  draw_edge( img, e, active_color );  
            e = cvSubdiv2DGetEdge(e,CV_NEXT_AROUND_LEFT);  
        }  
        while( e != e0 );  
    }  


    /**/
}


//画出三角剖分的顶点
 inline void draw_point( IplImage* img, CvPoint2D32f fp, CvScalar color )  
{  
    cvCircle( img, cvPoint(cvRound(fp.x), cvRound(fp.y)),3, color, CV_FILLED, 8, 0 );  
} 

在主工程文件中添加代码

/* 主工程文件中添加的代码 */

#include <opencv2/legacy/legacy.hpp>  
#include<opencv2\opencv.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<opencv2/nonfree/nonfree.hpp>
#include<iostream> 
#include "delaunayfunc.h"
using namespace std;  
using namespace cv;  

void main()
{
    // Delaunay 三角形初始化工作
    CvScalar active_facet_color, delaunay_color, voronoi_color, bkgnd_color; 
    active_facet_color = CV_RGB( 255, 0, 0 );  
    delaunay_color  = CV_RGB( 255,0,0);  
    voronoi_color = CV_RGB(0, 180, 0); 
    bkgnd_color = CV_RGB(255,255,255);

    char delaunay[] = "delaunay";
    char Voronoi[] = "Voronoi";
    cvNamedWindow( delaunay, 1 ); 
    cvNamedWindow(Voronoi,1);

    CvRect rect = { 0, 0, 600, 600};  
    CvMemStorage* storage = cvCreateMemStorage(0); ;  
    CvSubdiv2D* subdiv= init_delaunay( storage, rect ); 


    IplImage* img_delaunay = cvCreateImage( cvSize(rect.width,rect.height), 8, 3 ); 
    IplImage* img_Voronoi = cvCreateImage( cvSize(rect.width,rect.height), 8, 3 ); 
    cvSet( img_delaunay, bkgnd_color, 0 );  
    cvSet( img_Voronoi, bkgnd_color, 0 );



        for(int i = 0;i<20;i++)  
        {  
            CvPoint2D32f fp = cvPoint2D32f( (float)(rand()%(rect.width-10)+5),
                (float)(rand()%(rect.height-10)+5));

            cvSubdivDelaunay2DInsert( subdiv, fp );//插入新点,重新计算新生成的Delaunary三角形
            draw_point(img_delaunay,fp,cvScalarAll(0)); //高亮插入点
        }

        cvCalcSubdivVoronoi2D( subdiv );//由Delaunary三角形计算Voronoi图
        draw_delaunay( img_delaunay, subdiv, delaunay_color);

        cvShowImage( delaunay, img_delaunay );


        cvSet( img_Voronoi, bkgnd_color, 0 );//重新刷新画布,即设置背景颜色为白色  
        draw_voronoi( subdiv, img_Voronoi );//画出voronoi划分 
        cvShowImage(Voronoi,img_Voronoi);


    cvWaitKey(0);  

    cvReleaseMemStorage( &storage );  
    cvReleaseImage(&img_delaunay); 
    cvReleaseImage(&img_Voronoi); 
    cvDestroyAllWindows();
}

结果如下图:
结果

四、引用:

http://www.cnblogs.com/soroman/archive/2007/05/17/750430.html


转载请注明作者和出处:http://blog.csdn.net/holamirai,未经允许请勿用于商业用途

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值