https://blog.csdn.net/piaoxuezhong/article/details/68065170
一.原理部分
點集的三角剖分(Triangulation),對數值分析(如有限元分析)以及圖形學來說,都是極為重要的預處理技術。尤其是Delaunay三角剖分,關於點集的很多種幾何圖都和Delaunay三角剖分相關,如Voronoi圖,EMST樹等。Delaunay三角剖分有最大化最小角“最接近於規則化的“的三角網和唯一性(任意四點不能共圓)兩個特點。Delaunay三角剖分是變現三維形狀的基礎。可以通過這個物體的投影來建立二維視覺圖,並用二維Delaunay三角剖分來分析識別該物體,或者將它與實物相比較。Delaunay剖分是連接計算機視覺與計算機圖形學的橋梁。以下內容參考:http://www.cnblogs.com/RenLiQQ/archive/2008/02/06/1065399.html
1.三角剖分與Delaunay剖分的定義
如何把一個離散幾何剖分成不均勻的三角形網格,這就是離散點的三角剖分問題,散點集的三角剖分,對數值分析以及圖形學來說,都是極為重要的一項處理技術。該問題圖示如下:

1.1 三角剖分定義
【定義】三角剖分:假設V是二維實數域上的有限點集,邊e是由點集中的點作為端點構成的封閉線段,E為e的集合。那么該點集V的一個三角剖分T=(V,E)是一個平面圖G,該平面圖滿足條件:
1、除了端點,平面圖中的邊不包含點集中的任何點。
2、沒有相交邊。//邊和邊沒有交叉點
3、平面圖中所有的面都是三角面,且所有三角面的合集是散點集V的凸包。
//凸包的概念:用不嚴謹的話來講,給定二維平面上的點集,凸包就是將最外層的點連接起來構成的凸多邊型,它能包含點集中所有的點。
1.2 Delaunay三角剖分的定義
在實際中運用的最多的三角剖分是Delaunay三角剖分,它是一種特殊的三角剖分。先從Delaunay邊說起:
【定義】Delaunay邊:假設E中的一條邊e(兩個端點為a,b),e若滿足下列條件,則稱之為Delaunay邊:存在一個圓經過a,b亮點,圓內(注意是園內,圓上最多三點共圓)不含點集V中任何其他的點,這一特性又稱空圓特性。
【定義】Delaunay三角剖分:如果點集V的一個三角剖分T只包含Delaunay邊,那么該三角剖分稱為Delaunay三角剖分。
1.3 Delaunay三角剖分的准則
要滿足Delaunay三角剖分的定義,必須符合兩個重要的准則:
1、空圓特性:Delaunay三角網是唯一的(任意四點不能共圓),在Delaunay三角形網中任一三角形的外接圓范圍內不會有其它點存在。如下圖所示:

2、最大化最小角特性:在散點集可能形成的三角剖分中,Delaunay三角剖分所形成的三角形的最小角最大。從這個意義上講,Delaunay三角網是“最接近於規則化的”三角網。具體的說是在兩個相鄰的三角形構成凸四邊形的對角線,在相互交換后,兩個內角的最小角不再增大。如下圖所示:
1.4 Delaunay三角剖分的特性
以下是Delaunay剖分所具備的優異特性:
1、最接近:以最接近的 三點形成三角形,且各線段(三角行的邊)皆不相交。
2、唯一性:不論從區域何處開始構建,最終都將得到一致的結果。
3、最優性:任意兩個相鄰三角形構成的凸四邊形的對角線如果可以互換,那么兩個三角形六個內角中最小角度不會變化。
4、最規則:如果將三角網中的每個三角形的最小角進行升序排列,則Delaunay三角網的排列得到的數值最大。
5、區域性:新增、刪除、移動某一個頂點只會影響鄰近的三角形。
6、具有凸邊形的外殼:三角網最外層的邊界形成一個凸多邊形的外殼。
1.5局部最優化處理
理論上為了構造Delaunay三角網,Lawson提出的局部優化過程LOP(Local Optimization Procedure),一般三角網經過LOP處理,即可確保為Delaunay三角網,其基本做法如下所示:
1、將兩個具有共同邊的三角形合成一個多邊形。
2、以最大空圓准則作檢查,看其第四個頂點是否在三角形的外接圓內。
3、如果在,修正對角線即將對角線對調,即完成局部優化過程的處理。
LOP處理過程如下圖所示:

1.6三維空間情形
Delaunay三角網格的性質可以推廣到高維。一個三維點集的三角剖分是由四面體構成。下圖顯示了一個由兩個四面體構成的簡單三維Delaunay三角剖分。一個四面體的外接球來說明空接球准則。

2.Delaunay剖分的算法
Delaunay剖分是一種三角剖分的標准,實現它有多種算法。
2.1.Lawson算法
逐點插入的Lawson算法是Lawson在1977年提出的,該算法思路簡單,易於編程實現。基本原理為:首先建立一個大的三角形或多邊形,把所有數據點包圍起來,向其中插入一點,該點與包含它的三角形三個頂點相連,形成三個新的三角形,然后逐個對它們進行空外接圓檢測,同時用Lawson設計的局部優化過程LOP進行優化,即通過交換對角線的方法來保證所形成的三角網為Delaunay三角網。
上述基於散點的構網算法理論嚴密、唯一性好,網格滿足空圓特性,較為理想。由其逐點插入的構網過程可知,遇到非Delaunay邊時,通過刪除調整,可以構造形成新的Delaunay邊。在完成構網后,增加新點時,無需對所有的點進行重新構網,只需對新點的影響三角形范圍進行局部聯網,且局部聯網的方法簡單易行。同樣,點的刪除、移動也可快速動態地進行。但在實際應用當中,這種構網算法當點集較大時構網速度也較慢,如果點集范圍是非凸區域或者存在內環,則會產生非法三角形。
2.2.Bowyer-Watson算法 (推薦)
目前采用逐點插入方式生成的Delaunay三角網的算法主要基於Bowyer-Watson算法,Bowyer-Watson算法的主要步驟如下:
1、構造一個超級三角形,包含所有散點,放入三角形鏈表。
2、將點集中的散點依次插入,在三角形鏈表中找出其外接圓包含插入點的三角形(稱為該點的影響三角形),刪除影響三角形的公共邊,將插入點同影響三角形的全部頂點連接起來,從而完成一個點在Delaunay三角形鏈表中的插入。
3、根據優化准則對局部新形成的三角形進行優化。將形成的三角形放入Delaunay三角形鏈表。
4、循環執行上述第2步,直到所有散點插入完畢。
這一算法的關鍵的第2步圖示如下:

2.3偽代碼表述
input: 頂點列表(vertices) //vertices為外部生成的隨機或亂序頂點列表 output:已確定的三角形列表(triangles) 初始化頂點列表 創建索引列表(indices = new Array(vertices.length)) //indices數組中的值為0,1,2,3,......,vertices.length-1 基於vertices中的頂點x坐標對indices進行sort //sort后的indices值順序為頂點坐標x從小到大排序(也可對y坐標,本例中針對x坐標) 確定超級三角形 將超級三角形保存至未確定三角形列表(temp triangles) 將超級三角形push到triangles列表 遍歷基於indices順序的vertices中每一個點 //基於indices后,則頂點則是由x從小到大出現 初始化邊緩存數組(edge buffer) 遍歷temp triangles中的每一個三角形 計算該三角形的圓心和半徑 如果該點在外接圓的右側 則該三角形為Delaunay三角形,保存到triangles 並在temp里去除掉 跳過 如果該點在外接圓外(即也不是外接圓右側) 則該三角形為不確定 //后面會在問題中討論 跳過 如果該點在外接圓內 則該三角形不為Delaunay三角形 將三邊保存至edge buffer 在temp中去除掉該三角形 對edge buffer進行去重 將edge buffer中的邊與當前的點進行組合成若干三角形並保存至temp triangles中 將triangles與temp triangles進行合並 除去與超級三角形有關的三角形 end
以下摘自:http://www.cnblogs.com/zhiyishou/p/4430017.html
用圖來解釋偽代碼過程,先用三點來做實例:

如圖,隨機的三個點
根據離散點的最大分布來求得隨機一個超級三角形(超級三角形意味着該三角形包含了點集中所有的點),方法是根據相似三角形定理求得與矩形一半的小矩形的對角三角形,擴大一倍后則擴大后的直角三角形斜邊經過點(Xmax,Ymin),但是為了將所有的點包含在超級三角形內,在右下角對該三角形的頂點進行了橫和高的擴展,並要保證這個擴展三角形底大於高,才能實現包含。這樣求得的超級三角形不會特別大使得計算復雜,而且過程也簡單,並將超級三角形放入temp triangles中。

接下來就像是偽代碼中描述的那樣,對temp triangle中的的三角形遍歷畫外接圓,這時先對左邊的第一個點進行判斷,其在圓內,所以該三角形不為Delaunay三角形,將其三邊保存至edge buffer中,temp triangle中刪除該三角形。

將該點與edge buffer中的每一個邊相連,組成三個三角形,加入到temp triangles中,

再將重復對temp triangles的遍歷並畫外接圓,這時使用的是第二個點來進行判斷
- 該點在三角形1外接圓右側,則表示左側三角形為Delaunay三角形,將該三角形保存至triangles中
- 該點在三角形2外接圓外側,為不確定三角形,所以跳過(后面會講到為什么要跳過該三角形),但並不在temp triangles中刪除
- 該點在三角形3外接圓內側,則這時向清空后的edge buffer加入該三角形的三條邊,並用該點寫edge buffer中的三角邊進行組合,組合成了三個三角形並加入到temp triangles中

再次對temp triangles進行遍歷,這里該數組里則含有四個三角形,一個是上次檢查跳過的含有第一個點的三角形和新根據第二個點生成的三個三角形
- 該點在三角形1外接圓右側,則該三角形為Delaunay三角形,保存至triangles中,並在temp triangles中刪除
- 該點在三角形2外接圓外側,跳過
- 該點在三角形3外接圓內側,將該三邊保存至temp buffer中,並在temp triangles中刪除
- 該點在三角形4外接圓內側,將該三邊保存至temp buffer中,並在temp triangles中刪除
這時,temp buffer 中有六條邊,triangles中有兩個三角形,temp triangles中有1個三角形,對temp buffer中的六條邊進行去重,得到五條邊,將該點與這五條邊組合成五個三角形並加入到temp triagnles 中,這時temp triangles中有6個三角形

由於三個點已經遍歷結束,到了不會再對第三個點形成的三角形做外接圓,這時則將triangles與temp trianlges合並,合並后的數組表示包含已經確定的Delaunay三角形和剩下的三角形。這時除去合並后數組中的和超級三角形三個點有關的所有三角形,即進行數組坐標的限定,則得到了最后的結果:
二.代碼部分
實例1:(Matlab)
clear all;clc;close all; %load seamount [x,y,z]=peaks; plot3(x,y,z); %plot(x,y,'.','markersize',10) xlabel('x'), ylabel('y') grid on tri = delaunay(x,y); hold on, triplot(tri,x,y), hold off figure hidden on trimesh(tri,x,y,z) xlabel('x'),ylabel('y'),zlabel('Depth z');

左圖為peaks函數的三維圖形,有圖為使用dalaunay三角剖分得到的三維模型,對比。
另外,在http://blog.csdn.net/newthinker_wei/article/details/45598769這篇博文里有使用opencv進行dalaunay三角剖分的講述。
參考:
https://en.wikipedia.org/wiki/Delaunay_triangulation
http://www.cnblogs.com/RenLiQQ/archive/2008/02/06/1065399.html
http://www.linuxidc.com/Linux/2014-04/100131.htm
http://blog.csdn.net/newthinker_wei/article/details/45598769
http://cn.mathworks.com/help/matlab/math/delaunay-triangulation.html
