学习笔记 - 计算机视觉 - 狄洛尼三角剖分

[外链图片转存中…(img-tW5xUJwu-1567856655225)]

1 狄洛尼三角剖分

1.1 三角剖分(triangulation)

给定平面上的一组点,将平面细分成以这些点为顶点的三角形。

假设 V V V是二维实数域上的有限点集,边 e e e是由点集中的点作为端点构成的封闭线段, E E E e e e的集合。那么该点集 V V V的一个三角剖分 T = ( V , E ) T=(V,E) T=(V,E)是一个平面图 G G G,该平面图满足条件:

(1)除了端点,平面图中的边不包含点集中的任何点。

(2)没有相交边。

(3)平面图中所有的面都是三角面,且所有三角面的合集是散点集 V V V的凸包。

1.2 狄洛尼三角剖分(Delaunay Triangulation)

  • 狄洛尼边(Delaunay edge)

假设 E E E中的一条边 e e e(两个端点为 a , b a,b a,b), e e e若满足条件:存在一个圆经过 a , b a,b a,b两点,圆内(注意是圆内,圆上最多三点共圆)不含点集 V V V中任何其他的点,则称之为狄洛尼边,这一特性又称空圆特性。

  • 狄洛尼三角剖分(Delaunay Triangulation)

如果点集 V V V的一个三角剖分 T T T只包含狄洛尼边,那么该三角剖分称为狄洛尼三角剖分。

[外链图片转存中…(img-R3dUcXOe-1567856655225)]

  • 狄洛尼三角剖分准则

(1)空圆特性:狄洛尼三角网是唯一的(任意四点不能共圆),在狄洛尼三角形网中任一三角形的外接圆范围内不会有其它点存在。

(2)最大化最小角特性:在散点集可能形成的三角剖分中,狄洛尼三角剖分所形成的三角形的最小角最大。从这个意义上讲,狄洛尼三角网是“最接近于规则化的”三角网(在两个相邻的三角形构成凸四边形的对角线,在相互交换后,两个内角的最小角不再增大)。

  • 狄洛尼三角剖分特性

(1)最接近:以最接近的三点形成三角形,且各线段(三角行的边)皆不相交。

(2)唯一性:不论从区域何处开始构建,最终都将得到一致的结果。

(3)最优性:任意两个相邻三角形构成的凸四边形的对角线如何可以互换的话,那么两个三角形六个内角中最小角度不会变化。

(4)最规则:如果将三角网中的每个三角形的最小角进行升序排列,则狄洛尼三角网的排列得到的数值最大。

(5)区域性:新增、删除、移动某一个顶点只会影响邻近的三角形。

(6)具有凸边形的外壳:三角网最外层的边界形成一个凸多边形的外壳。

1.2.1 Lawson算法

逐点插入Lawson算法是Lawson在1977年提出的。该算法思路简单,易于编程实现。

  • 局部寻优(LOP,local optimization procedure)

(1)将两个具有共同边的三角形合成一个多边形。

(2)以最大空圆准则作检查,看其第四个顶点是否在三角形的外接圆之内。

(3)如果在,修正对角线即将对角线对调,即完成局部优化过程的处理。

一般三角网经过LOP处理,可确保成其为狄洛尼三角网。

  • Lawson算法

(1)建立一个大的三角形或多边形,把所有数据点包围起来;

(2)向其中插入一点,该点与包含它的三角形三个顶点相连,形成三个新的三角形;

(3)逐个对它们进行空外接圆检测,同时用LOP进行优化,即通过交换对角线的方法来保证所形成的三角网为狄洛尼三角网。

  • 优点

(1)理论严密、唯一性好,网格满足空圆特性,较为理想。

(2)完成构网后,增加新点时,无需对所有的点进行重新构网,只需对新点的影响三角形范围进行局部联网,且局部联网的方法简单易行。

(3)点的删除、移动也可快速动态地进行。

  • 缺点

(1)点集较大时构网速度慢。

