基于边缘的主动轮廓模型——从零到一用python实现snake
從零到一實現snake算法
- 1、Snake算法原理
- 2、基于曲線演化的實現方法
- 2.1演化方程推導
- 2.2離散化過程
- 2.3 代碼實現
- 3、基于水平集的實現方法
- 4、討論與分析
- 源碼地址[snake](https://github.com/woshimami/snake)
1、Snake算法原理
Kass等人1最早于1988年提出了主動輪廓模型,該方法通過構造能量函數,從而將圖像分割轉化為求解能量泛函極值的問題,其核心思想在于,作者認為和之前的分割方法相比,在不同的圖像解讀(image interpretation)任務中,表觀或者淺層次(low level)的圖像判讀任務應該需要一些深層次(high level)的圖像信息,并論文[1]中進行了舉例分析,有興趣的同學可以自行翻閱論文查看,這也和主動輪廓模型的兩大特點不謀而合:全局性和可拓展性。全局性也就是說在圖像處理任務進行中,不僅要關注局部區域或者邊緣信息,同樣也要關注圖像或曲線的整體信息如曲線整體的長度和曲率等。再者針對不同的圖像特點,能夠設計或者添加個性化的分割策略,接下來我將根據公式對此進一步分析。
對于圖像I(x,y),C(s)=C(x(s),y(s))I(x,y),C(s)=C(x(s),y(s))I(x,y),C(s)=C(x(s),y(s))為演化曲線,定義snake模型的能量函數為:Esnake=Eint+EextE_{snake}= E_{int}+E_{ext}Esnake?=Eint?+Eext? 這里我們區分內部能量和外部能量的依據就是看它是否和演化曲線本身的特性相關。比如說下式中,定義內部能量項的兩部分內容分別代表的就是曲線的長度和曲率: Eint=∫0112?(α(s)∣cs(s)∣2+β(s)∣css(s)∣2)dsE_{int} =\int_0^1 \frac{1}{2}* {(\alpha(s)|c_s(s)|^2+\beta(s)|c_{ss}(s)|^2)} \,{\rm d}s Eint?=∫01?21??(α(s)∣cs?(s)∣2+β(s)∣css?(s)∣2)ds 其中公式中的兩項內容分別表示的就是曲線的長度項和曲線的光滑程度, α(s)\alpha(s)α(s)和β(s)\beta(s)β(s)可以根據曲線上某一點的特征進行個性化處理,但是大多數情況下我們一般定義它們為常數,用于調節曲線長度項和光滑程度占比。
而外部能量項通常由兩部分組成構成:Eext=Eimg+EconsE_{ext} = E_{img}+E_{cons}Eext?=Eimg?+Econs? EimgE_{img}Eimg?通常表示和圖像相關的外部能量項目,主要包含如下三種信息,
(1)
最后還有一個限制項EconE_{con}Econ?,這一項就是對曲線演化添加一定的限制作用,比如限制整體曲線到某一點或者某一區域RRR的距離:Econ=∫01∣∣C(x(s),y(s))?R∣∣dsE_con = \int_0^1{||C(x(s),y(s))-R||}\,{\rm d}sEc?on=∫01?∣∣C(x(s),y(s))?R∣∣ds具體的情況大家可以根據圖片特點或者需求靈活定義。
2、基于曲線演化的實現方法
好了,終于到了激動人心的實現環節了,大家是不是跟我一樣激動的直搓手,哈哈哈哈哈哈哈
開始之前我先說明兩點內容:1)本次實現過程基于較為簡單基礎的能量泛函,大家可以先實現體驗一邊,然后再觸類旁通,舉一反三;2)以方面需要對一些數學知識進行回顧分析,另一方面希望大家仔細品一品從連續到離散的變化過程。
2.1演化方程推導
首先我們定義能量函數:Esnake=∫0112?(α∣cs(s)∣2+β∣css(s)∣2?∣?I(x(s),y(s))∣2)dsE_{snake} =\int_0^1 \frac{1}{2}* {(\alpha|c_s(s)|^2+\beta|c_{ss}(s)|^2 -|\nabla{I(x(s),y(s))}|^2)}\,{\rm d}sEsnake?=∫01?21??(α∣cs?(s)∣2+β∣css?(s)∣2?∣?I(x(s),y(s))∣2)ds,當能量函數最小時,就可以獲得C(x(s),y(s))C(x(s),y(s))C(x(s),y(s))的曲線方程,本質上這也是一個求能量泛函極值的問題,直接根據歐拉-拉格朗日方程(變分原理),當能量泛函取極值時,應滿足:?αc′′(s)+βc′′′′(ss)+?Eext=0-\alpha c^{''}(s)+\beta c^{''''}(ss)+\nabla E_{ext} = 0?αc′′(s)+βc′′′′(ss)+?Eext?=0 可是就算推到這種程度我們以然沒辦法直接求出曲線c(x(s),y(s))c(x(s),y(s))c(x(s),y(s)),尤其是我們希望公式能夠以偏微分方程的形式出現,這樣我們就可以利用計算機進行編程序迭代計算了,于是我們在曲線中引入了一個輔組變量ttt,則解可以表示為c(t,s)c(t,s)c(t,s),隨時間變化的解c(t,s)c(t,s)c(t,s)應該使得能量函數逐漸變小(推導過程后續會補充),從而可以得出:?c(s)?t=αc′′(s)?βc′′′′(ss)??Eext\frac{\partial c(s)}{\partial t} = \alpha c^{''}(s)-\beta c^{''''}(ss)-\nabla E_{ext} ?t?c(s)?=αc′′(s)?βc′′′′(ss)??Eext?
2.2離散化過程
接下來就是整個離散化過程了,首先,我們先明確一下概念,上式中的sss表示什么意思,因為我們看到的圖片都是離散的點,所以我們剛開定義初始化的snake曲線時,實際上定義的也就是一系列離散的點的集合,而曲線的演化過程實際上也就是這些固定數目的點的演化過程,離散化后,我們用iii表示sss,那假設初始化的snake曲線有NNN個點,那i∈[0,N?1]i\in [0,N-1]i∈[0,N?1],且x[N]=x[0],x[N+1]=x[2]....x[N] = x[0],x[N+1]=x[2]....x[N]=x[0],x[N+1]=x[2]....,那么對于曲線上任一點c(x(i),y(i))c(x(i),y(i))c(x(i),y(i)):x′′[i]=x[i?1]?2x[i]+x[i+1]x^{''}[i] = x[i-1]-2x[i]+x[i+1]x′′[i]=x[i?1]?2x[i]+x[i+1] x′′′′[i]=x[i?2]?4x[i?1]+6x[i]?4x[i+1]+x[i+2]x^{''''}[i] = x[i-2]-4x[i-1]+6x[i]-4x[i+1]+x[i+2]x′′′′[i]=x[i?2]?4x[i?1]+6x[i]?4x[i+1]+x[i+2]對y也是同理,假設圖像xxx方向和yyy方向的梯度為G(x,y)G(x,y)G(x,y)(均為正值),那外部力則分別為Gx[x,y],Gy[x,y]G_{x}[x,y],G_{y}[x,y]Gx?[x,y],Gy?[x,y],假定每次迭代后,曲線變化較小,我們用Gt?1(x,y)G_{t-1}(x,y)Gt?1?(x,y)代替Gt(x,y)G_{t}(x,y)Gt?(x,y),那么對任一點c(x(i),y(i))c(x(i),y(i))c(x(i),y(i))就可以推導出離散后的公式為:
?xt[i]?t=α(xt[i?1]?2xt[i]+xt[i+1])+β(?xt[i?2]+4xt[i?1]?6xt[i]+4xt[i+1]?xt[i+2])+Gx(xt[i],yt[i])\frac{\partial x_t[i] }{\partial t} = α(x_t[i-1]-2xt[i]+x_t[i+1]) + β(-x_t[i-2]+4x_t[i-1]-6xt[i]+4x_t[i+1]-x_t[i+2]) + G_x(x_t[i],y_t[i])?t?xt?[i]?=α(xt?[i?1]?2xt[i]+xt?[i+1])+β(?xt?[i?2]+4xt?[i?1]?6xt[i]+4xt?[i+1]?xt?[i+2])+Gx?(xt?[i],yt?[i])用λ\lambdaλ表示步長,則可以進一步表示為:xt[i]?xt?1[i]λ=α(xt[i?1]?2xt[i]+xt[i+1])+β(?xt[i?2]+4xt[i?1]?6xt[i]+4xt[i+1]?xt[i+2])+Gx(xt?1[i],yt?1[i])\frac{x_t[i] - x_{t-1}[i] }{\lambda} = α(x_t[i-1]-2xt[i]+x_t[i+1]) + β(-x_t[i-2]+4x_t[i-1]-6xt[i]+4x_t[i+1]-x_t[i+2]) + G_x(x_{t-1}[i],y_{t-1}[i])λxt?[i]?xt?1?[i]?=α(xt?[i?1]?2xt[i]+xt?[i+1])+β(?xt?[i?2]+4xt?[i?1]?6xt[i]+4xt?[i+1]?xt?[i+2])+Gx?(xt?1?[i],yt?1?[i])同理, yt[i]?yt?1[i]λ=α(yt[i?1]?2yt[i]+yt[i+1])+β(?yt[i?2]+4yt[i?1]?6yt[i]+4yt[i+1]?yt[i+2])+Gy(yt?1[i],yt?1[i])\frac{y_t[i] - y_{t-1}[i] }{\lambda} = α(y_t[i-1]-2yt[i]+y_t[i+1]) + β(-y_t[i-2]+4y_t[i-1]-6yt[i]+4y_t[i+1]-y_t[i+2]) + G_y(y_{t-1}[i],y_{t-1}[i])λyt?[i]?yt?1?[i]?=α(yt?[i?1]?2yt[i]+yt?[i+1])+β(?yt?[i?2]+4yt?[i?1]?6yt[i]+4yt?[i+1]?yt?[i+2])+Gy?(yt?1?[i],yt?1?[i]) ,如果一條曲線上有NNN個點,那就對xxx和yyy分別列出NNN個式子,如果用矩陣表示的話,那就是:Xt?Xt?1λ=AXt+Gx(xt?1,yt?1)\frac{X_{t}-X_{t-1}}{\lambda} = AX_t + G_x(x_{t-1},y_{t-1})λXt??Xt?1??=AXt?+Gx?(xt?1?,yt?1?)矩陣A則為:(?2α+6βα+4β?β000??βα+4βα+4β?2α+6βα+4β?β00?0?β?βα+4β?2α+6βα+4β?β0?000?βα+4β?2α+6βα+4β?β?0000?βα+4β?2α+6βα+4β?00??????β00000??2α+6βα+4βα+4β?β0000?α+4β?2α+6β)\begin{pmatrix} -2\alpha+6\beta & \alpha+4\beta & -\beta & 0 & 0& 0 & \cdots & -\beta & \alpha+4\beta & \\ \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & -\beta & 0& 0 & \cdots & 0 & -\beta & \\ -\beta & \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & -\beta & 0 & \cdots & 0 & 0 & \\ 0 & -\beta & \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & -\beta & \cdots & 0 & 0 & \\ 0 & 0 & -\beta & \alpha+4\beta & -2\alpha+6\beta & \alpha+4\beta & \cdots & 0 & 0 & \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ -\beta & 0 &0 & 0 & 0& 0 & \cdots & -2\alpha+6\beta & \alpha+4\beta & \\ \alpha+4\beta & -\beta &0 & 0 & 0& 0 & \cdots & \alpha+4\beta & -2\alpha+6\beta & \\ \end{pmatrix} ???????????????2α+6βα+4β?β00??βα+4β?α+4β?2α+6βα+4β?β0?0?β??βα+4β?2α+6βα+4β?β?00?0?βα+4β?2α+6βα+4β?00?00?βα+4β?2α+6β?00?000?βα+4β00??????????β0000?2α+6βα+4β?α+4β?β000α+4β?2α+6β????????????????,求解矩陣即可得出:Xt=(1?λA)?1Xt?1+λGx(Xt?1,Yt?1)X_t = (1-\lambda A)^{-1}{X_{t-1}+\lambda G_x(X_{t-1},Y_{t-1})}Xt?=(1?λA)?1Xt?1?+λGx?(Xt?1?,Yt?1?) Yt=(1?λA)?1Yt?1+λGy(Xt?1,Yt?1)Y_t = (1-\lambda A)^{-1}{Y_{t-1}+\lambda G_y(X_{t-1},Y_{t-1})}Yt?=(1?λA)?1Yt?1?+λGy?(Xt?1?,Yt?1?)至此,需要的核心演化公式到手。
2.3 代碼實現
看完上面的離散化過程,是不是大家現在都胸有成竹,躍躍欲試,蠢蠢欲動啦,哈哈,接下來就讓我們一起開始愉快的coding吧。
聲明使用的依賴庫主要有:
代碼實現總共分為如下幾個步驟:
隨手用windows畫圖工具畫了個圓(簡約派):
然后讀入圖片,注意
計算結果,撒花,撒花,撒花!
3、基于水平集的實現方法
本來這一段打算拖一拖的,但是看到評論區有哥們兒問到了,我就仔細找了找文獻,看看怎么給處理下,結果發現自己給自己挖了個大坑,對于原始的snake算法來講,使用level set并不是一個很可行的方案或者說需要對snake算法做一些改變,那么為什么將level set方法直接引用到snake模型上不合適呢?
1)首先,大家需要明白常見的主動輪廓模型的常見處理流程:1、構建能量泛函; 2、極小化能量泛函(歐拉-拉格朗日方程+梯度下降法)獲得用于演化的偏微分方程; 3、離散化偏微分方程; 4、代碼實現與調試,一般都是從步驟1或者步驟2中引入level set,可以參見我的另一篇博客(水平集方法引入主動輪廓模型算法中的兩種方法);2)但是,對于原始的snake算法來講,只存在從步驟1即從能量函數中引入水平集函數的可能性,不過因為那樣可能處理起來太麻煩,所以后來Caselles 等人就在1993年直接對原始snake算法就行了調整,提出了改進的幾何活動輪廓模型,不僅引入了水平集方法還擺脫了原始snake模型對參數的依賴問題。
因此,總的來說,將level set方法引入snake是理論上的可行性的,只不過比較麻煩,這個博主最近時間比較緊張,等過一段時間閑了再想辦法給實現下吧(又立flag…)。
4、討論與分析
接下來就讓我們愉快的討(tu)論(cao)下snake算法吧! 有一說一,不得不承認,Kass等人1當初將圖像分割問題轉化為求解能量泛函極值的問題確實很有想象力,這個控制領域的李雅普諾夫函數簡直有異曲同工之妙之妙,很好奇當時作者哪來的這么大的腦洞哈。
源碼地址snake
[1] (KASS,M. snake Active contour models[J]. Proc.first Intl Conf.computer Vision, 1988, 4.) [2] (Caselles V . Geometric models for active contours[C]// International Conference on Image Processing. IEEE Computer Society, 1995.)總結
以上是生活随笔為你收集整理的基于边缘的主动轮廓模型——从零到一用python实现snake的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gvf snake matlab,GVF
- 下一篇: 使用xshell连接阿里云服务器【最近版