[Computational Geometry] Principle and Implementation of Delaunay Triangulation


Abstract : The triangulation of plane point set is an extremely important preprocessing technique in the fields of numerical analysis and graphics. As a widely used triangulation technique, Delaunay triangulation ensures close and regular triangulation and uniqueness by maximizing the minimum angle. This paper implements an incremental Delaunay triangulation construction algorithm by summarizing the principle of Delaunay triangulation. Experiments are carried out on real face feature point data and simulated data, and tests are carried out on different data scales. The results show the effectiveness of the algorithm.

Keywords: Delaunay triangulation; Voronoi diagram; Delaunay diagram; triangulation


introduction

In mathematics and computational geometry, for a discrete point set PP given a general positionP , whose Delaunay triangulation is denoted asDT ( P ) DT(P)D T ( P ) , satisfyingPPNo point in P is in DT ( P ) DT(P)DT ( P ) in the circumcircle of any triangle . Delaunay triangulation is the maximization of the minimum angle of all triangles in the triangulation, tending to avoid long and narrow triangles. This triangulation is named after Boris Delaunay, who started working on the subject in 1934.

For a set of points on the same line, there is no Delaunay triangulation; for four or more points on the same circle, there are non-unique Delaunay triangulations. The concept of Delaunay triangulation can be extended to three dimensions and beyond by considering circumscribed spheres, and generalizations to metrics other than Euclidean distance are possible. However, in these cases the Delaunay triangulation is not guaranteed to exist or be unique.

This paper implements the Delaunay triangulation algorithm, the experiment verifies the effectiveness of the algorithm, and will be used in the triangulation of face feature point data.

Delaunay triangulation

In this section, the definition of Delaunay graph is first introduced from the dual graph of Voronoi graph, and then the definition of Delaunay triangulation is introduced and how to construct Delaunay triangulation is discussed.

Voronoi figure

Plane Discrete Point Set PPThe Voronoi diagram of P is a subdivision of the plane, which containsnnn subregions, corresponding toPPEach base pointp ∈ P p \in PpThe sub-region corresponding to P is represented by ppp is composed of all points closest to the base point.

Dual graphs of Voronoi diagrams

P P The Voronoi diagram of P , denoted asV or ( P ) Vor(P)V or ( P ) . _ with base pointppThe subregion corresponding to p is called ppThe Voronoi unit of p , denoted asV ( p ) V(p)V ( p ) . As shown, this paper mainly studies the dual graph of the Voronoi graph, denoted asGGG. _ Among them, corresponding to each Voronoi unit, there is a node. If two units share a common edge, an arc is connected between the corresponding nodes of the two units. Therefore, corresponding toV or ( P ) Vor(P)For each edge in V o r ( P ) , GGThere is an arc corresponding to it in G. As shown, inGGAll interfaces of G with V or ( P ) Vor(P)There is a one-to-one correspondence between all vertices in V o r ( P ) .

Delaunay diagram

Delaunay diagram

Investigate GGThe following straight line embedding of G. where, with the Voronoi unitV ( p ) V(p)The node corresponding to V ( p ) , with point ppp to achieve; while connected toV ( p ) V(p)V ( p ) andV ( q ) V(q)For the arc between V ( q ) , use the line segmentpq ‾ \overline{pq}pqto achieve (as shown in the figure below). This linear embedding, called PPThe Delaunay graphof P , denoted asDG ( P ) DG(P)D G ( P ) . A Delaunay graph of a set of points is always a planar graph in which no two edges in the straight line embedding cross each other.

Theorem 1. The Delaunay graph of any set of planar points is a planar graph.

Proof 1. In order to prove this conclusion, a characteristic of the Voronoi diagram needs to be used. Here, the concept of the Delaunay diagram will be used for a concrete proof.

As shown, pipj ‾ \overline{p_ip_j}pipjis the Delaunay graph DG ( P ) DG(P)An edge in D G ( P ) if and only if there exists such a closed diskC ij C_{ij}Cij: its boundary goes through pi p_ipiand pj p_jpj, and which does not contain theAny other base point of P.

The interior of the circle defined by each pair of base points and any point on their common boundary must be empty.

by vertex pi p_ipi p j p_j pjand C ij C_{ij}CijThe triangle determined by the center of the center is denoted as tij t_{ij}tij. It can be seen that tij t_{ij}tijis connected to pi p_ipiAnd the center C ij C_{ij}CijThe edge between must fall completely on V ( pi ) V(p_i)V(pi) within;pj p_jpjalso have similar properties. Now, take any DG ( P ) DG(P)Another edgepkpl ‾ \overline{p_kp_l} in D G ( P )pkpl, refer to C ij C_{ij}Cij t i j t_{ij} tijThe definition method of the disk C kl C_{kl} can also be definedCkland the triangle tkl t_{kl}tkl

