實現SLIC演算法生成畫素畫

凹凸實驗室發表於2021-06-24

前言

畫素風最早出現在8bit的電子遊戲中,受制於電腦記憶體大小以及顯示色彩單一, 只能使用少量畫素來呈現內容,卻成就了不少經典的畫素遊戲。隨著記憶體容量與螢幕解析度的提升,記憶體與顯示媒介的限制不再是問題,而畫素風也慢慢演變成一種獨特的創作風格。

畫素畫的一般的繪製流程包括了勾線、填色等,而逐個畫素的繪製需要大量時間。一些流行的藝術方式,比如線描與繪畫領域,都逐漸出現了自動化或半自動化生成的方法。本文將從零開始實現SLIC[1]演算法,並實現一款生成畫素畫工具。

什麼是SLIC演算法

畫素畫的繪製之所以不簡單,是因為直接的下采樣並不能準確的捕獲關鍵畫素,且容易導致丟失邊緣資訊,生成的畫素畫往往不盡人意。手工的勾線、填色,都是為了選取合適的畫素點。由此,我們的問題變成了如何選取合適的畫素點進行填色。

首先,引入一個概念——超畫素。超畫素是 2003 年 Xiaofeng Ren 提出和發展起來的影像分割技術,是指具有相似紋理、顏色、亮度等特徵的相鄰畫素構成的有一定視覺意義的不規則畫素塊[1]。

通過將圖片分割為超畫素,可以得到相似的畫素簇,相似的畫素使用同一個顏色進行填充,得到的畫素畫會更合理。

超畫素點分割的方法包括了提取輪廓、聚類、梯度上升等多種。論文[1]提出的SLIC超畫素點分割演算法(簡單線性迭代聚類,simple linear iterative clustering)就是其中一種,它基於K-means聚類演算法,根據畫素的顏色和距離特徵進行聚類來實現良好的分割結果,與若干種超畫素點分割演算法相比,SLIC具有簡單靈活、效果好、處理速度快等優勢。
SLIC

如何實現SLIC演算法

SLIC的基本流程如下:

  1. 影像預處理。

    將影像從RGB顏色空間轉換到CIE-Lab顏色空間,Lab顏色空間更符合人類對顏色的視覺感知。這個空間裡的距離能反映人感覺到的顏色差別,相關計算更為準確。

    Lab顏色空間同樣具有三個通道,分別是lab,其中l代表亮度,數值範圍為[0,100]a表示從綠色到紅色的分量,數值範圍為[-128,127]b表示藍色到黃色的分量,數值範圍為[-128,127]

    RGBLAB之間沒有直接的轉換公式,需要將RGB轉為XYZ顏色空間再轉為LAB,程式碼見文末完整程式碼。

  2. 初始化聚類中心。

    根據引數確定超畫素的數目,也就是需要劃分為多少個區域。假設圖片有N個畫素點,預計分割為K個超畫素,每個超畫素大小為N/K,相鄰中心距離為S=Sqr(N/K),得到K個聚類座標。

  3. 優化初始聚類中心。在聚類中心的3*3鄰域內選擇梯度最小的畫素點作為新的聚類中心。

    把影像看成二維離散函式,梯度也就是這個函式的求導,當相鄰畫素值有變化就會存在梯度,而在邊緣上的畫素點的梯度最大。將聚類中心挪到梯度最小的地方可以避免其落到邊緣輪廓上,影響聚類效果。

    離散梯度的梯度計算這裡不做詳細推導了,由於其中包含了若干*方與開方,計算量較大,一般會簡化為用絕對值來*似*方和*方根的操作。簡化後的計算座標為(i,j)的畫素點的梯度公式為:

    其中(i+1,j)(i,j+1)為畫素右側點與畫素下方點的座標。l(a,b)(a,b)座標上畫素的亮度通道值l

  4. 計算畫素點與聚類中心的距離。

    在聚類中心距離S的區域內 2S*2S的鄰域內計算畫素點與每個聚類中心的距離。

    這裡的距離使用的是歐式距離,總距離Ddc顏色距離與ds空間距離兩部分組成。公式如下:

    如果直接將labxy拼接成一個向量計算距離,當超畫素的大小變化時,xy的值可以取到非常大 ,比如如果一張圖1000*1000,空間距離可以達到1000*Sqr(2),而顏色距離最大僅10*Sqr(2),導致最終計算得到的距離值中,空間距離ds權重佔比過大。

    所以需要進行歸一化,除以最大值即超畫素點的初始寬度S,將值對映到[0,1]

    而顏色空間距離也會給到一個固定的值m來調節顏色距離與空間距離的影響權重,m取值範圍為[1,40]

    距離公式即變成了

    205.png

    m越大,顏色空間除以m後的值越小,即空間距離的權重越大,生成的畫素會更為形狀規則,當m越小,顏色距離權重更大,超畫素會在邊緣更為緊湊,而形狀大小較為不規則。

  5. 畫素點分類。

    標記每個畫素點的類別為距離其最小的聚類中心的類別。

  6. 重新計算聚類中心。

    計算屬於同一個聚類的所有畫素點的*均向量值,重新得到聚類中心 。

  7. 迭代4~6的過程。

    直到舊聚類中心與新聚類中心的距離小於一定閾值或者達到一定迭代次數,一般來說,當迭代次數到達10,演算法能夠達到收斂。

  8. 聚類優化。

    迭代到最後,可能會出現與聚類中心不屬於同一連通域的孤立畫素點,可以使用到連通演算法將其分配到最*的聚類標籤。

    論文中並未給出具體的實現演算法。而本文的應用場景是生成畫素畫,會對畫素進行下取樣,並不會細化到每個畫素,由此,本文不做聚類優化處理。

