主動輪廓線模型又稱為Snake模型,自Kass于1987年提出以來,已廣泛應用于數字圖像分析和計算機視覺領域。由于Snake模型具有良好的提取和跟蹤特定區域內目標輪廓的能力,因此非常適合于醫學圖像如CT和MR圖像的處理,以獲得特定器官及組織的輪廓。簡單的來講,Snake模型就是一條可變形的參數曲線及相應的能量函數,以最小化能量目標函數為目標,控制參數曲線變形,具有最小能量的閉合曲線就是目標輪廓。 Snake模型具有一些經典方法所無法比擬的優點:圖像數據、初始估計、目標輪廓及基于知識的約束統一于一個過程中;經適當的初始化后,它能自主地收斂于能量極小值狀態;尺度空間中由初到精地極小化能量可以極大地擴展捕獲區域和降低復雜性。同時,Snake模型也有其自身的缺點:對初始位置敏感,需要依賴其他機制將Snake放置在感興趣的圖像特征附近;由于Snake模型的非凸性,它有可能收斂到局部極值點,甚至發散。
?
Snake模型稱為動態輪廓模型(Active Contour? Model)是Kass與1987年提出的,它對于在噪聲和對比度不敏感,能將目標從復雜背景中分割出來,并能有效的跟蹤目標的形變和非剛體的復雜運動而被廣泛用于圖像分割和物體跟蹤等圖像處理領域。
Snake主要原理是先提供待分割圖像的一個初始輪廓的位置,并對其定義個能量函數,是輪廓沿能量降低的方向靠近。當能量函數達到最小的時候,提供的初始輪廓收斂到圖形中目標的真實輪廓。
Snake能量函數是有內部能量函數和外部能量函數組成,內部能量控制輪廓的平滑性和連續性,外部能量由圖像能量和約束能量組成,控制輪廓向著實際輪廓收斂,其中約束能量可根據具體的對象形態定義,使得snake具有很大的靈活性。
Snake模型發展10多年來,許多學者對于經典的snake模型做了改進,提出各種改進的snake模型,其中梯度矢量流(Gradient Vector? Flow,GVF)模型擴大了經典snake的外力作用范圍,加強了對目標凹輪廓邊緣的吸引力,提高了傳統的snake模型。
?
Snake模型主要研究的方面:
1.表示內部能量的曲線演化??? 2.外力??? 3.能量最小化
Snake模型初始輪廓的選擇
由于snake模型對于初始位置比較敏感,因此要求初始輪廓盡可能的靠近真實輪廓,而當圖像邊緣模糊,目標比較復雜或與其他的物體靠的比較近時,其初始輪廓更不易確定。
現有的初始輪廓確定的方法有以下幾種:1.人工勾勒圖像的邊緣??? 2.序列圖像差分邊界??? 3.基于序列圖像的前一幀圖像邊界的預測? 4.基于傳統圖像分割結果進行邊界選取
分水嶺算法
分水嶺算法是由S.Beucher? F.Meyer最早引入圖像分割領域,它的基本思想是來源于測地學上的側線重構,其內容是把圖像看做是測地學上的拓撲地貌。進行分水嶺模型計算的比較經典的算法是L? Vincent提出的,在該算法中首先是對每個像素的灰度級進行從低到高排序,然后用等級對壘模擬淹沒,初始時,等級隊列中為淹沒的初始點,在從低到高實現淹沒的過程中,對每一個局部極小值在H階高度的影響域采用先進先出(FIFO)結構進行判斷及標注,直到最后一個值被淹沒,從而正確劃分各個區域。
整個洪水淹沒的循環迭代過程可以通過以下兩個步驟表示:
分水嶺算法的優點:
1.分水嶺算法對于圖像中由于像素差別較小而產生微弱邊緣具有良好的響應,可以得到封閉連續的邊緣,而且可以保證在照明,陰影等影響下分割邊緣的封閉性和連續性
分水嶺算法對于目標物體之間或者是目標物體同背景物體之間粘連的情況有較好的處理效果。能夠較好的分割這類目標物體。
3.圖像內部的陰暗變化對于分水嶺算法影響較小,可以在一定程度上減小由于陰暗便哈帶來的圖像分割影響
與其他邊緣分割算子比較:
Canny算子可以很好的勾勒出物體的輪廓,過分的強調輪廓的特性,而沒有強調物體的輪廓必須是封閉的,在圖像中顯示的輪廓是不封閉的,物體內部陰暗變化也被當做邊界檢測出來,形成大量的偽邊緣。
分水嶺算法分割得到的輪廓曲線時連續封閉的,圖像內部的陰暗變化沒有生成獨立的輪廓線。
Snake模型的缺陷:
對初始位置敏感,易陷入局部極值,無法收斂到輪廓深度凹陷部分,不具備自動拓撲變換功能等。
Snake模型的改進算法:
1.Cohen提出的氣球(balloon)理論模型:應用壓力和高斯能力一起增大吸引范圍的方法,該壓力可使模型擴大或縮小,因此不再要求將模型初始化在所期望的對象邊界附近。在圖像的梯度力場上疊加氣球里,以使輪廓線作為一個整體進行膨脹或收縮,從而擴大了模型尋找圖像特證的范圍。
優勢:對初始邊界不敏感????????????
存在的缺點:存在弱邊界,漏出邊界間隙等問題。
2.Xu提出梯度矢量流(GVF)概念,用GVF場代替經典外力場,GVF場可以看做是對圖像梯度場得逼近,這不僅使模型捕捉的范圍得到了提高,而且能使活動輪廓進入凹陷區。
優勢:有良好的收斂性,深入目標邊緣的凹陷區域???????????
存在的缺點:仍不能解決曲線的拓撲變化問題
局部優化算法:
1.Amini提出基于動態規劃的snake算法。 2.變分法? 3.貪婪算法? 4.有限差分法?? 5.有限元法
全局優化算法:
1.模擬退火???? 2.遺傳算法??? 3.神經網絡
Snake模型的蟻群算法(Ant Colony Optimization)模型
蟻群算法是最近幾年有意大利學者M.Dorigo等人首次提出的一種新型的模擬進化算法,稱為蟻群系統,蟻群算法通過候選解組成的群體的進化過程來尋求最優解,該過程包括兩個基本階段:適應階段和協同工作階段,算法本身采用正反饋原理,加快了進化過程,不易陷入局部最優解,而且個體之間不斷進行信息交流和傳遞,有利于對解空間的進一步探索,因此有很強的發展解的能力。
?
?
Snake的進化模型
1.McInerney 提出一種拓撲自適應snake模型(Topology Adaptive? Snake,T-Snake)
該算法基于仿射細胞圖像分解(Affine Cell Image? Decomposition,ACID)先在待分割圖像上加以三角網格,然后在圖像區域的適當位置做一條初始曲線,最后取曲線與網格的交點作為snake的初始離散點,其第i個snake的離散點的坐標為其中,相鄰兩點,之間由一條彈性樣條連接而成
由于T-Snake模型可借助三角形網格和網格點的特征函數來確定邊界三角形,可促使snake模型演化過程中的分裂和合并,從而保證了其具有能夠處理拓撲結果復雜圖像的能力,因此能夠很好的滿足醫學圖像拓撲結果復雜的特點。此算法用于腦部MR切片有良好的性能。
2.雙T-Snake模型
雙T-Snake模型(Dual-T-Snakes)是在T-Snake模型的基礎上產生的,其主要思想是采用內外兩個初始輪廓,其中一個輪廓從目標外向內收縮和分裂,另一個輪廓從目標內部向外膨脹,兩個初始輪廓可以離目標邊界較遠,迭代的過程中對能量較大的輪廓增加驅動力,使其靠近與之相對應的輪廓,直到連個輪廓收斂到同一個為止
3.Loop? Snake 模型
Loop? Snake模型是一種加強了拓撲控制的T-Snake模型,這種方法的關鍵集中在曲線的每一步進化中都要形成循環,其基本思想是,確保圖像輪廓曲線精確地線性地映射到適當的分類中,然后在額外的記過loop-Tree的幫助下,盡可能少的時間內運用已經被snake探究的循環來決定是否進行區域劃分,這種模型的實質是對T-Snake模型的一種改進。由于加強了拓撲控制,使得Loop Snake模型既可以忽略背景中強噪聲又可以在演化過程中進行多次分裂。
4.連續snake模型
在Snake模型中,輪廓曲線由一條給定容許誤差范圍的光滑曲線組成,相對于離散snake來說,連續snake模型所需要的控制點少,比離散的更具優越性。
5.B-Snake模型
B-Snake模型是通過B樣條曲線來定義的,其輪廓曲線由各曲線段光滑相連而成,每一個曲線段都是由一個給定次數多項式表示,這種多項式是B樣條曲度函數的一種線性組合,并以控制點為系數。在有些B-Snake模型中并沒有明確應用內部能量,這是因為B樣條本身就含有內部能量,snake輪廓曲線只受外力影響著圖像邊緣移動。可用于對圖像切片分割區域的描述與跟蹤而用于器官的三維重建。
?
應用snake的優勢:由于生物或人體組織解剖結構的復雜性,以及軟組織形狀的易變性,那些僅依賴于圖像本身的灰度,紋理屬性等低層次視覺屬性來進行分割的圖像分割方法難以獲得理想的分割效果,因此醫學圖像分割迫切需要有一種靈活的框架,能將基于圖像本身低層次視覺屬性(邊緣,紋理,灰度,色彩)和人們對于待分割目標的知識經驗,如目標形狀的描述,亮度,色彩的經驗統計,醫生的經驗等,可以一種有機的方式整合起來,得到待分割區域的完整表達。
?
Opencv中snake函數解析及例子:
?
[cpp]?view plain?copy
/*M///?//?//??IMPORTANT:?READ?BEFORE?DOWNLOADING,?COPYING,?INSTALLING?OR?USING.?//?//??By?downloading,?copying,?installing?or?using?the?software?you?agree?to?this?license.?//??If?you?do?not?agree?to?this?license,?do?not?download,?install,?//??copy?or?use?the?software.?//?//?//????????????????????????Intel?License?Agreement?//????????????????For?Open?Source?Computer?Vision?Library?//?//?Copyright?(C)?2000,?Intel?Corporation,?all?rights?reserved.?//?Third?party?copyrights?are?property?of?their?respective?owners.?//?//?Redistribution?and?use?in?source?and?binary?forms,?with?or?without?modification,?//?are?permitted?provided?that?the?following?conditions?are?met:?//?//???*?Redistribution's?of?source?code?must?retain?the?above?copyright?notice,?//?????this?list?of?conditions?and?the?following?disclaimer.?//?//???*?Redistribution's?in?binary?form?must?reproduce?the?above?copyright?notice,?//?????this?list?of?conditions?and?the?following?disclaimer?in?the?documentation?//?????and/or?other?materials?provided?with?the?distribution.?//?//???*?The?name?of?Intel?Corporation?may?not?be?used?to?endorse?or?promote?products?//?????derived?from?this?software?without?specific?prior?written?permission.?//?//?This?software?is?provided?by?the?copyright?holders?and?contributors?"as?is"?and?//?any?express?or?implied?warranties,?including,?but?not?limited?to,?the?implied?//?warranties?of?merchantability?and?fitness?for?a?particular?purpose?are?disclaimed.?//?In?no?event?shall?the?Intel?Corporation?or?contributors?be?liable?for?any?direct,?//?indirect,?incidental,?special,?exemplary,?or?consequential?damages?//?(including,?but?not?limited?to,?procurement?of?substitute?goods?or?services;?//?loss?of?use,?data,?or?profits;?or?business?interruption)?however?caused?//?and?on?any?theory?of?liability,?whether?in?contract,?strict?liability,?//?or?tort?(including?negligence?or?otherwise)?arising?in?any?way?out?of?//?the?use?of?this?software,?even?if?advised?of?the?possibility?of?such?damage.?//?//M*/??#include?"_cv.h"?????#define?_CV_SNAKE_BIG?2.e+38f??#define?_CV_SNAKE_IMAGE?1??#define?_CV_SNAKE_GRAD??2????????/*F///?//????Name:??????icvSnake8uC1R?????//????Purpose:???//????Context:???//????Parameters:?//???????????????src?-?source?image,?//???????????????srcStep?-?its?step?in?bytes,?//???????????????roi?-?size?of?ROI,?//???????????????pt?-?pointer?to?snake?points?array?//???????????????n?-?size?of?points?array,?//???????????????alpha?-?pointer?to?coefficient?of?continuity?energy,?//???????????????beta?-?pointer?to?coefficient?of?curvature?energy,??//???????????????gamma?-?pointer?to?coefficient?of?image?energy,??//???????????????coeffUsage?-?if?CV_VALUE?-?alpha,?beta,?gamma?point?to?single?value?//????????????????????????????if?CV_MATAY?-?point?to?arrays?//???????????????criteria?-?termination?criteria.?//???????????????scheme?-?image?energy?scheme?//?????????????????????????if?_CV_SNAKE_IMAGE?-?image?intensity?is?energy?//?????????????????????????if?_CV_SNAKE_GRAD??-?magnitude?of?gradient?is?energy?//????Returns:???//F*/?????static?CvStatus??icvSnake8uC1R(?unsigned?char?*src,???//原始圖像數據?????????????????int?srcStep,?????????//每行的字節數?????????????????CvSize?roi,?????????//圖像尺寸?????????????????CvPoint?*?pt,???????//輪廓點(變形對象)?????????????????int?n,????????????//輪廓點的個數?????????????????float?*alpha,???????//指向α的指針,α可以是單個值,也可以是與輪廓點個數一致的數組?????????????????float?*beta,????????//β的值,同α?????????????????float?*gamma,???????//γ的值,同α?????????????????int?coeffUsage,???//確定αβγ是用作單個值還是個數組??????????CvSize?win,???????//每個點用于搜索的最小的領域大小,寬度為奇數???????????????CvTermCriteria?criteria,???//遞歸迭代終止的條件準則??int?scheme?)?????????//確定圖像能量場的數據選擇,1為灰度,2為灰度梯度??{??????int?i,?j,?k;??????int?neighbors?=?win.height?*?win.width;????//當前點領域中點的個數????????//當前點的位置??????int?centerx?=?win.width?>>?1;????????????????int?centery?=?win.height?>>?1;??????????????????float?invn;????????//n?的倒數???????int?iteration?=?0;?????//迭代次數??????int?converged?=?0;??????//收斂標志,0為非收斂??????????//能量??????float?*Econt;????//??????float?*Ecurv;???//輪廓曲線能量??????float?*Eimg;????//圖像能量??????float?*E;??????//?????????//αβγ的副本??????float?_alpha,?_beta,?_gamma;?????????/*#ifdef?GRAD_SNAKE?*/??????float?*gradient?=?NULL;??????uchar?*map?=?NULL;??????int?map_width?=?((roi.width?-?1)?>>?3)?+?1;??????int?map_height?=?((roi.height?-?1)?>>?3)?+?1;??????CvSepFilter?pX,?pY;??????#define?WTILE_SIZE?8??????#define?TILE_SIZE?(WTILE_SIZE?+?2)?????????????short?dx[TILE_SIZE*TILE_SIZE],?dy[TILE_SIZE*TILE_SIZE];??????CvMat?_dx?=?cvMat(?TILE_SIZE,?TILE_SIZE,?CV_16SC1,?dx?);??????CvMat?_dy?=?cvMat(?TILE_SIZE,?TILE_SIZE,?CV_16SC1,?dy?);??????CvMat?_src?=?cvMat(?roi.height,?roi.width,?CV_8UC1,?src?);?????????/*?inner?buffer?of?convolution?process?*/??????//char?ConvBuffer[400];?????????/*#endif?*/??????????//檢點參數的合理性??????/*?check?bad?arguments?*/??????if(?src?==?NULL?)??????????return?CV_NULLPTR_ERR;??????if(?(roi.height?<=?0)?||?(roi.width?<=?0)?)??????????return?CV_BADSIZE_ERR;??????if(?srcStep?<?roi.width?)??????????return?CV_BADSIZE_ERR;??????if(?pt?==?NULL?)??????????return?CV_NULLPTR_ERR;??????if(?n?<?3?)?????????????????????????//輪廓點至少要三個??????????return?CV_BADSIZE_ERR;??????if(?alpha?==?NULL?)??????????return?CV_NULLPTR_ERR;??????if(?beta?==?NULL?)??????????return?CV_NULLPTR_ERR;??????if(?gamma?==?NULL?)??????????return?CV_NULLPTR_ERR;??????if(?coeffUsage?!=?CV_VALUE?&&?coeffUsage?!=?CV_ARRAY?)??????????return?CV_BADFLAG_ERR;??????if(?(win.height?<=?0)?||?(!(win.height?&?1)))???//鄰域搜索窗口得是奇數??????????return?CV_BADSIZE_ERR;??????if(?(win.width?<=?0)?||?(!(win.width?&?1)))??????????return?CV_BADSIZE_ERR;?????????invn?=?1?/?((float)?n);????????//輪廓點數n的倒數,用于求平均??????????if(?scheme?==?_CV_SNAKE_GRAD?)??{???????//X方向上和Y方向上的Scoble梯度算子,用于求圖像的梯度,??//處理的圖像最大尺寸為TILE_SIZE+2,此例為12,算子半長為3即{-3,-2,-1,0,1,2,3}??//處理后的數據類型為16位符號數,分別存放在_dx,_dy矩陣中,長度為10??????????pX.init_deriv(?TILE_SIZE+2,?CV_8UC1,?CV_16SC1,?1,?0,?3?);??????????pY.init_deriv(?TILE_SIZE+2,?CV_8UC1,?CV_16SC1,?0,?1,?3?);?????????//圖像梯度存放緩沖區??????????gradient?=?(float?*)?cvAlloc(?roi.height?*?roi.width?*?sizeof(?float?));?????????????if(?!gradient?)??????????????return?CV_OUTOFMEM_ERR;?????????//map用于標志相應位置的分塊的圖像能量是否已經求得??????????map?=?(uchar?*)?cvAlloc(?map_width?*?map_height?);??????????if(?!map?)??????????{??????????????cvFree(?&gradient?);??????????????return?CV_OUTOFMEM_ERR;??????????}??????????/*?clear?map?-?no?gradient?computed?*/?????????//清除map標志??????????memset(?(void?*)?map,?0,?map_width?*?map_height?);??}??//各種能量的存放處,取每點的鄰域的能量??????Econt?=?(float?*)?cvAlloc(?neighbors?*?sizeof(?float?));??????Ecurv?=?(float?*)?cvAlloc(?neighbors?*?sizeof(?float?));??????Eimg?=?(float?*)?cvAlloc(?neighbors?*?sizeof(?float?));??????E?=?(float?*)?cvAlloc(?neighbors?*?sizeof(?float?));?????//開始迭代??????while(?!converged?)????//收斂標志無效時進行??????{??????????float?ave_d?=?0;??//輪廓各點的平均距離??????????int?moved?=?0;??????//輪廓變形時,發生移動的數量?????????????converged?=?0;???????//標志未收斂??????????iteration++;????????//更新迭代次數+1?????//計算輪廓中各點的平均距離??????????/*?compute?average?distance?*/????????//從點0到點n-1的距離和??????????for(?i?=?1;?i?<?n;?i++?)??????????{??????????????int?diffx?=?pt[i?-?1].x?-?pt[i].x;??????????????int?diffy?=?pt[i?-?1].y?-?pt[i].y;?????????????????ave_d?+=?cvSqrt(?(float)?(diffx?*?diffx?+?diffy?*?diffy)?);???????????}???????//再加上從點n-1到點0的距離,形成回路輪廓??????????ave_d?+=?cvSqrt(?(float)?((pt[0].x?-?pt[n?-?1].x)?*????????????????????????????????????(pt[0].x?-?pt[n?-?1].x)?+????????????????????????????????????(pt[0].y?-?pt[n?-?1].y)?*?(pt[0].y?-?pt[n?-?1].y)));??????//求平均,得出平均距離??????????ave_d?*=?invn;??????????/*?average?distance?computed?*/??????????????//對于每個輪廓點進行特定循環迭代求解??????????for(?i?=?0;?i?<?n;?i++?)??????????{??????????????/*?Calculate?Econt?*/????????????//初始化各個能量??????????????float?maxEcont?=?0;??????????????float?maxEcurv?=?0;??????????????float?maxEimg?=?0;??????????????float?minEcont?=?_CV_SNAKE_BIG;??????????????float?minEcurv?=?_CV_SNAKE_BIG;??????????????float?minEimg?=?_CV_SNAKE_BIG;??????????????float?Emin?=?_CV_SNAKE_BIG;???????????//初始化變形后輪廓點的偏移量??????????????int?offsetx?=?0;??????????????int?offsety?=?0;??????????????float?tmp;?????????????//計算邊界??????????????/*?compute?bounds?*/?????????????//計算合理的搜索邊界,以防領域搜索超過ROI圖像的范圍??????????????int?left?=?MIN(?pt[i].x,?win.width?>>?1?);??????????????int?right?=?MIN(?roi.width?-?1?-?pt[i].x,?win.width?>>?1?);??????????????int?upper?=?MIN(?pt[i].y,?win.height?>>?1?);??????????????int?bottom?=?MIN(?roi.height?-?1?-?pt[i].y,?win.height?>>?1?);????????????//初始化Econt??????????????maxEcont?=?0;??????????????minEcont?=?_CV_SNAKE_BIG;???????????//在合理的搜索范圍內進行Econt的計算??????????????for(?j?=?-upper;?j?<=?bottom;?j++?)??????????????{??????????????????for(?k?=?-left;?k?<=?right;?k++?)??????????????????{??????????????????????int?diffx,?diffy;??????????????????????float?energy;???????????????//在輪廓點集的首尾相接處作相應處理,求輪廓點差分??????????????????????if(?i?==?0?)??????????????????????{??????????????????????????diffx?=?pt[n?-?1].x?-?(pt[i].x?+?k);??????????????????????????diffy?=?pt[n?-?1].y?-?(pt[i].y?+?j);??????????????????????}??????????????????????else???????????????//在其他地方作一般處理?????????????????????????{??????????????????????????diffx?=?pt[i?-?1].x?-?(pt[i].x?+?k);??????????????????????????diffy?=?pt[i?-?1].y?-?(pt[i].y?+?j);??????????????????????}???????????????//將鄰域陳列坐標轉成Econt數組的下標序號,計算鄰域中每點的Econt????????????????//Econt的值等于平均距離和此點和上一點的距離的差的絕對值(這是怎么來的?)??????????????????????Econt[(j?+?centery)?*?win.width?+?k?+?centerx]?=?energy?=??????????????????????????(float)?fabs(?ave_d?-????????????????????????????????????????cvSqrt(?(float)?(diffx?*?diffx?+?diffy?*?diffy)?));???????????????//求出所有鄰域點中的Econt的最大值和最小值??????????????????????maxEcont?=?MAX(?maxEcont,?energy?);??????????????????????minEcont?=?MIN(?minEcont,?energy?);??????????????????}??????????????}?????????????//求出鄰域點中最大值和最小值之差,并對所有的鄰域點的Econt進行標準歸一化,若最大值最小?????????????//相等,則鄰域中的點Econt全相等,Econt歸一化束縛為0??????????????tmp?=?maxEcont?-?minEcont;??????????????tmp?=?(tmp?==?0)???0?:?(1?/?tmp);??????????????for(?k?=?0;?k?<?neighbors;?k++?)??????????????{??????????????????Econt[k]?=?(Econt[k]?-?minEcont)?*?tmp;??????????????}???????????????????//計算每點的Ecurv??????????????/*??Calculate?Ecurv?*/??????????????maxEcurv?=?0;??????????????minEcurv?=?_CV_SNAKE_BIG;??????????????for(?j?=?-upper;?j?<=?bottom;?j++?)??????????????{??????????????????for(?k?=?-left;?k?<=?right;?k++?)??????????????????{??????????????????????int?tx,?ty;??????????????????????float?energy;??????????????????????//第一個點的二階差分??????????????????????if(?i?==?0?)??????????????????????{??????????????????????????tx?=?pt[n?-?1].x?-?2?*?(pt[i].x?+?k)?+?pt[i?+?1].x;??????????????????????????ty?=?pt[n?-?1].y?-?2?*?(pt[i].y?+?j)?+?pt[i?+?1].y;??????????????????????}?????????????????????//最后一個點的二階差分??????????????????????else?if(?i?==?n?-?1?)??????????????????????{??????????????????????????tx?=?pt[i?-?1].x?-?2?*?(pt[i].x?+?k)?+?pt[0].x;??????????????????????????ty?=?pt[i?-?1].y?-?2?*?(pt[i].y?+?j)?+?pt[0].y;??????????????????????}?????????????????????//其余點的二階差分??????????????????????else??????????????????????{??????????????????????????tx?=?pt[i?-?1].x?-?2?*?(pt[i].x?+?k)?+?pt[i?+?1].x;??????????????????????????ty?=?pt[i?-?1].y?-?2?*?(pt[i].y?+?j)?+?pt[i?+?1].y;??????????????????????}????????????????????//轉換坐標為數組序號,并求各點的Ecurv的值,二階差分后取平方??????????????????????Ecurv[(j?+?centery)?*?win.width?+?k?+?centerx]?=?energy?=??????????????????????????(float)?(tx?*?tx?+?ty?*?ty);????????????????????//取最小的Ecurv和最大的Ecurv??????????????????????maxEcurv?=?MAX(?maxEcurv,?energy?);??????????????????????minEcurv?=?MIN(?minEcurv,?energy?);??????????????????}??????????????}?????????????????//對Ecurv進行標準歸一化??????????????tmp?=?maxEcurv?-?minEcurv;??????????????tmp?=?(tmp?==?0)???0?:?(1?/?tmp);??????????????for(?k?=?0;?k?<?neighbors;?k++?)??????????????{??????????????????Ecurv[k]?=?(Ecurv[k]?-?minEcurv)?*?tmp;??????????????}????????????????????????//求Eimg??????????????/*?Calculate?Eimg?*/??????????????for(?j?=?-upper;?j?<=?bottom;?j++?)??????????????{??????????????????for(?k?=?-left;?k?<=?right;?k++?)??????????????????{??????????????????????float?energy;?????????????????//若采用灰度梯度數據??????????????????????if(?scheme?==?_CV_SNAKE_GRAD?)??????????????????????{??????????????????????????/*?look?at?map?and?check?status?*/??????????????????????????int?x?=?(pt[i].x?+?k)/WTILE_SIZE;??????????????????????????int?y?=?(pt[i].y?+?j)/WTILE_SIZE;??????????????????????????//若此處的圖像能量還沒有獲取,則對此處對應的圖像分塊進行圖像能量的求解??????????????????????????if(?map[y?*?map_width?+?x]?==?0?)??????????????????????????{??????????????????????????????int?l,?m;????????????????????????????????????????????????????????????/*?evaluate?block?location?*/?????????????????????????????//計算要進行梯度算子處理的圖像塊的位置??????????????????????????????int?upshift?=?y???1?:?0;??????????????????????????????int?leftshift?=?x???1?:?0;??????????????????????????????int?bottomshift?=?MIN(?1,?roi.height?-?(y?+?1)*WTILE_SIZE?);??????????????????????????????int?rightshift?=?MIN(?1,?roi.width?-?(x?+?1)*WTILE_SIZE?);????????????????????????????//圖像塊的位置大小(由于原ROI不一定是8的倍數,所以圖像塊會大小不一)??????????????????????????????CvRect?g_roi?=?{?x*WTILE_SIZE?-?leftshift,?y*WTILE_SIZE?-?upshift,??????????????????????????????????leftshift?+?WTILE_SIZE?+?rightshift,?upshift?+?WTILE_SIZE?+?bottomshift?};??????????????????????????????CvMat?_src1;??????????????????????????????cvGetSubArr(?&_src,?&_src1,?g_roi?);??//得到圖像塊的數據??????????????????????????????//分別對圖像的X方向和Y方向進行梯度算子??????????????????????????????pX.process(?&_src1,?&_dx?);??????????????????????????????pY.process(?&_src1,?&_dy?);???????????????????????????//求分塊區域中的每個點的梯度??????????????????????????????for(?l?=?0;?l?<?WTILE_SIZE?+?bottomshift;?l++?)??????????????????????????????{??????????????????????????????????for(?m?=?0;?m?<?WTILE_SIZE?+?rightshift;?m++?)??????????????????????????????????{??????????????????????????????????????gradient[(y*WTILE_SIZE?+?l)?*?roi.width?+?x*WTILE_SIZE?+?m]?=??????????????????????????????????????????(float)?(dx[(l?+?upshift)?*?TILE_SIZE?+?m?+?leftshift]?*???????????????????????????????????????????????????dx[(l?+?upshift)?*?TILE_SIZE?+?m?+?leftshift]?+???????????????????????????????????????????????????dy[(l?+?upshift)?*?TILE_SIZE?+?m?+?leftshift]?*???????????????????????????????????????????????????dy[(l?+?upshift)?*?TILE_SIZE?+?m?+?leftshift]);??????????????????????????????????}??????????????????????????????}??????????????????????????????//map相應位置置1表示此處圖像能量已經獲取??????????????????????????????map[y?*?map_width?+?x]?=?1;??????????????????????????}????????????????????????//以梯度數據作為圖像能量??????????????????????????Eimg[(j?+?centery)?*?win.width?+?k?+?centerx]?=?energy?=??????????????????????????????gradient[(pt[i].y?+?j)?*?roi.width?+?pt[i].x?+?k];??????????????????????}??????????????????????else??????????????????????{?????????????????????????//以灰度作為圖像能量??????????????????????????Eimg[(j?+?centery)?*?win.width?+?k?+?centerx]?=?energy?=??????????????????????????????src[(pt[i].y?+?j)?*?srcStep?+?pt[i].x?+?k];??????????????????????}?????????????????????//獲得鄰域中最大和最小的圖像能量??????????????????????maxEimg?=?MAX(?maxEimg,?energy?);??????????????????????minEimg?=?MIN(?minEimg,?energy?);??????????????????}??????????????}????????????????//Eimg的標準歸一化??????????????tmp?=?(maxEimg?-?minEimg);??????????????tmp?=?(tmp?==?0)???0?:?(1?/?tmp);?????????????????for(?k?=?0;?k?<?neighbors;?k++?)??????????????{??????????????????Eimg[k]?=?(minEimg?-?Eimg[k])?*?tmp;??????????????}??????????????//加入系數??????????????/*?locate?coefficients?*/??????????????if(?coeffUsage?==?CV_VALUE)??????????????{??????????????????_alpha?=?*alpha;??????????????????_beta?=?*beta;??????????????????_gamma?=?*gamma;??????????????}??????????????else??????????????{????????????????????????????????????_alpha?=?alpha[i];??????????????????_beta?=?beta[i];??????????????????_gamma?=?gamma[i];??????????????}?????????????????/*?Find?Minimize?point?in?the?neighbors?*/??????????????//求得每個鄰域點的Snake能量??????????????for(?k?=?0;?k?<?neighbors;?k++?)??????????????{??????????????????E[k]?=?_alpha?*?Econt[k]?+?_beta?*?Ecurv[k]?+?_gamma?*?Eimg[k];??????????????}??????????????Emin?=?_CV_SNAKE_BIG;??????????//獲取最小的能量,以及對應的鄰域中的相對位置??????????????for(?j?=?-upper;?j?<=?bottom;?j++?)??????????????{??????????????????for(?k?=?-left;?k?<=?right;?k++?)??????????????????{?????????????????????????if(?E[(j?+?centery)?*?win.width?+?k?+?centerx]?<?Emin?)??????????????????????{??????????????????????????Emin?=?E[(j?+?centery)?*?win.width?+?k?+?centerx];??????????????????????????offsetx?=?k;??????????????????????????offsety?=?j;??????????????????????}??????????????????}??????????????}???????????//如果輪廓點發生改變,則記得移動次數??????????????if(?offsetx?||?offsety?)??????????????{??????????????????pt[i].x?+=?offsetx;??????????????????pt[i].y?+=?offsety;??????????????????moved++;??????????????}??????????}???????????//各個輪廓點迭代計算完成后,如果沒有移動的點了,則收斂標志位有效,停止迭代??????????converged?=?(moved?==?0);???????//達到最大迭代次數時,收斂標志位有效,停止迭代??????????if(?(criteria.type?&?CV_TERMCRIT_ITER)?&&?(iteration?>=?criteria.max_iter)?)??????????????converged?=?1;????//到大相應精度時,停止迭代(與第一個條件有相同效果)??????????if(?(criteria.type?&?CV_TERMCRIT_EPS)?&&?(moved?<=?criteria.epsilon)?)??????????????converged?=?1;??????}???????//釋放各個緩沖區??????cvFree(?&Econt?);??????cvFree(?&Ecurv?);??????cvFree(?&Eimg?);??????cvFree(?&E?);?????????if(?scheme?==?_CV_SNAKE_GRAD?)??????{??????????cvFree(?&gradient?);??????????cvFree(?&map?);??????}??????return?CV_OK;??}????????CV_IMPL?void??cvSnakeImage(?const?IplImage*?src,?CvPoint*?points,????????????????int?length,?float?*alpha,????????????????float?*beta,?float?*gamma,????????????????int?coeffUsage,?CvSize?win,????????????????CvTermCriteria?criteria,?int?calcGradient?)??{?????????CV_FUNCNAME(?"cvSnakeImage"?);?????????__BEGIN__;?????????uchar?*data;??????CvSize?size;??????int?step;?????????if(?src->nChannels?!=?1?)??????????CV_ERROR(?CV_BadNumChannels,?"input?image?has?more?than?one?channel"?);?????????if(?src->depth?!=?IPL_DEPTH_8U?)??????????CV_ERROR(?CV_BadDepth,?cvUnsupportedFormat?);?????????cvGetRawData(?src,?&data,?&step,?&size?);?????????IPPI_CALL(?icvSnake8uC1R(?data,?step,?size,?points,?length,????????????????????????????????alpha,?beta,?gamma,?coeffUsage,?win,?criteria,????????????????????????????????calcGradient???_CV_SNAKE_GRAD?:?_CV_SNAKE_IMAGE?));??????__END__;??}?????/*?end?of?file?*/?????????????????測試應用程序?????#include?"stdafx.h"??#include?<iostream>??#include?<string.h>??#include?<cxcore.h>??#include?<cv.h>??#include?<highgui.h>??#include?<fstream>????????IplImage?*image?=?0?;?//原始圖像??IplImage?*image2?=?0?;?//原始圖像copy?????using?namespace?std;??int?Thresholdness?=?141;??int?ialpha?=?20;??int?ibeta=20;??int?igamma=20;?????void?onChange(int?pos)??{???????????if(image2)?cvReleaseImage(&image2);??????if(image)?cvReleaseImage(&image);?????????image2?=?cvLoadImage("grey.bmp",1);?//顯示圖片??????image=?cvLoadImage("grey.bmp",0);?????????cvThreshold(image,image,Thresholdness,255,CV_THRESH_BINARY);?//分割域值????????????CvMemStorage*?storage?=?cvCreateMemStorage(0);??????CvSeq*?contours?=?0;?????????cvFindContours(?image,?storage,?&contours,?sizeof(CvContour),?//尋找初始化輪廓??????????CV_RETR_EXTERNAL?,?CV_CHAIN_APPROX_SIMPLE?);?????????if(!contours)?return?;??????int?length?=?contours->total;?????????if(length<10)?return?;??????CvPoint*?point?=?new?CvPoint[length];?//分配輪廓點?????????CvSeqReader?reader;??????CvPoint?pt=?cvPoint(0,0);;?????????CvSeq?*contour2=contours;????????????cvStartReadSeq(contour2,?&reader);??????for?(int?i?=?0;?i?<?length;?i++)??????{??????????CV_READ_SEQ_ELEM(pt,?reader);??????????point[i]=pt;??????}??????cvReleaseMemStorage(&storage);?????????//顯示輪廓曲線??????for(int?i=0;i<length;i++)??????{??????????int?j?=?(i+1)%length;??????????cvLine(?image2,?point[i],point[j],CV_RGB(?0,?0,?255?),1,8,0?);??????}?????????float?alpha=ialpha/100.0f;??????float?beta=ibeta/100.0f;??????float?gamma=igamma/100.0f;?????????CvSize?size;??????size.width=3;??????size.height=3;??????CvTermCriteria?criteria;??????criteria.type=CV_TERMCRIT_ITER;??????criteria.max_iter=1000;??????criteria.epsilon=0.1;??????cvSnakeImage(?image,?point,length,&alpha,&beta,&gamma,CV_VALUE,size,criteria,0?);?????????//顯示曲線??????for(int?i=0;i<length;i++)??????{??????????int?j?=?(i+1)%length;??????????cvLine(?image2,?point[i],point[j],CV_RGB(?0,?255,?0?),1,8,0?);??????}??????delete?[]point;?????}?????int?main(int?argc,?char*?argv[])??{??????????????cvNamedWindow("win1",0);??????cvCreateTrackbar("Thd",?"win1",?&Thresholdness,?255,?onChange);??????cvCreateTrackbar("alpha",?"win1",?&ialpha,?100,?onChange);??????cvCreateTrackbar("beta",?"win1",?&ibeta,?100,?onChange);??????cvCreateTrackbar("gamma",?"win1",?&igamma,?100,?onChange);??????cvResizeWindow("win1",300,500);??????onChange(0);?????????for(;;)??????{??????????if(cvWaitKey(40)==27)?break;??????????cvShowImage("win1",image2);??????}???????????return?0;??}?
?
?
?
本文由以下兩篇博客整理得到:
http://blog.csdn.net/lbd2008/article/details/7180506
http://chyyeng.blog.163.com/blog/static/16918230201272724529214/
總結
以上是生活随笔為你收集整理的主动轮廓线模型Snake模型简介openCV中cvSnakeImage()函数代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。