If this theorem does not hold , that is, pipj ‾ \overline{p_ip_j}pipjwith pkpl ‾ \overline{p_kp_l}pkplintersect. Since pk p_kpkand pl p_lplMust all fall on the disc C ij C_{ij}Cij, so it must also fall in the triangle tij t_{ij}tijoutside. This means that, in the form of tij t_{ij}tijAmong the three sides of C ij C_{ij}CijOne of the two sides associated with the center of the circle must be pkpl ‾ \overline{p_kp_l}pkplintersect. Similarly, when forming tkl t_{kl}tklAmong the three sides of C kl C_{kl}CklThere must be one of the two sides associated with the center of the circle with pipj ‾ \overline{p_ip_j}pipjintersect. Therefore, if we consider tij t_{ij}tijOn the boundary of C ij C_{ij}CijThe two sides associated with the center of the circle, and in tkl t_{kl}tklOn the boundary of C kl C_{kl}CklThe two sides associated with the center of the circle, then one of the first two sides must intersect with one of the last two sides. However, this is impossible since the two edges must each fall entirely within two different Voronoi cells.

Each segment boundary of the same surface in the Delaunay graph corresponds to each Voronoi edge associated with the same Voronoi vertex in the Voronoi graph.

Plane point set PPThe Delaunay graph of P is a linear embedding of the dual graph of the Voronoi graph. As we have noted before,V or ( P ) Vor(P)Each vertex in V o r ( P ) corresponds to a certain surface in the Delaunay graph. The edges that enclose each face in the Delaunay graph correspond to the Voronoi edges associated with a Voronoi vertex in the Voronoi graph (as shown). Specifically, in Vor§, if the base pointsp 1 , p 2 , p 3 , ⋯ , pk p_1, p_2, p_3, \cdots, p_kp1,p2,p3,,pkCorresponding to each (a total of kkk ) Voronoi units have a common vertexvvv , then inDG ( P ) DG(P)D G ( P ) , withvvThe face ffcorresponding to vThe vertices of f must bep 1 , p 2 , p 3 , ⋯ , pk p_1, p_2, p_3, \cdots, p_kp1,p2,p3,,pk. In this case, the points p 1 , p 2 , p 3 , ⋯ , pk p_1, p_2, p_3, \cdots, p_kp1,p2,p3,,pkmust be scattered in a place starting with vvv as the center of the circle. It can thus be seen thatfff is not only akkk - polygon, and it must also be convex.

If PPThe points in P are randomly distributed, and the possibility of any four points being exactly on the same circle will be very small. Any set, as long as there are no four points in the same circle, we say it is in ageneral position. IfPPIf P is indeed in a general position, then in its corresponding Voronoi diagram, the degree of each vertex must be 3. Then,DG ( P ) DG(P)Every surface in D G ( P ) must be a triangle. The reason why we often refer toDG ( P ) DG(P)D G ( P ) is called a Delaunay triangulation (Delaunay triangulation) for this reason. However, we should be more cautious here, letDG ( P ) DG(P)D G ( P ) is calledPPDelaunay graph of P. As for the Delaunay triangulation, we have another definition: a triangulation obtained by introducing joint edges based on the Delaunay graph. SinceDG ( P ) DG(P)Each face in DG ( P ) is a convex set, so a triangulation can be easily obtained . It should be noted thatPPThe Delaunay triangulation of P is uniquely determined if and only ifDG ( P ) DG(P)D G ( P ) itself is already a triangulation, in other words,PPP is inthe general position.

With the help of the concept of Delaunay diagrams, the following theorems about Voronoi diagrams can be obtained:

Theorem 2. Let PPP is any plane point set, then inPPIn the Delaunay diagram of P :

  • 三界点pi , pj , pk ∈ P p_i, p_j, p_k \in Ppi,pj,pkP is also a vertex of a certain surface, if and only ifpi , pj , pk p_i, p_j, p_kpi,pj,pkThe interior of the circumscribed circle does not contain PPany point in P ;
  • 书了点pi , pj ∈ P p_i, p_j \in Ppi,pjP is also associated with an edge if and only if there is a closed diskCCC , exceptpi p_ipiand pj p_jpjfalls outside its boundary, the disk does not contain PPAny other point in P.