小小總結一下,SLIC演算法流程大體與K-means是一致的,不斷迭代計算距離最小的聚類簇,不同的是隻對聚類中心的S距離內畫素點進行計算,減少了不少的計算量。

生成畫素畫

基於SLIC演算法,我們已經可以把一張圖劃分為N個超畫素點。每個超畫素中畫素都是相*的。也就是說,每個畫素都被歸類為一個超畫素,有一個聚類中心。那麼將畫素的顏色賦值為其聚類中心的顏色即得到我們想要的效果。

設定一定步長stride,使用Canvas,每隔stride個畫素,將畫素賦值為其聚類中心的顏色,即得到最終的畫素化結果。

而每個人對於畫素畫的主觀感受是不一致的,為了讓使用者有更多的選擇,得到自己滿意的結果。可以暴露更多的人工干預引數,比如取消聚類優化的終止條件,改為由使用者來設定迭代次數,以及最終取畫素值的步長。人工設定的引數包括了

  • 超畫素點大小blocksizeblocksize越小,超畫素點分割越細膩。
  • 迭代次數itersiters越大,分割結果更精準,計算時間越長。
  • 顏色空間權重weightweight越大,顏色對於分割結果的影響越大。
  • 取畫素點步長stridestride越小,生成的畫素圖越接*超畫素點,也就越細膩。

實現使用者互動介面

作為一個工具,自然需要使用者互動介面,前端介面基於HTML/Javascript/CSS搭建,使用Canvas API繪製影像內容,而使用者互動皮膚選擇的是dat.gui [3] 庫。dat.gui是一個輕量級的影像化介面庫,非常適用於引數的修改,常用作視覺化 Demo 的演示。支援的引數型別包括了NumberStringBoolean、自定義函式等。可以為不同的屬性繫結相應的響應事件,當屬性值改變時自動觸發事件。

為生成畫素化工具新增以下屬性與事件:

  • iters、stride、blockSize、weight(顏色空間權重m)引數變化時重新進行SLIC演算法的計算,並重新繪製計算結果;
  • 新增Upload imageExport image按鈕,支援使用者上傳圖片與下載畫素化後的圖片;

在繪製影像的Canvas畫布層上疊加一層Canvas畫布,對演算法的結果進行視覺化,新增以下功能

  • grid開關控制是否繪製畫素網格;
  • Centers開關控制是否顯示聚類中心;
  • Contours開關控制是否顯示聚類邊緣輪廓;

其中聚類中心點Centers的繪製直接使用ctx.fillRect 傳入中心點座標即可。

超畫素輪廓Contours的繪製則需要先計算得到輪廓點。

可以對每個畫素點與周圍的8個畫素點進行比較,如果聚類中心不同的畫素點個數大於2,則代表著這個畫素點周圍有兩個以上不同類別的點,則這個點為輪廓。效果如下:

最後,就得到一個簡單的生成畫素畫工具了。

體驗地址

完整版程式碼地址(JS版)

參考文獻

[1] Achanta R, Shaji A, Smith K, Lucchi A, Fua P, Su ̈sstrunk S. SLIC superpixels. Technical Report. IVRG CVLAB; 2010.

[2] Gerstner T , Decarlo D , Alexa M , et al. Pixelated image abstraction with integrated user constraints[J]. Computers & graphics, 2013.

[3] https://github.com/dataarts/dat.gui

歡迎關注凹凸實驗室部落格:aotu.io

或者關注凹凸實驗室公眾號(AOTULabs),不定時推送文章:

歡迎關注凹凸實驗室公眾號

相關文章