(2)如果点集范围是非凸区域或者存在内环,则会产生非法三角形。

当离散点集构成圆环时,Lawson算法产生的非法三角形

1.2.2 Bowyer-Watson算法

目前广泛采用的生成狄洛尼三角剖分的算法是Bowyer-Watson算法,这是一种逐点插入方式生成狄洛尼三角网的算法,主要步骤如下:

(1)建立初始三角网格:针对给定的点集 V V V,找到一个包含该点集的矩形 R R R,称 R R R为辅助窗口。连接 R R R的任意一条对角线,形成两个三角形,作为初始狄洛尼三角网格。

(2)逐点插入:假设已经有一个狄洛尼三角网格 T T T,在其内部再插入一个点 P P P,找到点 P P P所在三角形。从 P P P所在的三角形开始,搜索该三角形的邻近三角形,并进行空外接圆检测。找到外接圆包含点 P P P的所有的三角形并删除这些三角形,形成一个包含 P P P的多边形空腔,称之为狄洛尼空腔。然后连接 P P P与狄洛尼腔的每一个顶点,形成新的狄洛尼三角网格。

(3)删除辅助窗口 R R R:重复(2),当点集 V V V中所有点都已经插入到三角形网格中后,将顶点包含辅助窗口 R R R的三角形全部删除。

1.3 沃罗诺伊图

  • 平面划分

将一个平面分割为一组不重叠的、能够覆盖整个平面的区域。

  • 沃罗诺伊图(Voronoi Diagram,也称作Dirichlet tessellation,狄利克雷镶嵌)

由俄国数学家格奥尔吉·沃罗诺伊建立的空间分割算法。灵感来源于笛卡尔用凸域分割空间的思想。在几何,晶体学建筑学,地理学,气象学,信息系统等许多领域有广泛的应用。

对三角剖分中的所有三角形各边做垂直平分线,将各相邻三角形三边的垂直平分线的交点(即外接圆圆心)连接起来得到一个多边形,称这个多边形为沃罗诺伊图

2 实现(Learn OpenCV)

2.1 类Subdiv2D介绍

给定点集 V V V,OpenCV提供类Subdiv2D实现狄洛尼三角剖分和沃罗诺伊图

(1)将点集 V V V存入列表

points = []
# This is how you can add one point. 
points.append((x, y))

(2)定义一个包含整个点集的矩形,对于图像可取整个图像范围。

img = cv2.imread("image.jpg");
size = img.shape
rect = (0, 0, size[1], size[0])

(3)创建类Subdiv2D的实例,subdiv

subdiv  = cv2.Subdiv2D(rect)

(4)将点依次插入subdiv

subdiv.insert(point)

(5)读取狄洛尼三角列表

subdiv.getTriangleList()

(6)读取沃罗诺伊面列表

subdiv.getVoronoiFacetList([])

2.2 代码

#!/usr/bin/python

import cv2
import numpy as np
import random

# Check if a point is inside a rectangle
def rect_contains(rect, point) :
    if point[0] < rect[0] :
        return False
    elif point[1] < rect[1] :
        return False
    elif point[0] > rect[2] :
        return False
    elif point[1] > rect[3] :
        return False
    return True

# Draw a point
def draw_point(img, p, color ) :
    cv2.circle(img, p, 2, color, -1, cv2.LINE_AA, 0)


# Draw delaunay triangles
def draw_delaunay(img, subdiv, delaunay_color) :

    triangleList = subdiv.getTriangleList();
    size = img.shape
    r = (0, 0, size[1], size[0])

    for t in triangleList :
        
        pt1 = (t[0], t[1])
        pt2 = (t[2], t[3])
        pt3 = (t[4], t[5])
        
        if rect_contains(r, pt1) and rect_contains(r, pt2) and rect_contains(r, pt3) :
        
            cv2.line(img, pt1, pt2, delaunay_color, 1, cv2.LINE_AA, 0)
            cv2.line(img, pt2, pt3, delaunay_color, 1, cv2.LINE_AA, 0)
            cv2.line(img, pt3, pt1, delaunay_color, 1, cv2.LINE_AA, 0)

