Delaunay Triangulation-狄洛尼三角剖分

一,狄洛尼三角剖分
1.1 三角剖分(triangulation)
给定平面上的一组点,将平面细分成以这些点为顶点的三角形。
假设V是二维实数域上的有限点集,边e是由点集中的点作为端点构成的封闭线段,E为e的集合。那么该点集V的一个三角剖分T=(V,E)是一个平面图G,该平面图满足条件:
(1)除了端点,平面图中的边不包含点集中的任何点。
(2)没有相交边。
(3)平面图中所有的面都是三角面,且所有三角面的合集是散点集V的凸包。
Delaunay Triangulation-狄洛尼三角剖分_第1张图片
1.2 狄洛尼三角剖分(Delaunay Triangulation)
狄洛尼三角剖分准则
1)空圆特性:狄洛尼三角网是唯一的(任意四点不能共圆),在狄洛尼三角形网中任一三角形的外接圆范围内不会有其它点存在。
Delaunay Triangulation-狄洛尼三角剖分_第2张图片
2)最大化最小角特性
在散点集可能形成的三角剖分中,狄洛尼三角剖分所形成的三角形的最小角最大。从这个意义上讲,狄洛尼三角网是“最接近于规则化的”三角网(在两个相邻的三角形构成凸四边形的对角线,在相互交换后,两个内角的最小角不再增大)。
Delaunay Triangulation-狄洛尼三角剖分_第3张图片
3)狄洛尼三角剖分特性
(1)最接近:以最接近的三点形成三角形,且各线段(三角行的边)皆不相交。
(2)唯一性:不论从区域何处开始构建,最终都将得到一致的结果。
(3)最优性:任意两个相邻三角形构成的凸四边形的对角线如何可以互换的话,那么两个三角形六个内角中最小角度不会变化。
(4)最规则:如果将三角网中的每个三角形的最小角进行升序排列,则狄洛尼三角网的排列得到的数值最大。
(5)区域性:新增、删除、移动某一个顶点只会影响邻近的三角形。
(6)具有凸边形的外壳:三角网最外层的边界形成一个凸多边形的外壳。

4)生成狄洛尼三角剖分的算法
Lawson算法:
逐点插入Lawson算法是Lawson在1977年提出的。该算法思路简单,易于编程实现。
(1)建立一个大的三角形或多边形,把所有数据点包围起来;
(2)向其中插入一点,该点与包含它的三角形三个顶点相连,形成三个新的三角形;
(3)逐个对它们进行空外接圆检测,同时用LOP进行优化,即通过交换对角线的方法来保证所形成的三角网为狄洛尼三角网。
Bowyer-Watson算法:
目前广泛采用的生成狄洛尼三角剖分的算法是Bowyer-Watson算法,这是一种逐点插入方式生成狄洛尼三角网的算法,主要步骤如下:
(1)建立初始三角网格:针对给定的点集V,找到一个包含该点集的矩形R,称R为辅助窗口。连接R的任意一条对角线,形成两个三角形,作为初始狄洛尼三角网格。
(2)逐点插入:假设已经有一个狄洛尼三角网格TT TT,在其内部再插入一个点P,找到点P所在三角形。从P所在的三角形开始,搜索该三角形的邻近三角形,并进行空外接圆检测。找到外接圆包含点P的所有的三角形并删除这些三角形,形成一个包含P的多边形空腔,称之为狄洛尼空腔。然后连接P与狄洛尼腔的每一个顶点,形成新的狄洛尼三角网格。
(3)删除辅助窗口R:重复(2),当点集V中所有点都已经插入到三角形网格中后,将顶点包含辅助窗口R的三角形全部删除。
Delaunay Triangulation-狄洛尼三角剖分_第4张图片

1.3 沃罗诺伊图
沃罗诺伊图(Voronoi Diagram,也称作Dirichlet tessellation,狄利克雷镶嵌)由俄国数学家格奥尔吉·沃罗诺伊建立的空间分割算法。灵感来源于笛卡尔用凸域分割空间的思想。在几何,晶体学建筑学,地理学,气象学,信息系统等许多领域有广泛的应用。
对三角剖分中的所有三角形各边做垂直平分线,将各相邻三角形三边的垂直平分线的交点(即外接圆圆心)连接起来得到一个多边形,称这个多边形为沃罗诺伊图。
Delaunay Triangulation-狄洛尼三角剖分_第5张图片

二,openCV的代码实现

# !/usr/bin/python

import cv2
import numpy as np
import random
import dlib  # 人脸识别的库 dlib


# 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("./faces/123.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 = [];

    # 使用特征提取器 get_frontal_face_detector正面
    detector = dlib.get_frontal_face_detector()
    # dlib 的68点模型,使用官方训练好的特征预测器
    predictor = dlib.shape_predictor("./model/shape_predictor_68_face_landmarks.dat")

    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

    # 使用人脸检测器检测每一帧图像中的人脸,并返回人脸数 faces
    faces = detector(img_gray, 0)

    if (len(faces) != 0):

        # 对每个人脸都标出68个特征点            
        # enumerate 方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象
        for k, d in enumerate(faces):
            # 用红色矩阵框出人脸, 光的三原色BGR Red(0,0,255), Green(0,255,0), Blue(255,0,0)
            #  rectangle(img, pt1, pt2, color), 其中pt1为矩阵上顶点,pt2为矩阵下顶点
            cv2.rectangle(img, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255))

            # 使用预测器得到68点数据的坐标
            shape = predictor(img, d)

            # 圆圈显示每个特征点
            for i in range(68):
                # circle(img, center, radius, color), 
                # img,Image where the circle is drawn
                # center,Center of the circle
                # radius,Radius of the circle (半径)
                # color,Circle color
                subdiv.insert((shape.part(i).x, shape.part(i).y))
                # 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)

三,效果图
Delaunay Triangulation-狄洛尼三角剖分_第6张图片
Delaunay Triangulation-狄洛尼三角剖分_第7张图片

你可能感兴趣的:(openCV,AI,图像的处理)