Proof 2. The proof is abbreviated, see reference [@cgaa_book] Theorem 7.4 for details.

At the same time, Theorem re-expressed as:

Theorem 3. Let PPP is any set of points on the plane, andTTT isPPAny triangulation of P. ThenTTT is forPPThe Delaunay triangulation of P if and only if in TTThe interior of the circumcircle of each triangle in T does not contain PPany point in P.

Proof 3 Proof is the same as Theorem 1 .

legal triangulation

for PPAny triangulationTT of PT , let's examine one of the edgese = pipj ‾ e = \overline{p_ip_j}e=pipj. if side eee does not belong toTTThe borderless boundary in T , it must be connected with two triangles △ pipjpk \triangle p_ip_jp_kpipjpk △ p i p j p l \triangle p_ip_jp_l pipjplAssociated. If these two triangles are combined to form a convex quadrilateral, then just pipj ‾ \overline{p_ip_j}pipjfrom TTDeleted from T and replaced by pkpl p_kp_lpkpl, we can get another triangulation T ′ T'T' . As shown in the figure below, this operation is calledan edge flip.

edge flip operation

vs TTT andT'T'T , there are six differences:A ( T ) A(T)The six angles α 1 , α 2 , ⋯ , α 6in A ( T ) {\alpha_1, \alpha_2, \cdots, \alpha_6}a1,a2,,a6, at A ( T ′ ) A(T')A(T )were replaced byα 1 ′ , α 2 ′ , ⋯ , α 6 ′ {\alpha'_1, \alpha'_2, \cdots, \alpha'_6}a1,a2,,a6. if

