大概是暑假期間,和學弟討論了一下這個問題,當時只草草分析了一下問題的解決思路,但是自己一直沒有功夫動手實現。剛好國慶期間浪完返校,就想寫個程序收收心。於是就把這個Low Poly做了一下。
還是慣例先貼一下實現結果:
一、本文提供的資源
程序的百度網盤鏈接:https://pan.baidu.com/s/1YlTIMPv4VjxOtUmkdRiF5w 密碼:ufwu
bilibili程序演示視頻:https://www.bilibili.com/video/av33758221
二、程序信息
編程語言:C++(Qt)。程序除了自動生成Low Poly圖像外,還允許用戶使用鼠標對Polygon進行編輯調整。具體使用操作見我錄製的的Bilibili視頻。
程序的界面如下:
三、參考文獻:
[1] Meng Gai, Guoping Wang. Artistic Low Poly Rendering for Images.
[2] Cihan Topal, Cuneyt Akinlar. Edge Drawing:A Combined Real-time Edge and Segment Detector.
四、程序的算法流程
這裏簡單的介紹了本程序所採用的算法思路,重點說明了採用文獻[2]的Edge Drawing方法的單像素邊緣提取方法。
1. 高斯模糊
同傳統的圖像處理程序一樣,我們首先對原始圖像進行高斯模糊(Gaussian Blur),以減弱圖像噪聲並平滑圖像。關於這一步的處理,可以參考我的另一篇博客。
2. Edge Drawing - 單像素寬度的邊緣檢測
Edge Drawing是Topal等[2]提出的一種有效的邊緣檢測算法。同Canny、Sobel等傳統邊緣提取算法相比,Edge Drawing的一個優勢是:能夠提出到乾淨的、單像素寬的邊緣線條,並且在算法中能夠準確的獲取到這些數據結構。
圖1是兩種算法的對比圖,能夠發現Edge Drawing算法能夠更加有效的獲取邊緣信息。
Edge Drawing算法的詳細流程如下:
(1)計算Gradient map(map G)。map G用來反映圖像亮度梯度的變化。這裏可以採用任意梯度計算的算子進行計算,如Sobel、Prewitt等。map G中的每個像素的梯度變化可由公式計算得到。
(2)計算Direction map(map D)。mapD可與Gradient map同時計算得到。如果,則一條豎直方向的邊通過該像素;如果,則一條水平方向的邊經過該像素。
(3)錨點提取(Extraction of Archors)。這裏archors中的頂點,被用於作爲圖像邊緣edges的起始分析點。我們對原始圖像進行二次求導,即對Gradient map進行一次求導。此時計算結果反映的是亮度梯度的變化率。我們採用亮度梯度變化率較大的點作爲圖像的archor點。其算法流程如圖2所示:
(4)邊緣路徑追蹤。這裏我們對每個錨點進行邊緣路徑追蹤,以生成圖像的edges。首先Direction map反映了通過某點的edge方向,因此對於水平、豎直兩方向的點有如下幾個探測方向,如圖3所示:
之後,我們對每個archor進行相反方向的路徑追蹤,這可以是一個遞歸算法。圖4反映了向左追蹤的算法流程,其餘幾個方向與之類似。圖5反映了以點(4,8)爲起點的路徑追蹤結果。
3. 三角形頂點提取
此處的頂點提取,我們採取一種簡單的策略。即在Edge Drawing中獲取到的各個邊緣,限制每條Edge在經過minLen個像素寬度時變發生斷裂(例如minLen = 5),這時所有Edge在斷裂後餘下的點就是我們Delaunay三角剖分的輸入。
4. Delaunay三角剖分
Delaunay三角剖分是一種很常見的將離散點轉變成三角網格的處理算法,其實現也較爲簡單。不過需要提的一點是,建議參考維基中提及的flipped優化方法來實現,該策略能夠將算法耗時顯著降低。