# Draw voronoi diagram
def draw_voronoi(img, subdiv) :

    (facets, centers) = subdiv.getVoronoiFacetList([])

    for i in range(len(facets)) :
        ifacet_arr = []
        for f in facets[i] :
            ifacet_arr.append(f)
        
        ifacet = np.array(ifacet_arr, np.int)
        color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

        cv2.fillConvexPoly(img, ifacet, color, cv2.LINE_AA, 0);
        ifacets = np.array([ifacet])
        cv2.polylines(img, ifacets, True, (0, 0, 0), 1, cv2.LINE_AA, 0)
        cv2.circle(img, (centers[i][0], centers[i][1]), 3, (0, 0, 0), -1, cv2.LINE_AA, 0)
        
%matplotlib inline
import matplotlib.pyplot as plt
import math

def plotImgs(idxFig, lstImgs):

    fig = plt.figure(idxFig, figsize=(12, 24))
    fig.clf()

    intNumImgs = len(lstImgs)
    intNumCols = int(math.sqrt(intNumImgs))
    intNumRows = int(math.ceil(intNumImgs / intNumCols))
    
    idxImg = 0
    for idxRow in range(intNumRows):
        for idxCol in range(intNumCols):

            ax = fig.add_subplot(intNumRows, intNumCols, idxImg + 1, frameon=False)
            if lstImgs[idxImg].dtype == "uint8":
                ax.imshow(cv2.cvtColor(lstImgs[idxImg], cv2.COLOR_BGR2RGB))
            else:
                ax.imshow(lstImgs[idxImg])
            ax.get_yaxis().set_visible(False)
            ax.get_xaxis().set_visible(False)
            idxImg += 1
            if idxImg >= intNumImgs:
                break

    plt.show()
if __name__ == '__main__':

    # Define window names
    win_delaunay = "Delaunay Triangulation"
    win_voronoi = "Voronoi Diagram"

    # Turn on animation while drawing triangles
    animate = True
    
    # Define colors for drawing.
    delaunay_color = (255,255,255)
    points_color = (0, 0, 255)

    # Read in the image.
    img = cv2.imread("donald_trump.jpg");
    
    # Keep a copy around
    img_orig = img.copy();
    
    # Rectangle to be used with Subdiv2D
    size = img.shape
    rect = (0, 0, size[1], size[0])
    
    # Create an instance of Subdiv2D
    subdiv = cv2.Subdiv2D(rect);

    # Create an array of points.
    points = [];
    
    # Read in the points from a text file
    with open("donald_trump.jpg.txt") as file :
        for line in file :
            x, y = line.split()
            points.append((int(x), int(y)))

    # Insert points into subdiv
    for p in points :
        subdiv.insert(p)
        
        # Show animation
        if animate :
            img_copy = img_orig.copy()
            # Draw delaunay triangles
            draw_delaunay(img_copy, subdiv, (255, 255, 255));
            cv2.imshow(win_delaunay, img_copy)
            cv2.waitKey(100)

    # Draw delaunay triangles
    draw_delaunay(img, subdiv, (255, 255, 255));

    # Draw points
    for p in points :
        draw_point(img, p, (0,0,255))

    # Allocate space for voronoi Diagram
    img_voronoi = np.zeros(img.shape, dtype = img.dtype)

    # Draw voronoi diagram
    draw_voronoi(img_voronoi, subdiv)

    # Show results
    cv2.imshow(win_delaunay, img)
    cv2.imshow(win_voronoi, img_voronoi)
    cv2.waitKey(0)
idxFig = 1
lstImgs = [img, img_voronoi]
plotImgs(idxFig, lstImgs)

你可能感兴趣的:(学习笔记 - 计算机视觉 - 狄洛尼三角剖分)