KaTeX parse error: No such environment: equation* at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲*̲}̲ \min_{1 \leq i…

Then e = pipj ‾ e = \overline{p_ip_j}e=pipjIt is called an illegal edge . In other words, as long as we can increase the local minimum angle (that is, the corresponding six angles) after performing the edge flip operation on an edge, it must be an illegal edge. From the definition of illegal edges, the following observations can be drawn immediately.

Corollary 1. Suppose eee is the triangulationTTAn illegal edge in T. inTTT toeee After the edge flipping operation, set the new triangulation asT ′ T’T , then there must beA ( T ′ ) > A ( T ) A(T') > A(T)A(T)>A(T)

A triangulation that does not contain any illegal edges is called a legal triangulation . From the above observations, it can be seen that the triangulation with the optimal angle must also be a legal triangulation.

In order to get a good triangulation, that is to make its corresponding angle vector as large as possible. From legal triangulations, a consideration of the angle vectors of Delaunay triangulations is introduced.

Theorem 4. Let PPP is any set of points on the plane. ThenTTT is forPPA legal triangulation of P if and only if TTT is forPPDelaunay triangulation of P.

Proof 4. For the proof, please refer to Theorem 9.8 in reference [@cgaa_book; @dengjunhui] for details.

Since the optimal triangulation of any angle must be legal, it can be deduced from: PPThe optimal triangulation of any angle of P must be a Delaunay triangulation of P. Legal triangulations only exist when P is in general position. It is the only angle-optimal triangulation, that is, the only Delaunay triangulation that fits the Delaunay graph exactly.

It can be shown that in PPOf all the triangulations of P , the Delaunay triangulation maximizes the smallest angle. PPThe optimal triangulation of any angle of P must be PPA Delaunay triangulation of P.

Construct Delaunay triangulation

P P The Delaunay triangulation of P is indeed a suitable triangulation. The reason for this is that a Delaunay triangulation maximizes the smallest angle in it. In this section, we mainly introduce the direct computation of Delaunay triangulation using random incremental algorithm.

First use a large enough triangle to divide the entire point set PPSurrounded by P , two auxiliary points need to be introduced p − 1 p_{-1}p1and p − 2 p_{-2}p2, which are related to PPThe triangle formed by the union of the highest points in P will contain all the points. Therefore, we need to construct{ p − 1 , p − 2 } ∪ P \{p_{-1}, p_{-2}\} \cup P{ p1,p2}Triangulation of P , in the resulting triangulation only need to deletep − 1 p_{-1}p1and p − 2 p_{-2}p2and the sides associated with them are PPTriangulation of P. For this, we choosep − 1 p_{-1}p1and p − 2 p_{-2}p2Must be far enough apart so as not to PPAny triangles in the Delaunay triangulation of P are affected. In particular, it must be ensured that they cannot fall onPPThe circumscribed circle of any three points in P.

A point surrounded by a large enough triangle

Points are introduced one by one in random order, maintaining and updating a Delaunay triangulation corresponding to the current set of points throughout the process. Consider introducing the point pr p_rprsituation, as shown in Fig. First, in the current triangulation, determine pr p_rprWhich triangle does it fall in. Then, the pr p_rprConnect with the three vertices of the triangle to generate three sides. If pr p_rprhappens to fall on an edge of the triangulation eeOn e , you need to find the same aseeThe two triangles associated with e , then pr p_rprConnect with the two opposite vertices respectively to generate two edges.

Two possible situations when introducing a point: falling inside a triangle (left), falling exactly on an edge (right)

In this way, a triangulation is obtained, but it is not necessarily a Delaunay
triangulation. Because, at the introduction point pr p_rprAfterwards, some of the original edges may no longer be valid. In order to eliminate these illegalities, it is necessary to call the sub-function once for each possible illegal edge LEGALIZEEDGE. This subroutine converts all illegal edges to legal edges through an edge flip operation. For the convenience of analysis, it may be assumed that the set
PPP by( n + 1 ) (n+1)(n+1 ) consists of points.

Algorithm 1

let p 0 p_0p0for PPThe point with the highest lexicographical order in P is in R 2 \mathcal{R}^2RSelect pointp − 1 p_{-1} in 2p1and p − 2 p_{-2}p2, so that PPP is completely contained in the triangle△ p 0 p − 1 p − 2 \triangle p_0p_{-1}p_{-2}p0p1p2General TTT is initialized as a single triangle△ p 0 p − 1 p − 2 \triangle p_0p_{-1}p_{-2}p0p1p2Randomly pick P / p 0 P/{p_0}P/p0A sequence of points in : p 1 , p 2 , ⋯ , pn p_1, p_2, \cdots, p_np1,p2,,pnPoint p − 1 , p − 2 p_{-1}, p_{-2}p1,p2and all edges associated with it from TTremoved from T.

Algorithm 2

From Theorem 2, a triangulation is a Delaunay triangulation if and only if all the edges in it are valid. According to the principle LEGALTRIANGULATIONof , the illegal edge is continuously flipped until it returns to a legal triangulation. Since any original legal edge pipj ‾ \overline{p_ip_j}pipj, may become an illegal edge only when the triangle associated with it changes. Therefore, we only need to check the sides of those newly generated triangles. This is done LEGALIZEEDGEby that checks the sides involved and flips them if necessary. After each edge is flipped, it may in turn make some other edges illegal. For all possible new illegal edges, LEGALIZEEDGEcall itself recursively, checking them one by one.

Only when the triangle associated with it changes, the original legal side may become an illegal side

Among them, line 2 checks the legality of a side. It is clear from the code LEGALIZEEDGEof that since pr p_rprEvery new edge created by the insertion of , must be related to pr p_rprAssociated. This can also be seen from this: after some original triangles are destroyed, the new triangles are shown in gray. If any edge (from legal) becomes illegal, one of the (at most two) triangles associated with it must have changed. All edges that may become illegal are bound to be checked by the algorithm. That is, the algorithm is correct. It should be pointed out that this algorithm will not fall into an infinite loop. This is because the angle vector of the triangulation always grows monotonically with each flip.

Each new edge must be associated with

Experiment and Results

In this section, the Delaunay triangulation algorithm is mainly implemented by using the Python program, and the effectiveness of the algorithm is verified through simulation experiments.

lab environment

Experiments were performed using Python 3.7 on a laptop configured with an Intel® Core™ 7-10510U CPU @ 1.80GHz.2.30 GHz and 16 GB memory.

results and analysis

For the convenience of display, the Delaunay triangulation construction of 24 points and the visualization results of the Voronoi diagram generation steps are shown. For a complete video of the generation steps, see Support Materials .

Delaunay triangulation visualization

First, randomly generate a set PP including several pointsP , adopt successive iterations, and finally generatePPA triangulation of P. For the convenience of visualization, set the point setPPThe size of P is 24 and the resulting triangulation is as shown.

Delaunay triangulation

An example of triangulation of facial feature points is given, which is the original image of Trump's face and Delaunay triangulation of 68 feature points of his face.

Voronoi diagram visualization

On the basis of Delaunay triangulation, construct Voronoi
diagram. Make the circumcircle of each triangular face (such as ), and the centers of these circumscribed circles are the endpoints of each side of the Voronoi diagram (such as ). Connect the centers of two circumscribed circles with the same Delaunay side as the chord to form the sides of the Voronoi diagram, and color the sub-regions of the Voronoi diagram as shown in and respectively.

Algorithm running time

In order to count the impact of data scale on the running time of the algorithm, 10, 100, and 1000 base points were generated for experiments. The experimental results of the running time of the Delaunay triangulation algorithm are shown in the table below. It can be seen that as the data size increases, the algorithm running time increases logarithmically. It can be proved that the time complexity of O ( n log ⁡ n ) \mathcal{O}(n \log n)O ( nlogn)

   \#     基点规模   Delaunay 三角面数量   运行时间 (s)

----- ---------- --------------------- --------------

   $1$          10                    13         0.0049
   $2$         100                   186         0.9200
   $3$        1000                  1977         6.1894
   $4$       10000                 19964       560.1147

  : Delaunay 三角剖分算法运行时间

In addition, the number of triangular meshes generated by Delaney triangulation of different scale base points is also reported. It can be proved that the expected value of the total number of triangles generated by Algorithm 1 does not exceed 9 n + 1 9n + 19 n+1

summary

This article outlines the principle of Delaunay triangulation, and elaborates the algorithm of constructing Delaunay triangulation. Experimental verification is carried out on simulated data and real data respectively, and the experimental results show the effectiveness of the algorithm.

References

[1] M. de Berg, O. Cheong, M. van Kreveld, and M. Overmars, Computational
Geometry: Algorithms and Applications, 2008.
[2] Deng Junhui, Computational Geometry Algorithms and Applications (Chinese Edition), 2011.

[3] https://github.com/jmespadero/pyDelaunay2D

[4] https://blog.csdn.net/weixin_42512684/article/details/106650061

appendix

main program

# -*- coding: ascii -*-
"""
Simple structured Delaunay triangulation in 2D with Bowyer-Watson algorithm.

Written by Jose M. Espadero ( http://github.com/jmespadero/pyDelaunay2D )
Based on code from Ayron Catteau. Published at http://github.com/ayron/delaunay

Just pretend to be simple and didactic. The only requisite is numpy.
Robust checks disabled by default. May not work in degenerate set of points.
"""

import numpy as np
from math import sqrt


class Delaunay2D:
    """
    Class to compute a Delaunay triangulation in 2D
    ref: http://en.wikipedia.org/wiki/Bowyer-Watson_algorithm
    ref: http://www.geom.uiuc.edu/~samuelp/del_project.html
    """

    def __init__(self, center=(0, 0), radius=9999):
        """ Init and create a new frame to contain the triangulation
        center -- Optional position for the center of the frame. Default (0,0)
        radius -- Optional distance from corners to the center.
        """
        center = np.asarray(center)
        # Create coordinates for the corners of the frame
        self.coords = [center+radius*np.array((-1, -1)),
                       center+radius*np.array((+1, -1)),
                       center+radius*np.array((+1, +1)),
                       center+radius*np.array((-1, +1))]

        # Create two dicts to store triangle neighbours and circumcircles.
        self.triangles = {
    
    }
        self.circles = {
    
    }

        # Create two CCW triangles for the frame
        T1 = (0, 1, 3)
        T2 = (2, 3, 1)
        self.triangles[T1] = [T2, None, None]
        self.triangles[T2] = [T1, None, None]

        # Compute circumcenters and circumradius for each triangle
        for t in self.triangles:
            self.circles[t] = self.circumcenter(t)

    def circumcenter(self, tri):
        """Compute circumcenter and circumradius of a triangle in 2D.
        Uses an extension of the method described here:
        http://www.ics.uci.edu/~eppstein/junkyard/circumcenter.html
        """
        pts = np.asarray([self.coords[v] for v in tri])
        pts2 = np.dot(pts, pts.T)
        A = np.bmat([[2 * pts2, [[1],
                                 [1],
                                 [1]]],
                      [[[1, 1, 1, 0]]]])

        b = np.hstack((np.sum(pts * pts, axis=1), [1]))
        x = np.linalg.solve(A, b)
        bary_coords = x[:-1]
        center = np.dot(bary_coords, pts)

        # radius = np.linalg.norm(pts[0] - center) # euclidean distance
        radius = np.sum(np.square(pts[0] - center))  # squared distance
        return (center, radius)

    def inCircleFast(self, tri, p):
        """Check if point p is inside of precomputed circumcircle of tri.
        """
        center, radius = self.circles[tri]
        return np.sum(np.square(center - p)) <= radius

    def inCircleRobust(self, tri, p):
        """Check if point p is inside of circumcircle around the triangle tri.
        This is a robust predicate, slower than compare distance to centers
        ref: http://www.cs.cmu.edu/~quake/robust.html
        """
        m1 = np.asarray([self.coords[v] - p for v in tri])
        m2 = np.sum(np.square(m1), axis=1).reshape((3, 1))
        m = np.hstack((m1, m2))    # The 3x3 matrix to check
        return np.linalg.det(m) <= 0

    def addPoint(self, p):
        """Add a point to the current DT, and refine it using Bowyer-Watson.
        """
        p = np.asarray(p)
        idx = len(self.coords)
        # print("coords[", idx,"] ->",p)
        self.coords.append(p)

        # Search the triangle(s) whose circumcircle contains p
        bad_triangles = []
        for T in self.triangles:
            # Choose one method: inCircleRobust(T, p) or inCircleFast(T, p)
            if self.inCircleFast(T, p):
                bad_triangles.append(T)

        # Find the CCW boundary (star shape) of the bad triangles,
        # expressed as a list of edges (point pairs) and the opposite
        # triangle to each edge.
        boundary = []
        # Choose a "random" triangle and edge
        T = bad_triangles[0]
        edge = 0
        # get the opposite triangle of this edge
        while True:
            # Check if edge of triangle T is on the boundary...
            # if opposite triangle of this edge is external to the list
            tri_op = self.triangles[T][edge]
            if tri_op not in bad_triangles:
                # Insert edge and external triangle into boundary list
                boundary.append((T[(edge+1) % 3], T[(edge-1) % 3], tri_op))

                # Move to next CCW edge in this triangle
                edge = (edge + 1) % 3

                # Check if boundary is a closed loop
                if boundary[0][0] == boundary[-1][1]:
                    break
            else:
                # Move to next CCW edge in opposite triangle
                edge = (self.triangles[tri_op].index(T) + 1) % 3
                T = tri_op

        # Remove triangles too near of point p of our solution
        for T in bad_triangles:
            del self.triangles[T]
            del self.circles[T]

        # Retriangle the hole left by bad_triangles
        new_triangles = []
        for (e0, e1, tri_op) in boundary:
            # Create a new triangle using point p and edge extremes
            T = (idx, e0, e1)

            # Store circumcenter and circumradius of the triangle
            self.circles[T] = self.circumcenter(T)

            # Set opposite triangle of the edge as neighbour of T
            self.triangles[T] = [tri_op, None, None]

            # Try to set T as neighbour of the opposite triangle
            if tri_op:
                # search the neighbour of tri_op that use edge (e1, e0)
                for i, neigh in enumerate(self.triangles[tri_op]):
                    if neigh:
                        if e1 in neigh and e0 in neigh:
                            # change link to use our new triangle
                            self.triangles[tri_op][i] = T

            # Add triangle to a temporal list
            new_triangles.append(T)

        # Link the new triangles each another
        N = len(new_triangles)
        for i, T in enumerate(new_triangles):
            self.triangles[T][1] = new_triangles[(i+1) % N]   # next
            self.triangles[T][2] = new_triangles[(i-1) % N]   # previous

    def exportTriangles(self):
        """Export the current list of Delaunay triangles
        """
        # Filter out triangles with any vertex in the extended BBox
        return [(a-4, b-4, c-4)
                for (a, b, c) in self.triangles if a > 3 and b > 3 and c > 3]

    def exportCircles(self):
        """Export the circumcircles as a list of (center, radius)
        """
        # Remember to compute circumcircles if not done before
        # for t in self.triangles:
        #     self.circles[t] = self.circumcenter(t)

        # Filter out triangles with any vertex in the extended BBox
        # Do sqrt of radius before of return
        return [(self.circles[(a, b, c)][0], sqrt(self.circles[(a, b, c)][1]))
                for (a, b, c) in self.triangles if a > 3 and b > 3 and c > 3]

    def exportDT(self):
        """Export the current set of Delaunay coordinates and triangles.
        """
        # Filter out coordinates in the extended BBox
        coord = self.coords[4:]

        # Filter out triangles with any vertex in the extended BBox
        tris = [(a-4, b-4, c-4)
                for (a, b, c) in self.triangles if a > 3 and b > 3 and c > 3]
        return coord, tris

    def exportExtendedDT(self):
        """Export the Extended Delaunay Triangulation (with the frame vertex).
        """
        return self.coords, list(self.triangles)

    def exportVoronoiRegions(self):
        """Export coordinates and regions of Voronoi diagram as indexed data.
        """
        # Remember to compute circumcircles if not done before
        # for t in self.triangles:
        #     self.circles[t] = self.circumcenter(t)
        useVertex = {
    
    i: [] for i in range(len(self.coords))}
        vor_coors = []
        index = {
    
    }
        # Build a list of coordinates and one index per triangle/region
        for tidx, (a, b, c) in enumerate(sorted(self.triangles)):
            vor_coors.append(self.circles[(a, b, c)][0])
            # Insert triangle, rotating it so the key is the "last" vertex
            useVertex[a] += [(b, c, a)]
            useVertex[b] += [(c, a, b)]
            useVertex[c] += [(a, b, c)]
            # Set tidx as the index to use with this triangle
            index[(a, b, c)] = tidx
            index[(c, a, b)] = tidx
            index[(b, c, a)] = tidx

        # init regions per coordinate dictionary
        regions = {
    
    }
        # Sort each region in a coherent order, and substitude each triangle
        # by its index
        for i in range(4, len(self.coords)):
            v = useVertex[i][0][0]  # Get a vertex of a triangle
            r = []
            for _ in range(len(useVertex[i])):
                # Search the triangle beginning with vertex v
                t = [t for t in useVertex[i] if t[0] == v][0]
                r.append(index[t])  # Add the index of this triangle to region
                v = t[1]            # Choose the next vertex to search
            regions[i-4] = r        # Store region.

        return vor_coors, regions

Face Feature Point Extraction

import dlib
import cv2

predictor_path  = "./shape_predictor_68_face_landmarks.dat"
png_path = "./trump.jpeg"

txt_path = "./points.txt"
f = open(txt_path,'w+')


detector = dlib.get_frontal_face_detector()
predicator = dlib.shape_predictor(predictor_path)
win = dlib.image_window()
img1 = cv2.imread(png_path)


dets = detector(img1, 1)
print("Number of faces detected : {}".format(len(dets)))
for k,d in enumerate(dets):
    print("Detection {}  left:{}  Top: {} Right {}  Bottom {}".format(
        k,d.left(),d.top(),d.right(),d.bottom()
    ))
    lanmarks = [[p.x,p.y] for p in predicator(img1,d).parts()]
    for idx,point in enumerate(lanmarks):
        f.write(str(point[0]))
        f.write("\t")
        f.write(str(point[1]))
        f.write('\n')

visualization program

#!/usr/bin/env python3
import time
import numpy as np
from delaunay2D import Delaunay2D


def vis(dt):

    """
    Demostration of how to plot the data.
    """
    import matplotlib.pyplot as plt
    import matplotlib.tri
    import matplotlib.collections

    plt.rcParams['font.family'] = 'serif'
    plt.rcParams['font.size'] = 18
    plt.rcParams['text.usetex'] = True

    from matplotlib.animation import FFMpegWriter

    # Create a plot with matplotlib.pyplot
    # fig, ax = plt.subplots()
    # ax.margins(0.1)
    # ax.set_aspect('equal')
    # plt.axis([-1, radius+1, -1, radius+1])

    # made the video of result
    metadata = dict(title='Hand tremor cleaning result', artist='Matplotlib', comment='Paper showing!')
    writer = FFMpegWriter(fps=15, metadata=metadata)
    fig = plt.figure(figsize=(10, 9))
    fig.tight_layout()

    with writer.saving(fig=fig,
                       outfile="output-delaunay2D_%d.mp4" % numSeeds,
                       dpi=300):
        # plt.axis('off')
        ax = plt.subplot(111)
        # Plot a step-by-step triangulation
        # Starts from a new Delaunay2D frame
        dt2 = Delaunay2D(center, 50 * radius)
        for i, s in enumerate(seeds):
            print("Inserting seed", i, s)
            dt2.addPoint(s)
            if i > 1:
                ax.margins(0.1)
                ax.set_aspect('equal')
                ax.set_title("%d" % i)
                ax.axis([-1, radius + 1, -1, radius + 1])

                for i, v in enumerate(seeds):
                    plt.annotate(i, xy=v)  # Plot all seeds
                for t in dt2.exportTriangles():
                    polygon = [seeds[i] for i in t]  # Build polygon for each region
                    plt.fill(*zip(*polygon), fill=False, color="b")  # Plot filled polygon

            # ax.axis("off")
            writer.grab_frame()

        plt.clf()
        ax = plt.subplot(111)
        ax.margins(0.1)
        ax.set_aspect('equal')
        ax.axis([-1, radius + 1, -1, radius + 1])
        # Plot our Delaunay triangulation (plot in blue)
        cx, cy = zip(*seeds)
        dt_tris = dt.exportTriangles()
        ax.triplot(matplotlib.tri.Triangulation(cx, cy, dt_tris), 'bo--')

        print("Plot annotated Delaunay vertex (seeds)")
        # Plot annotated Delaunay vertex (seeds)
        for i, v in enumerate(seeds):
            ax.set_title("Plot annotated Delaunay vertex (seeds)")

            plt.annotate(i, xy=v)

            writer.grab_frame()

        # Dump plot to file
        plt.savefig('output-delaunay2D_Delaunay_vertex-%d.pdf' % numSeeds)

        # DEBUG: Use matplotlib to create a Delaunay triangulation (plot in green)
        # DEBUG: It should be equal to our result in dt_tris (plot in blue)
        # DEBUG: If boundary is diferent, try to increase the value of your margin
        # ax.triplot(matplotlib.tri.Triangulation(*zip(*seeds)), 'g--')

        # DEBUG: plot the extended triangulation (plot in red)
        # edt_coords, edt_tris = dt.exportExtendedDT()
        # edt_x, edt_y = zip(*edt_coords)
        # ax.triplot(matplotlib.tri.Triangulation(edt_x, edt_y, edt_tris), 'ro-.')

        print("Plot the circumcircles (circles in black)")
        # Plot the circumcircles (circles in black)
        for c, r in dt.exportCircles():
            ax.set_title("Plot the circumcircles (circles in black)")

            ax.add_artist(plt.Circle(c, r, color='k', fill=False, ls='dotted'))

            writer.grab_frame()

        # Dump plot to file
        plt.savefig('output-delaunay2D_circumcircles-%d.pdf' % numSeeds)

        # Build Voronoi diagram as a list of coordinates and regions
        vc, vr = dt.exportVoronoiRegions()

        print("Plot annotated voronoi vertex")
        # Plot annotated voronoi vertex
        plt.scatter([v[0] for v in vc], [v[1] for v in vc], marker='.')
        for i, v in enumerate(vc):
            ax.set_title("Plot annotated voronoi vertex")

            plt.annotate(i, xy=v)

            writer.grab_frame()

        # Dump plot to file
        plt.savefig('output-delaunay2D_voronoi_vertex-%d.pdf' % numSeeds)

        print("Plot annotated voronoi regions as filled polygons")
        # Plot annotated voronoi regions as filled polygons
        for r in vr:
            ax.set_title("Plot annotated voronoi regions as filled polygons")

            polygon = [vc[i] for i in vr[r]]  # Build polygon for each region
            plt.fill(*zip(*polygon), alpha=0.2)  # Plot filled polygon
            plt.annotate("$r_{%d}$" % r, xy=np.average(polygon, axis=0))

            writer.grab_frame()
        # Dump plot to file
        plt.savefig('output-delaunay2D_voronoi-%d.pdf' % numSeeds)

        print("Plot voronoi diagram edges (in red)")
        # Plot voronoi diagram edges (in red)
        for r in vr:
            ax.set_title("Plot voronoi diagram edges (in red)")

            polygon = [vc[i] for i in vr[r]]  # Build polygon for each region
            plt.plot(*zip(*polygon), color="red")  # Plot polygon edges in red

            writer.grab_frame()

        # Dump plot to file
        plt.savefig('output-delaunay2D_voronoi_edge-%d.pdf' % numSeeds)

        plt.show()
        plt.close()


if __name__ == '__main__':

    ###########################################################
    # Generate 'numSeeds' random seeds in a square of size 'radius'
    numSeeds = 10
    radius = 100
    seeds = radius * np.random.random((numSeeds, 2))
    print("seeds:\n", seeds)
    print("BBox Min:", np.amin(seeds, axis=0),
          "Bbox Max: ", np.amax(seeds, axis=0))

    """
    Compute our Delaunay triangulation of seeds.
    """
    # It is recommended to build a frame taylored for our data
    time_start = time.time()
    center = np.mean(seeds, axis=0)
    dt = Delaunay2D(center, 50 * radius)
    
    # Insert all seeds one by one
    for s in seeds:
        dt.addPoint(s)
    time_end = time.time()

    # Dump number of DT triangles
    print(len(dt.exportTriangles()), "Delaunay triangles | Time: ",
          time_end - time_start)

    # vis(dt)

Guess you like

Origin blog.csdn.net/qq_38904659/article/details/117382180