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 Pp∈The sub-region corresponding to P is represented by ppp is composed of all points closest to the base point.
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
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.
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. ◻
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,pk∈P 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,pj∈P 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_k△pipjpk 和 △ 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.
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}p−1and p − 2 p_{-2}p−2, 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{ p−1,p−2}∪Triangulation of P , in the resulting triangulation only need to deletep − 1 p_{-1}p−1and p − 2 p_{-2}p−2and the sides associated with them are PPTriangulation of P. For this, we choosep − 1 p_{-1}p−1and p − 2 p_{-2}p−2Must 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.
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.
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.
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 2p−1and p − 2 p_{-2}p−2, so that PPP is completely contained in the triangle△ p 0 p − 1 p − 2 \triangle p_0p_{-1}p_{-2}△p0p−1p−2General TTT is initialized as a single triangle△ p 0 p − 1 p − 2 \triangle p_0p_{-1}p_{-2}△p0p−1p−2Randomly 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}p−1,p−2and all edges associated with it from TTremoved from T.
From Theorem 2, a triangulation is a Delaunay triangulation if and only if all the edges in it are valid. According to the principle LEGALTRIANGULATION
of , 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 LEGALIZEEDGE
by 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, LEGALIZEEDGE
call itself recursively, checking them one by one.
Among them, line 2 checks the legality of a side. It is clear from the code LEGALIZEEDGE
of 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.
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.
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)