cvsnakeimage(主动轮廓线模型Snake模型简介&openCV中cvSnakeImage函数代码分析「终于解决」)

发布时间:2025-12-10 19:33:39 浏览次数:3

主动轮廓线模型Snake模型简介&openCV中cvSnakeImage函数代码分析「终于解决」-

主动轮廓线模型Snake模型简介&openCV中cvSnakeImage()函数代码分析「终于解决」主动轮廓线模型又称为Snake模型,自Kass于1987年提出以来,已广泛应用于数字图像分析和计算机视觉领域。由于Snake模型具有良好的提取和跟踪特定区域内目标轮廓的能力,因此非常适合于医学图像如CT和MR图像的处理,以获得特定器官及组织的轮廓。简单的来讲,Snake模型就是一条可变形的参数曲线及相应的能量函数,以最小化能量目标函数为目标,控制参数曲线变形,具有最小能量的闭合曲线就是目标轮廓。Snake模型具有一些经典方法所无法比拟的优点:图像数据、初始估计、目标轮廓及基于知识的约束统一于一个过程中;经

主动轮廓线模型又称为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 plaincopy

/*M///////IMPORTANT:READBEFOREDOWNLOADING,COPYING,INSTALLINGORUSING.////Bydownloading,copying,installingorusingthesoftwareyouagreetothislicense.//Ifyoudonotagreetothislicense,donotdownload,install,//copyorusethesoftware.//////IntelLicenseAgreement//ForOpenSourceComputerVisionLibrary////Copyright(C)2000,IntelCorporation,allrightsreserved.//Thirdpartycopyrightsarepropertyoftheirrespectiveowners.////Redistributionanduseinsourceandbinaryforms,withorwithoutmodification,//arepermittedprovidedthatthefollowingconditionsaremet:////*Redistribution’sofsourcecodemustretaintheabovecopyrightnotice,//thislistofconditionsandthefollowingdisclaimer.////*Redistribution’sinbinaryformmustreproducetheabovecopyrightnotice,//thislistofconditionsandthefollowingdisclaimerinthedocumentation//and/orothermaterialsprovidedwiththedistribution.////*ThenameofIntelCorporationmaynotbeusedtoendorseorpromoteproducts//derivedfromthissoftwarewithoutspecificpriorwrittenpermission.////Thissoftwareisprovidedbythecopyrightholdersandcontributors“asis”and//anyexpressorimpliedwarranties,including,butnotlimitedto,theimplied//warrantiesofmerchantabilityandfitnessforaparticularpurposearedisclaimed.//InnoeventshalltheIntelCorporationorcontributorsbeliableforanydirect,//indirect,incidental,special,exemplary,orconsequentialdamages//(including,butnotlimitedto,procurementofsubstitutegoodsorservices;//lossofuse,data,orprofits;orbusinessinterruption)howevercaused//andonanytheoryofliability,whetherincontract,strictliability,//ortort(includingnegligenceorotherwise)arisinginanywayoutof//theuseofthissoftware,evenifadvisedofthepossibilityofsuchdamage.////M*/#include“_cv.h”#define_CV_SNAKE_BIG2.e+38f#define_CV_SNAKE_IMAGE1#define_CV_SNAKE_GRAD2/*F/////Name:icvSnake8uC1R//Purpose://Context://Parameters://src–sourceimage,//srcStep–itsstepinbytes,//roi–sizeofROI,//pt–pointertosnakepointsarray//n–sizeofpointsarray,//alpha–pointertocoefficientofcontinuityenergy,//beta–pointertocoefficientofcurvatureenergy,//gamma–pointertocoefficientofimageenergy,//coeffUsage–ifCV_VALUE–alpha,beta,gammapointtosinglevalue//ifCV_MATAY–pointtoarrays//criteria–terminationcriteria.//scheme–imageenergyscheme//if_CV_SNAKE_IMAGE–imageintensityisenergy//if_CV_SNAKE_GRAD–magnitudeofgradientisenergy//Returns://F*/staticCvStatusicvSnake8uC1R(unsignedchar*src,//原始图像数据intsrcStep,//每行的字节数CvSizeroi,//图像尺寸CvPoint*pt,//轮廓点(变形对象)intn,//轮廓点的个数float*alpha,//指向α的指针,α可以是单个值,也可以是与轮廓点个数一致的数组float*beta,//β的值,同αfloat*gamma,//γ的值,同αintcoeffUsage,//确定αβγ是用作单个值还是个数组CvSizewin,//每个点用于搜索的最小的领域大小,宽度为奇数CvTermCriteriacriteria,//递归迭代终止的条件准则intscheme)//确定图像能量场的数据选择,1为灰度,2为灰度梯度{inti,j,k;intneighbors=win.height*win.width;//当前点领域中点的个数//当前点的位置intcenterx=win.width>>1;intcentery=win.height>>1;floatinvn;//n的倒数?intiteration=0;//迭代次数intconverged=0;//收敛标志,0为非收敛//能量float*Econt;//float*Ecurv;//轮廓曲线能量float*Eimg;//图像能量float*E;////αβγ的副本float_alpha,_beta,_gamma;/*#ifdefGRAD_SNAKE*/float*gradient=NULL;uchar*map=NULL;intmap_width=((roi.width–1)>>3)+1;intmap_height=((roi.height–1)>>3)+1;CvSepFilterpX,pY;#defineWTILE_SIZE8#defineTILE_SIZE(WTILE_SIZE+2)shortdx[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);/*innerbufferofconvolutionprocess*///charConvBuffer[400];/*#endif*///检点参数的合理性/*checkbadarguments*/if(src==NULL)returnCV_NULLPTR_ERR;if((roi.height<=0)||(roi.width<=0))returnCV_BADSIZE_ERR;if(srcStep<roi.width)returnCV_BADSIZE_ERR;if(pt==NULL)returnCV_NULLPTR_ERR;if(n<3)//轮廓点至少要三个returnCV_BADSIZE_ERR;if(alpha==NULL)returnCV_NULLPTR_ERR;if(beta==NULL)returnCV_NULLPTR_ERR;if(gamma==NULL)returnCV_NULLPTR_ERR;if(coeffUsage!=CV_VALUE&&coeffUsage!=CV_ARRAY)returnCV_BADFLAG_ERR;if((win.height<=0)||(!(win.height&1)))//邻域搜索窗口得是奇数returnCV_BADSIZE_ERR;if((win.width<=0)||(!(win.width&1)))returnCV_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矩阵中,长度为10pX.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)returnCV_OUTOFMEM_ERR;//map用于标志相应位置的分块的图像能量是否已经求得map=(uchar*)cvAlloc(map_width*map_height);if(!map){cvFree(&gradient);returnCV_OUTOFMEM_ERR;}/*clearmap–nogradientcomputed*///清除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)//收敛标志无效时进行{floatave_d=0;//轮廓各点的平均距离intmoved=0;//轮廓变形时,发生移动的数量converged=0;//标志未收敛iteration++;//更新迭代次数+1//计算轮廓中各点的平均距离/*computeaveragedistance*///从点0到点n-1的距离和for(i=1;i<n;i++){intdiffx=pt[i–1].x–pt[i].x;intdiffy=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;/*averagedistancecomputed*///对于每个轮廓点进行特定循环迭代求解for(i=0;i<n;i++){/*CalculateEcont*///初始化各个能量floatmaxEcont=0;floatmaxEcurv=0;floatmaxEimg=0;floatminEcont=_CV_SNAKE_BIG;floatminEcurv=_CV_SNAKE_BIG;floatminEimg=_CV_SNAKE_BIG;floatEmin=_CV_SNAKE_BIG;//初始化变形后轮廓点的偏移量intoffsetx=0;intoffsety=0;floattmp;//计算边界/*computebounds*///计算合理的搜索边界,以防领域搜索超过ROI图像的范围intleft=MIN(pt[i].x,win.width>>1);intright=MIN(roi.width–1–pt[i].x,win.width>>1);intupper=MIN(pt[i].y,win.height>>1);intbottom=MIN(roi.height–1–pt[i].y,win.height>>1);//初始化EcontmaxEcont=0;minEcont=_CV_SNAKE_BIG;//在合理的搜索范围内进行Econt的计算for(j=-upper;j<=bottom;j++){for(k=-left;k<=right;k++){intdiffx,diffy;floatenergy;//在轮廓点集的首尾相接处作相应处理,求轮廓点差分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归一化束缚为0tmp=maxEcont–minEcont;tmp=(tmp==0)?0:(1/tmp);for(k=0;k<neighbors;k++){Econt[k]=(Econt[k]–minEcont)*tmp;}//计算每点的Ecurv/*CalculateEcurv*/maxEcurv=0;minEcurv=_CV_SNAKE_BIG;for(j=-upper;j<=bottom;j++){for(k=-left;k<=right;k++){inttx,ty;floatenergy;//第一个点的二阶差分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;}//最后一个点的二阶差分elseif(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和最大的EcurvmaxEcurv=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/*CalculateEimg*/for(j=-upper;j<=bottom;j++){for(k=-left;k<=right;k++){floatenergy;//若采用灰度梯度数据if(scheme==_CV_SNAKE_GRAD){/*lookatmapandcheckstatus*/intx=(pt[i].x+k)/WTILE_SIZE;inty=(pt[i].y+j)/WTILE_SIZE;//若此处的图像能量还没有获取,则对此处对应的图像分块进行图像能量的求解if(map[y*map_width+x]==0){intl,m;/*evaluateblocklocation*///计算要进行梯度算子处理的图像块的位置intupshift=y?1:0;intleftshift=x?1:0;intbottomshift=MIN(1,roi.height–(y+1)*WTILE_SIZE);intrightshift=MIN(1,roi.width–(x+1)*WTILE_SIZE);//图像块的位置大小(由于原ROI不一定是8的倍数,所以图像块会大小不一)CvRectg_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;}//加入系数/*locatecoefficients*/if(coeffUsage==CV_VALUE){_alpha=*alpha;_beta=*beta;_gamma=*gamma;}else{_alpha=alpha[i];_beta=beta[i];_gamma=gamma[i];}/*FindMinimizepointintheneighbors*///求得每个邻域点的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);}returnCV_OK;}CV_IMPLvoidcvSnakeImage(constIplImage*src,CvPoint*points,intlength,float*alpha,float*beta,float*gamma,intcoeffUsage,CvSizewin,CvTermCriteriacriteria,intcalcGradient){CV_FUNCNAME(“cvSnakeImage”);__BEGIN__;uchar*data;CvSizesize;intstep;if(src->nChannels!=1)CV_ERROR(CV_BadNumChannels,“inputimagehasmorethanonechannel”);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__;}/*endoffile*/测试应用程序#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;//原始图像copyusingnamespacestd;intThresholdness=141;intialpha=20;intibeta=20;intigamma=20;voidonChange(intpos){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;intlength=contours->total;if(length<10)return;CvPoint*point=newCvPoint[length];//分配轮廓点CvSeqReaderreader;CvPointpt=cvPoint(0,0);;CvSeq*contour2=contours;cvStartReadSeq(contour2,&reader);for(inti=0;i<length;i++){CV_READ_SEQ_ELEM(pt,reader);point[i]=pt;}cvReleaseMemStorage(&storage);//显示轮廓曲线for(inti=0;i<length;i++){intj=(i+1)%length;cvLine(image2,point[i],point[j],CV_RGB(0,0,255),1,8,0);}floatalpha=ialpha/100.0f;floatbeta=ibeta/100.0f;floatgamma=igamma/100.0f;CvSizesize;size.width=3;size.height=3;CvTermCriteriacriteria;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(inti=0;i<length;i++){intj=(i+1)%length;cvLine(image2,point[i],point[j],CV_RGB(0,255,0),1,8,0);}delete[]point;}intmain(intargc,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);}return0;}

本文由以下两篇博客整理得到:

http://blog.csdn.net/lbd2008/article/details/7180506

http://chyyeng.blog.163.com/blog/static/16918230201272724529214/

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477