精华内容
下载资源
问答
  • SLIC 算法

    2014-03-28 13:15:00
    读书笔记: http://blog.csdn.net/wishchin/article/details/17415785 转载于:https://www.cnblogs.com/muxiaoruo/p/3630438.html

    读书笔记:  http://blog.csdn.net/wishchin/article/details/17415785

     

    转载于:https://www.cnblogs.com/muxiaoruo/p/3630438.html

    展开全文
  • 超像素SLIC算法源码阅读

    千次阅读 2019-04-30 15:48:50
    超像素SLIC算法源码阅读超像素SLIC算法源码阅读SLIC简介源码阅读实验结果 超像素SLIC算法源码阅读 SLIC简介 SLIC的全称Simple Linear Iterative Clustering,即简单线性迭代聚类,论文和代码链接如下: 论文传送...

    超像素SLIC算法源码阅读

    SLIC简介

    SLIC的全称Simple Linear Iterative Clustering,即简单线性迭代聚类,论文和代码链接如下:
    论文传送门:SLIC Superpixels Comparedto State-of-the-Art Superpixel Methods
    代码传送门:superpixels-revisited,这个Github项目是Superpixels: An Evaluation of the State-of-the-Art的作者的开源项目,对比了几乎所有有开源代码的超像素算法,本文的代码就是来自于此。

    超像素的目的一般是为了降低后续图像处理的复杂度,SLIC定义的好的超像素算法要求是:(1)超像素的边界能够粘附图像边界;(2)计算速度应该快:(3)用于分割时应该能够提高分割质量

    SLIC的算法流程如下:
    (1)将图像分割成S×SS×S个格子,每个格子中心生成一个聚类中心
    (2)将聚类中心移动到格子中心的八邻域像素中梯度最低的像素位置(避免将超像素定义在边缘上,减少噪声接种的机会)
    (3)合并每一个像素和最近的聚类中心(这一步和kmeans不同的是,这里是遍历每一个聚类中心周围2S2S区域的像素点,而kmeans的方法是直接求距离某个像素最近的聚类中心,这种方法可以减少计算量同时使得算法复杂度与像素数量无关(这里我其实不太理解))。这里距离DD的定义是lab颜色空间和xy坐标空间五维空间的加权距离de=(ljli)2+(ajai)2+(bjbi)2d_e=\sqrt{(l_j-l_i)^2+(a_j-a_i)^2+(b_j-b_i)^2}ds=(xjxi)2+(yjyi)2d_s=\sqrt{(x_j-x_i)^2+(y_j-y_i)^2}D=de2+Nds2D=\sqrt{d_e^2+Nd_s^2}
    (4)遍历所有聚类中心后,重新跟新每个聚类中心的位置(这里和kmeans算法一致了),然后 进行迭代
    (5)迭代完成后进行连通域加强并且去除面积过小的超像素


    源码阅读

    函数DoSuperpixelSegmentation_ForGivenSuperpixelSize是主程序,其步骤大概就是将RGB图像转到Lab色域——计算Lab色域的梯度——初始化网格中心——将聚类中心初始化到网格中心的八邻域像素——聚类迭代——对联通域加强。

    void SLIC::DoSuperpixelSegmentation_ForGivenSuperpixelSize(
        const unsigned int*	      ubuff,//原始图像
    	const int	              width,//宽度
    	const int	              height,//高度
    	int*&	                  klabels,//输出的label,其实记录的是边界的位
    	int&	                  numlabels,//输出的label的数量
        const int&	              superpixelsize,//一个label的像素大小
        const double&             compactness,//紧密度的参数
        const bool&               perturbseeds,//注意这是个bool变量
        const int                 iterations)//迭代次数
    {
        //------------------------------------------------
        const int STEP = sqrt(double(superpixelsize))+0.5;//步长大小,其实就是画网格时,网格的间距
        //------------------------------------------------
    	vector<double> kseedsl(0);
    	vector<double> kseedsa(0);
    	vector<double> kseedsb(0);
    	vector<double> kseedsx(0);
    	vector<double> kseedsy(0);
    
    	//--------------------------------------------------
    	m_width  = width;
    	m_height = height;
    	int sz = m_width*m_height;//整个像素大小
    	//klabels.resize( sz, -1 );
    	//--------------------------------------------------
    	klabels = new int[sz];//一个数组
    	for( int s = 0; s < sz; s++ ) klabels[s] = -1;
        //--------------------------------------------------
        if(1)//LAB, the default option
        {
            DoRGBtoLABConversion(ubuff, m_lvec, m_avec, m_bvec);//将颜色空间从RGB转到Lab
        }
        else//RGB
        {
            m_lvec = new double[sz]; m_avec = new double[sz]; m_bvec = new double[sz];
            for( int i = 0; i < sz; i++ )
            {
                    m_lvec[i] = ubuff[i] >> 16 & 0xff;
                    m_avec[i] = ubuff[i] >>  8 & 0xff;
                    m_bvec[i] = ubuff[i]       & 0xff;
            }
        }
    	//--------------------------------------------------
    	vector<double> edgemag(0);
    	if(perturbseeds) DetectLabEdges(m_lvec, m_avec, m_bvec, m_width, m_height, edgemag);//求颜色的边界,perturb是干扰的意思,这个是用来求八领域里面最小梯度的梯度的梯度值就是从这里来的
    
    	//kseedsl这些值放进去之前都是零,运行完这个函数之后就是有值的了
    	GetLABXYSeeds_ForGivenStepSize(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, STEP, perturbseeds, edgemag);//这个应该是初始化的过程,建立网格,并且在网格中心的八领域里面寻找seed
    
    
    	PerformSuperpixelSLIC(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, klabels, STEP, edgemag, compactness, iterations);//基于Kmeans算法进行迭代
    	numlabels = kseedsl.size();
    
    	int* nlabels = new int[sz];
    	EnforceLabelConnectivity(klabels, m_width, m_height, nlabels, numlabels, double(sz)/double(STEP*STEP));
    	{for(int i = 0; i < sz; i++ ) klabels[i] = nlabels[i];}
    	if(nlabels) delete [] nlabels;
    }
    

    函数GetLABXYSeeds_ForGivenStepSize主要是完成了网格中心的初始化

    void SLIC::GetLABXYSeeds_ForGivenStepSize(
    	vector<double>&				kseedsl,
    	vector<double>&				kseedsa,
    	vector<double>&				kseedsb,
    	vector<double>&				kseedsx,
    	vector<double>&				kseedsy,
        const int&					STEP,//STEP这个值指的是网格间的间距是多少,即一步可以跨多长
        const bool&					perturbseeds,
        const vector<double>&       edgemag)
    {
        const bool hexgrid = false;
    	int numseeds(0);
    	int n(0);
    
    	//int xstrips = m_width/STEP;
    	//int ystrips = m_height/STEP;
    	int xstrips = (0.5+double(m_width)/double(STEP));//这里指的是有多少个步长,+0.5是为了进行四舍五入
    	int ystrips = (0.5+double(m_height)/double(STEP));
    
        int xerr = m_width  - STEP*xstrips;if(xerr < 0){xstrips--;xerr = m_width - STEP*xstrips;}//划分网格的时候有可能会多一步,需要保证网格划分在图像内
        int yerr = m_height - STEP*ystrips;if(yerr < 0){ystrips--;yerr = m_height- STEP*ystrips;}
    
    	double xerrperstrip = double(xerr)/double(xstrips);//每一步所包含的误差是多少
    	double yerrperstrip = double(yerr)/double(ystrips);
    
    	int xoff = STEP/2;//这个应是用来寻找中心点的位置
    	int yoff = STEP/2;
    	//-------------------------
    	numseeds = xstrips*ystrips;//一共会有多少个种子点
    	//-------------------------
    	kseedsl.resize(numseeds);
    	kseedsa.resize(numseeds);
    	kseedsb.resize(numseeds);
    	kseedsx.resize(numseeds);
    	kseedsy.resize(numseeds);
    
    	for( int y = 0; y < ystrips; y++ )
    	{
    		int ye = y*yerrperstrip;
    		for( int x = 0; x < xstrips; x++ )
    		{
    			int xe = x*xerrperstrip;//到这个格子累计x的误差
                int seedx = (x*STEP+xoff+xe);//确定准确的每个种子点的x的位置
                if(hexgrid){ seedx = x*STEP+(xoff<<(y&0x1))+xe; seedx = min(m_width-1,seedx); }//for hex grid sampling
                int seedy = (y*STEP+yoff+ye);
                int i = seedy*m_width + seedx;//转化成在整个坐标中的位置
    			
    			kseedsl[n] = m_lvec[i];
    			kseedsa[n] = m_avec[i];
    			kseedsb[n] = m_bvec[i];
                kseedsx[n] = seedx;
                kseedsy[n] = seedy;
    			n++;
    		}
    	}
    
    	
    	if(perturbseeds)
    	{
    		PerturbSeeds(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, edgemag);
    	}
    }
    

    函数PerturbSeeds是将聚类中心调整到8邻域像素中梯度最小的像素上,
    void SLIC::PerturbSeeds(
    	vector<double>&				kseedsl,
    	vector<double>&				kseedsa,
    	vector<double>&				kseedsb,
    	vector<double>&				kseedsx,
    	vector<double>&				kseedsy,
        const vector<double>&       edges)
    {
    	const int dx8[8] = {-1, -1,  0,  1, 1, 1, 0, -1};//像素的八领域
    	const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1,  1};
    	
    	int numseeds = kseedsl.size();//点的数量
    
    	for( int n = 0; n < numseeds; n++ )
    	{
    		int ox = kseedsx[n];//original x
    		int oy = kseedsy[n];//original y
    		int oind = oy*m_width + ox;//转化成数组里面的下标
    
    		int storeind = oind;
    		for( int i = 0; i < 8; i++ )
    		{
    			int nx = ox+dx8[i];//new x
    			int ny = oy+dy8[i];//new y
    
    			if( nx >= 0 && nx < m_width && ny >= 0 && ny < m_height)
    			{
    				int nind = ny*m_width + nx;
    				if( edges[nind] < edges[storeind])
    				{
    					storeind = nind;//记录最小梯度的位置
    				}
    			}
    		}
    		if(storeind != oind)
    		{
    			kseedsx[n] = storeind%m_width;
    			kseedsy[n] = storeind/m_width;
    			kseedsl[n] = m_lvec[storeind];
    			kseedsa[n] = m_avec[storeind];
    			kseedsb[n] = m_bvec[storeind];
    		}
    	}
    }
    

    函数PerformSuperpixelSLIC是完成整个迭代过程,其实就是一个kmeans算法的实现过程

    void SLIC::PerformSuperpixelSLIC(
    	vector<double>&				kseedsl,
    	vector<double>&				kseedsa,
    	vector<double>&				kseedsb,
    	vector<double>&				kseedsx,
    	vector<double>&				kseedsy,
            int*&					klabels,
            const int&				STEP,
        const vector<double>&       edgemag,
    	const double&				M,
            const int               iterations)
    {
    	int sz = m_width*m_height;
    	const int numk = kseedsl.size();
    	//----------------
    	int offset = STEP;//网络线之间的间距
            //if(STEP < 8) offset = STEP*1.5;//to prevent a crash due to a very small step size
    	//----------------
    	
    	vector<double> clustersize(numk, 0);
    	vector<double> inv(numk, 0);//to store 1/clustersize[k] values
    
    	vector<double> sigmal(numk, 0);
    	vector<double> sigmaa(numk, 0);
    	vector<double> sigmab(numk, 0);
    	vector<double> sigmax(numk, 0);
    	vector<double> sigmay(numk, 0);
    	vector<double> distvec(sz, DBL_MAX);//存储距离最近的seed的距离
    
    	double invwt = 1.0/((STEP/M)*(STEP/M));//这个invwet其实是权衡颜色距离和空间距离的一个权重,因此参数M也就是一个权重
    
    	int x1, y1, x2, y2;
    	double l, a, b;
    	double dist;
    	double distxy;
    	for( int itr = 0; itr < iterations; itr++ )//限制迭代次数
    	{
    		distvec.assign(sz, DBL_MAX);
    		for( int n = 0; n < numk; n++ )//遍历所有的种子点
    		{
                y1 = max(0.0, kseedsy[n]-offset);
                y2 = min((double)m_height, kseedsy[n]+offset);
                x1 = max(0.0, kseedsx[n]-offset);
                x2 = min((double)m_width, kseedsx[n]+offset);//限制邻域的范围(seedx + offset, seedy - offset
    
    
    			for( int y = y1; y < y2; y++ )//在邻域内搜索
    			{
    				for( int x = x1; x < x2; x++ )
    				{
    					int i = y*m_width + x;
    
    					l = m_lvec[i];
    					a = m_avec[i];
    					b = m_bvec[i];
    
    					dist =	(l - kseedsl[n])*(l - kseedsl[n]) +
    								(a - kseedsa[n])*(a - kseedsa[n]) +
    								(b - kseedsb[n])*(b - kseedsb[n]);//颜色空间距离
    
    					distxy =  (x - kseedsx[n])*(x - kseedsx[n]) +
    								(y - kseedsy[n])*(y - kseedsy[n]);//xy空间距离
    					
    					//------------------------------------------------------------------------
    					dist += distxy*invwt;//dist = sqrt(dist) + sqrt(distxy*invwt);//this is more exact
    					//------------------------------------------------------------------------
    					if( dist < distvec[i] )
    					{
    						distvec[i] = dist;//赋值距离
    						klabels[i]  = n;//赋值label
    					}
    				}
    			}
    		}
    		//-----------------------------------------------------------------
    		// Recalculate the centroid and store in the seed values
    		//-----------------------------------------------------------------
    		//instead of reassigning memory on each iteration, just reset.
    	
    		sigmal.assign(numk, 0);
    		sigmaa.assign(numk, 0);
    		sigmab.assign(numk, 0);
    		sigmax.assign(numk, 0);
    		sigmay.assign(numk, 0);
    		clustersize.assign(numk, 0);
    		//------------------------------------
    		//edgesum.assign(numk, 0);
    		//------------------------------------
    
    		{int ind(0);
    		for( int r = 0; r < m_height; r++ )
    		{
    			for( int c = 0; c < m_width; c++ )
    			{
    				sigmal[klabels[ind]] += m_lvec[ind];//klabel[ind]是这个点属于那个seed,sigmal[klabel[ind]]是这个seed的l颜色域的累加和,m_lvec[ind]就是这个点l的颜色域的值
    				sigmaa[klabels[ind]] += m_avec[ind];
    				sigmab[klabels[ind]] += m_bvec[ind];
    				sigmax[klabels[ind]] += c;
    				sigmay[klabels[ind]] += r;
    				//------------------------------------
    				//edgesum[klabels[ind]] += edgemag[ind];
    				//------------------------------------
    				clustersize[klabels[ind]] += 1.0;//统计这个seed一共有多少个像素点
    				ind++;
    			}
    		}}
    
    		{for( int k = 0; k < numk; k++ )
    		{
    			if( clustersize[k] <= 0 ) clustersize[k] = 1;
    			inv[k] = 1.0/clustersize[k];//computing inverse now to multiply, than divide later
    		}}
    		
    		{for( int k = 0; k < numk; k++ )
    		{
    			kseedsl[k] = sigmal[k]*inv[k];//重新分配这个seed的颜色域,然后再循环到重新开始计算距离什么的
    			kseedsa[k] = sigmaa[k]*inv[k];
    			kseedsb[k] = sigmab[k]*inv[k];
    			kseedsx[k] = sigmax[k]*inv[k];
    			kseedsy[k] = sigmay[k]*inv[k];
    			//------------------------------------
    			//edgesum[k] *= inv[k];
    			//------------------------------------
    		}}
    	}
    }
    
    

    函数EnforceLabelConnectivity实现了连通区域的重新赋值,之前所有的label值是聚类中心的值,通过这个函数将其统一超像素的编号,从0开始一直到最后一个超像素,然后会统计每个超像素所占的像素,如果太小的话会进行合并

    void SLIC::EnforceLabelConnectivity(
    	const int*					labels,//input labels that need to be corrected to remove stray labels
    	const int					width,
    	const int					height,
    	int*&						nlabels,//new labels
    	int&						numlabels,//the number of labels changes in the end if segments are removed
    	const int&					K) //the number of superpixels desired by the user
    {
    //	const int dx8[8] = {-1, -1,  0,  1, 1, 1, 0, -1};
    //	const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1,  1};
    
    	const int dx4[4] = {-1,  0,  1,  0};
    	const int dy4[4] = { 0, -1,  0,  1};//四邻域
    
    	const int sz = width*height;
    	const int SUPSZ = sz/K;//每个小网格像素的大小
    	//nlabels.resize(sz, -1);
    	for( int i = 0; i < sz; i++ ) nlabels[i] = -1;//nlabels为经过这个函数处理输出的数组
    	int label(0);
    	int* xvec = new int[sz];
    	int* yvec = new int[sz];//这连个数组都有整个图片那么大
    	int oindex(0);
    	int adjlabel(0);//adjacent label
    	for( int j = 0; j < height; j++ )//遍历每一个像素
    	{
    		for( int k = 0; k < width; k++ )
    		{
    			if( 0 > nlabels[oindex] )//nlabels小于零应该就是还没有处理过的像素
    			{
    				nlabels[oindex] = label;//oindex为0时候,label也为0
    				//--------------------
    				// Start a new segment
    				//--------------------
    				xvec[0] = k;//这里存储的是像素的坐标
    				yvec[0] = j;
    				//-------------------------------------------------------
    				// Quickly find an adjacent label for use later if needed
    				//-------------------------------------------------------
    				{for( int n = 0; n < 4; n++ )
    				{
    					int x = xvec[0] + dx4[n];
    					int y = yvec[0] + dy4[n];
    					if( (x >= 0 && x < width) && (y >= 0 && y < height) )//在图像范围内
    					{
    						int nindex = y*width + x;//这个是四邻域像素
    						if(nlabels[nindex] >= 0) adjlabel = nlabels[nindex];//查看邻域里面是否有大于等于0的nlabels,如果有的话就是adjlabel
    					}
    				}}
    
    				int count(1);
    				for( int c = 0; c < count; c++ )
    				{
    					for( int n = 0; n < 4; n++ )//又是一个四邻域
    					{
    						int x = xvec[c] + dx4[n];
    						int y = yvec[c] + dy4[n];
    
    						if( (x >= 0 && x < width) && (y >= 0 && y < height) )
    						{
    							int nindex = y*width + x;
    
    							if( 0 > nlabels[nindex] && labels[oindex] == labels[nindex] ) //这个邻域像素没有处理过 && 这个邻域像素的label和中心像素的label一致
    							{
    								xvec[count] = x;
    								yvec[count] = y;//把坐标存进去,就是记录下有哪些坐标
    								nlabels[nindex] = label;//向领域像素扩展
    								count++;//因为这个count会一直增加,因此邻域也会一直扩展,知道周围没有邻域像素可以再扩izhan了为止
    							}
    						}
    
    					}
    				}
    				//-------------------------------------------------------
    				// If segment size is less then a limit, assign an
    				// adjacent label found before, and decrement label count.
    				//-------------------------------------------------------
    				if(count <= SUPSZ >> 2)//这个区域太小
    				{
    					for( int c = 0; c < count; c++ )
    					{
    						int ind = yvec[c]*width+xvec[c];
    						nlabels[ind] = adjlabel;
    					}
    					label--;
    				}
    				label++;
    			}
    			oindex++;
    		}
    	}
    	numlabels = label;
    
    	if(xvec) delete [] xvec;
    	if(yvec) delete [] yvec;
    }
    

    实验结果

    最后的实验结果如下:


    在这里插入图片描述

    如果注释掉EnforceLabelConnectivity函数里面去除小区域的代码块的结果是

    在这里插入图片描述

    所以还是加上比较好,SLIC只能整体效果看上去还是不错哈,单张图片耗时0.0845s,速度也比较快。

    其他超像素算法对比

    这里我顺便跑了下这个项目里的其他超像素算法,以好有个对比
    (1) Felzenswalb & Huttenlocher (P. F. Felzenswalb, D. P. Huttenlocher. Efficient graph-based image segmentation. International Journal of Computer Vision, 59(2), 2004.)
    单张图片耗时:0.0653s


    在这里插入图片描述

    (2) Constant Intensity Superpixels/Compact Superpixels (O. Veksler, Y. Boykov, P. Mehrani. Superpixels and supervoxels in anenergy optimization framework. European Conference on Computer Vision, pages 211–224, 2010.)
    单张图片耗时:0.0468s


    在这里插入图片描述

    (3) Entropy Rate Superpixels (M. Y. Lui, O. Tuzel, S. Ramalingam, R. Chellappa. Entropy rate superpixel segmentation. Conference on Computer Vision and Pattern Recognition, pages 2097–2104, 2011.)
    单张图片耗时:0.786s


    在这里插入图片描述

    (4) Pseudo Boolean Superpixels (Superpixels via pseudo-boolean optimization. Y. Zhang, R. Hartley, J. Mashford, and S. Burn. In International Conference on Computer Vision, 2011.)
    单张图片耗时:0.0255s


    在这里插入图片描述

    (5) Contour Relaxed Superpixels ( C. Conrad, M. Mertz, R. Mester. Contour-relaxed superpixels. Energy Minimization Methods in Computer Vision and Pattern Recognition, volume 8081 of Lecture Notes in Computer Science, pages 280–293, 2013.)
    单张图片耗时:0.430s


    在这里插入图片描述

    (6) Superpixels Extracted via Energy-Driven Sampling (M. van den Bergh, X. Boix, G. Roig, B. de Capitani, L. van Gool. SEEDS: Superpixels extracted via energy-driven sampling. European Conference on Computer Vision, pages 13–26, 2012.)
    单张图片耗时:0.0819s


    在这里插入图片描述

    (7) VLFeat ( A. Vedaldi, B. Fulkerson. VLFeat: An Open and Portable Library of Computer Vision Algorithms.\url{http://www.vlfeat.org/, 2008.)
    单张图片耗时:0.630s(这个结果应该是我参数没有设好)


    在这里插入图片描述

    (8) SNN
    2018年好像又出了一个SNN专门用来生成超像素的深度神经网络框架,是基于SLIC的,但不管是效果还是速度都比SLIC要好。

    总结
    通过上面的退比可以发现同样的图片SLIC单张图片耗时0.0845ms,在所有算法中算比较快的,从结果来看,效果比较看上去是比较好的,与其有类似效果的有Contour Relaxed Superpixels算法,但是耗时高达400ms,但是超像素的结果对比不能仅仅从上看去的效果进行比较,本文开始的的简介中所提到的两篇文章都有定义超像素效果优良的衡量标准,如果需要详细对比各个算法间的好坏的话还是需要去细读论文和源码。

    展开全文
  • SLIC算法求superpixels的Windows软件,可自行设定superpixels的分割数目
  • 之前有关于SLIC Superpixel算法的个人理解,这篇文章是对其改进算法Improved SLIC算法的理解。 改进点: sigma filter 用来避免错误分割; 聚类结束后,会基于颜色相似度将小聚类融入临近的聚类中。 改进聚类中心 ...

    之前有关于SLIC Superpixel算法的个人理解,这篇文章是对其改进算法Improved SLIC算法的理解。

    改进点:

    1. sigma filter 用来避免错误分割;
    2. 聚类结束后,会基于颜色相似度将小聚类融入临近的聚类中。

    改进聚类中心

    原SLIC算法

    使用平均值更新聚类中心

    1

    Improved SLIC算法

    采用如下方法更新聚类中心,对应于改进点1:

    3

    其中δ_j表示,属于该聚类的所有像素点的亮度L的标准差,α是一个常数。

    改进点2

    原来的SLIC算法中,在进行迭代聚类后,得到一些小的聚类,这些聚类会被融入它们的最大临近聚类中,改进后的算法,这些小的聚类也会被融入到临近的聚类中,但是不是基于原来的聚类尺寸进行选择,而是基于亮度相似性进行选择,(对应于改进点2):

    4

    亮度相似性D_m通过上式得到,其中,μ和μ_m分别表示小聚类和它的临近聚类的亮度平均值,令D_q表示某个小聚类的所有D_m值中的最小值,设置阈值T,如果D_q< T,则这个小聚类将被融入它的第q个临近聚类中;否则,该小聚类保持独立。

    展开全文
  • 超像素经典 SLIC 算法 python 实现

    千次阅读 2020-02-10 09:17:50
    本文是研究生课程图像处理期末作业,内容是了解并入门超像素算法原理,主要介绍了超像素的评测标准,经典算法 SLIC,讨论了 SLIC 算法中的不足之处,以及 SLIC 的两个有效的改进算法 SEEDS 和 ETPS。 文章内容见我的...

    在这里插入图片描述

    摘要

    本文是研究生课程图像处理期末作业,内容是了解并入门超像素算法原理,主要介绍了超像素的评测标准,经典算法 SLIC,讨论了 SLIC 算法中的不足之处,以及 SLIC 的两个有效的改进算法 SEEDS 和 ETPS。
    文章内容见我的码云

    python3 代码

    运算速度慢但是便于理清 SLIC 算法的参考实现

    import math
    from skimage import io, color
    import numpy as np
    from tqdm import trange
    from tqdm import tqdm
    
    class Cluster(object):
        cluster_index = 1
    
        def __init__(self, h, w, l=0, a=0, b=0):
            self.update(h, w, l, a, b)
            self.pixels = []
            self.no = self.cluster_index
            Cluster.cluster_index += 1
    
        def update(self, h, w, l, a, b):
            self.h = h
            self.w = w
            self.l = l
            self.a = a
            self.b = b
    
        def __str__(self):
            return "{},{}:{} {} {} ".format(self.h, self.w, self.l, self.a, self.b)
    
        def __repr__(self):
            return self.__str__()
    
    
    class SLICProcessor(object):
        @staticmethod
        def open_image(path):
            """
            Return:
                3D array, row col [LAB]
            """
            rgb = io.imread(path)
            lab_arr = color.rgb2lab(rgb)
            return lab_arr
    
        @staticmethod
        def save_lab_image(path, lab_arr):
            """
            Convert the array to RBG, then save the image
            :param path:
            :param lab_arr:
            :return:
            """
            rgb_arr = color.lab2rgb(lab_arr)
            io.imsave(path, rgb_arr)
    
        def make_cluster(self, h, w):
            h = int(h)
            w = int(w)
            return Cluster(h, w,
                           self.data[h][w][0],
                           self.data[h][w][1],
                           self.data[h][w][2])
    
        def __init__(self, filename, K, M):
            self.K = K
            self.M = M
    
            self.data = self.open_image(filename)
            self.image_height = self.data.shape[0]
            self.image_width = self.data.shape[1]
            self.N = self.image_height * self.image_width
            self.S = int(math.sqrt(self.N / self.K))
    
            self.clusters = []
            self.label = {}
            self.dis = np.full((self.image_height, self.image_width), np.inf)
    
        def init_clusters(self):
            h = self.S // 2
            w = self.S // 2
            while h < self.image_height:
                while w < self.image_width:
                    self.clusters.append(self.make_cluster(h, w))
                    w += self.S
                w = self.S // 2
                h += self.S
    
        def get_gradient(self, h, w):
            if w + 1 >= self.image_width:
                w = self.image_width - 2
            if h + 1 >= self.image_height:
                h = self.image_height - 2
    
            gradient = self.data[h + 1][w + 1][0] - self.data[h][w][0] + \
                       self.data[h + 1][w + 1][1] - self.data[h][w][1] + \
                       self.data[h + 1][w + 1][2] - self.data[h][w][2]
            return gradient
    
        def move_clusters(self):
            for cluster in self.clusters:
                cluster_gradient = self.get_gradient(cluster.h, cluster.w)
                for dh in range(-1, 2):
                    for dw in range(-1, 2):
                        _h = cluster.h + dh
                        _w = cluster.w + dw
                        new_gradient = self.get_gradient(_h, _w)
                        if new_gradient < cluster_gradient:
                            cluster.update(_h, _w, self.data[_h][_w][0], self.data[_h][_w][1], self.data[_h][_w][2])
                            cluster_gradient = new_gradient
    
        def assignment(self):
            for cluster in tqdm(self.clusters):
                for h in range(cluster.h - 2 * self.S, cluster.h + 2 * self.S):
                    if h < 0 or h >= self.image_height: continue
                    for w in range(cluster.w - 2 * self.S, cluster.w + 2 * self.S):
                        if w < 0 or w >= self.image_width: continue
                        L, A, B = self.data[h][w]
                        Dc = math.sqrt(
                            math.pow(L - cluster.l, 2) +
                            math.pow(A - cluster.a, 2) +
                            math.pow(B - cluster.b, 2))
                        Ds = math.sqrt(
                            math.pow(h - cluster.h, 2) +
                            math.pow(w - cluster.w, 2))
                        D = math.sqrt(math.pow(Dc / self.M, 2) + math.pow(Ds / self.S, 2))
                        if D < self.dis[h][w]:
                            if (h, w) not in self.label:
                                self.label[(h, w)] = cluster
                                cluster.pixels.append((h, w))
                            else:
                                self.label[(h, w)].pixels.remove((h, w))
                                self.label[(h, w)] = cluster
                                cluster.pixels.append((h, w))
                            self.dis[h][w] = D
    
        def update_cluster(self):
            for cluster in self.clusters:
                sum_h = sum_w = number = 0
                for p in cluster.pixels:
                    sum_h += p[0]
                    sum_w += p[1]
                    number += 1
                _h = int(sum_h / number)
                _w = int(sum_w / number)
                cluster.update(_h, _w, self.data[_h][_w][0], self.data[_h][_w][1], self.data[_h][_w][2])
    
        def save_current_image(self, name):
            image_arr = np.copy(self.data)
            for cluster in self.clusters:
                for p in cluster.pixels:
                    image_arr[p[0]][p[1]][0] = cluster.l
                    image_arr[p[0]][p[1]][1] = cluster.a
                    image_arr[p[0]][p[1]][2] = cluster.b
                image_arr[cluster.h][cluster.w][0] = 0
                image_arr[cluster.h][cluster.w][1] = 0
                image_arr[cluster.h][cluster.w][2] = 0
            self.save_lab_image(name, image_arr)
    
        def iterate_10times(self):
            self.init_clusters()
            self.move_clusters()
            for i in trange(10):
                self.assignment()
                self.update_cluster()
                name = 'lenna_M{m}_K{k}_loop{loop}.png'.format(loop=i, m=self.M, k=self.K)
                self.save_current_image(name)
    
    
    if __name__ == '__main__':
        p = SLICProcessor('kemomimi.png', 500, 40)
        p.iterate_10times()
    
    

    将上面缓慢的循环体转化成 numpy 高速矩阵运算的参考实现,很有 python 优化计算的学习意义。
    作者原本是有一段合并不连续的超像素的函数( SLIC 可选的后期处理),发现并没有用到,就去掉了。

    # Aleena Watson
    # Final Project - Computer Vision Simon Niklaus
    # Winter 2018 - PSU
    
    import numpy as np
    import sys
    from skimage import io, color
    import tqdm
    
    # using algorithm in 3.2 apply image gradients as computed in eq2:
    # G(x,y) = ||I(x+1,y) - I(x-1,y)||^2+ ||I(x,y+1) - I(x,y-1)||^2
    
    # SLIC implements a special case of k-means clustering algorithm. 
    # Was recommended to use an off the shelf algorithm for clustering but
    # because this algorithm is based on this special case of k-means, 
    # I kept this implementation to stay true to the algorithm.
    
    def generate_pixels():
        indnp = np.mgrid[0:SLIC_height,0:SLIC_width].swapaxes(0,2).swapaxes(0,1)
        for i in tqdm.tqdm(range(SLIC_ITERATIONS)):
            SLIC_distances = 1 * np.ones(img.shape[:2])
            for j in range(SLIC_centers.shape[0]):
                x_low, x_high = int(SLIC_centers[j][3] - step), int(SLIC_centers[j][3] + step)
                y_low, y_high = int(SLIC_centers[j][4] - step), int(SLIC_centers[j][4] + step)
    
                if x_low <= 0:
                    x_low = 0
                #end
                if x_high > SLIC_width:
                    x_high = SLIC_width
                #end
                if y_low <=0:
                    y_low = 0
                #end
                if y_high > SLIC_height:
                    y_high = SLIC_height
                #end
    
                cropimg = SLIC_labimg[y_low : y_high , x_low : x_high]
                color_diff = cropimg - SLIC_labimg[int(SLIC_centers[j][4]), int(SLIC_centers[j][3])]
                color_distance = np.sqrt(np.sum(np.square(color_diff), axis=2))
    
                yy, xx = np.ogrid[y_low : y_high, x_low : x_high]
                pixdist = ((yy-SLIC_centers[j][4])**2 + (xx-SLIC_centers[j][3])**2)**0.5
    
                # SLIC_m is "m" in the paper, (m/S)*dxy
                dist = ((color_distance/SLIC_m)**2 + (pixdist/step)**2)**0.5
    
                distance_crop = SLIC_distances[y_low : y_high, x_low : x_high]
                idx = dist < distance_crop
                distance_crop[idx] = dist[idx]
                SLIC_distances[y_low : y_high, x_low : x_high] = distance_crop
                SLIC_clusters[y_low : y_high, x_low : x_high][idx] = j
            #end
    
            for k in range(len(SLIC_centers)):
                idx = (SLIC_clusters == k)
                colornp = SLIC_labimg[idx]
                distnp = indnp[idx]
                SLIC_centers[k][0:3] = np.sum(colornp, axis=0)
                sumy, sumx = np.sum(distnp, axis=0)
                SLIC_centers[k][3:] = sumx, sumy
                SLIC_centers[k] /= np.sum(idx)
            #end
        #end
    #end
    
    def display_contours(color):
        rgb_img = img.copy()
        is_taken = np.zeros(img.shape[:2], np.bool)
        contours = []
    
        for i in range(SLIC_width):
            for j in range(SLIC_height):
                nr_p = 0
                for dx, dy in [(-1,0), (-1,-1), (0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1)]:
                    x = i + dx
                    y = j + dy
                    if x>=0 and x < SLIC_width and y>=0 and y < SLIC_height:
                        if is_taken[y, x] == False and SLIC_clusters[j, i] != SLIC_clusters[y, x]:
                            nr_p += 1
                        #end
                    #end
                #end
    
                if nr_p >= 2:
                    is_taken[j, i] = True
                    contours.append([j, i])
                #end
            #end
        #end
        for i in range(len(contours)):
            rgb_img[contours[i][0], contours[i][1]] = color
        # for k in range(SLIC_centers.shape[0]):
        #     i,j = SLIC_centers[k][-2:]
        #     img[int(i),int(j)] = (0,0,0)
        #end
        io.imsave("SLIC_contours.jpg", rgb_img)
    
        return rgb_img
    #end
    
    def display_center():
        '''
        将超像素用聚类中心颜色代替
        '''
        import matplotlib.pyplot as plt
    
        lab_img = np.zeros([SLIC_height,SLIC_width,3]).astype(np.float64)
        for i in range(SLIC_width):
            for j in range(SLIC_height):
                k = int(SLIC_clusters[j, i])
                lab_img[j,i] = SLIC_centers[k][0:3]
        rgb_img = color.lab2rgb(lab_img)
        io.imsave("SLIC_centers.jpg",rgb_img)
        return (rgb_img*255).astype(np.uint8)
    
    def find_local_minimum(center):
        min_grad = 1
        loc_min = center
        for i in range(center[0] - 1, center[0] + 2):
            for j in range(center[1] - 1, center[1] + 2):
                c1 = SLIC_labimg[j+1, i]
                c2 = SLIC_labimg[j, i+1]
                c3 = SLIC_labimg[j, i]
                if ((c1[0] - c3[0])**2)**0.5 + ((c2[0] - c3[0])**2)**0.5 < min_grad:
                    min_grad = abs(c1[0] - c3[0]) + abs(c2[0] - c3[0])
                    loc_min = [i, j]
                #end
            #end
        #end
        return loc_min
    #end
    
    def calculate_centers():
        centers = []
        for i in range(step, SLIC_width - int(step/2), step):
            for j in range(step, SLIC_height - int(step/2), step):
                nc = find_local_minimum(center=(i, j))
                color = SLIC_labimg[nc[1], nc[0]]
                center = [color[0], color[1], color[2], nc[0], nc[1]]
                centers.append(center)
            #end
        #end
    
        return centers
    #end
    
    # global variables
    img = io.imread(sys.argv[1])
    print(img.max(),img.min())
    step = int((img.shape[0]*img.shape[1]/int(sys.argv[2]))**0.5)
    SLIC_m = int(sys.argv[3])
    SLIC_ITERATIONS = 4
    SLIC_height, SLIC_width = img.shape[:2]
    SLIC_labimg = color.rgb2lab(img)
    SLIC_distances = 1 * np.ones(img.shape[:2])
    SLIC_clusters = -1 * SLIC_distances
    SLIC_center_counts = np.zeros(len(calculate_centers()))
    SLIC_centers = np.array(calculate_centers())
    
    # main
    generate_pixels()
    calculate_centers()
    img_contours = display_contours([0.0, 0.0, 0.0])
    img_center = display_center()
    
    print(img,img_center,img_contours)
    result = np.hstack([img,img_contours,img_center])
    io.imsave("my_slic.jpg",result)
    
    展开全文
  • 最近想要在julia中实现 Simple Linear Iterative Clustering (SLIC) 算法对图像进行超像素分割,关于SLIC超像素分割算法,请参考...在网上搜索了一番,找到了SLIC算法的python实现代码,见laixintao仁兄SLIC算法分...
  • SLIC算法作为Superpixel中的聚类算法,简单高效,很多算法在预处理都会使用SLIC先对像素进行简单分类,方便之后的操作。
  • SLIC算法 超像素 superpixel

    千次阅读 2015-04-05 21:52:04
    SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel)。 算法大致思想是这样的,将图像从RGB颜色空间转换到CIE-Lab颜色空间,对应每个像素的(L,a,b)颜色值和(x,y)坐标...
  • SLIC算法分割超像素原理及Python实现

    千次阅读 2018-08-28 19:18:35
    为了从NYU数据集中得到分割标签,尝试用12年的SLIC算法,加上以关节点为中心的分割结果。 SLIC 原始链接 整个算法的输入只有一个,即超像素的个数K。图片原有N个像素,要分割成K个像素,那么每个像素的大小是N...
  • SLIC算法介绍及代码

    2016-02-14 14:54:56
    包括SLIC框架、相关论文介绍、关于SLIC的学习笔记、SLIC分割代码以及示例。
  • VLFeat库中SLIC算法代码解读 原作者:Andrea Vedaldi
  • SLIC图像superpixel的C++代码
  • 文章目录实验目的和前言什么是超像素分割SLIC算法实现超像素分割效果图代码 参考 https://blog.csdn.net/qq_40268412/article/details/103915197 实验目的和前言 本次实验目的: 实现超像素分割(无监督聚类方式...
  • SLIC算法介绍与Python实现

    千次阅读 2019-04-27 17:04:00
    SLIC(simple linear iterative clustering)算法介绍与Python实现 本文转载自:https://blog.csdn.net/shinian1987/article/details/78618917 图像分割是图像处理,计算机视觉领域里非常基础,非常重要的一个...
  • python:超像素SLIC算法使用

    万次阅读 2018-07-01 21:45:33
    skimage作为图像处理库,包括多种图像分割算法。其中超像素slic目前表现较好,该部分代码如下。 from skimage.segmentation import slic,mark_boundaries from skimage import io import matplotlib.pyplot as plt ...
  • SLIC算法理解(仅为个人笔记)

    千次阅读 2019-07-23 19:14:06
    MATLAB中有超像素分割算法superpixels函数,https://ww2.mathworks.cn/help/images/ref/superpixels.html?s_tid=doc_ta#bu1_lce-4,原理就是SLIC超像素分割。 1.SLIC超像素分割论文(翻译版) ...
  • 简介:最近项目使用到了超像素分割,因此顺道研究了以下SLIC这一算法。超像素分割这类low-level vision问题已经在CVPR,ICCV这种顶级会议上逐渐销声匿迹,越来越流行的learning method渐渐占据了这些顶级会议90%的...
  • 超像素(SuperPixel),就是把原本多个像素点,组合成一个大的像素。...在超像素算法方面,SLIC Superpixels Compared to State-of-the-art Superpixel Methods这篇论文非常经典。论文中从算法效率,内...
  • sp_label_matrix=fs.slic(img_rgb, n_segments=400, compactness=10, sigma=1, enforce_connectivity=False) sp_label_vector=sp_label_matrix.reshape(1,-1) sp_num=len(np.unique(sp_label_matrix)) sp_ind=np....
  • SLIC superpixel算法

    2017-03-14 01:22:00
    标题 SLIC superpixel算法 作者 YangZheng 联系方式 263693992 SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel)。 基本思想 算法大致思想是这样的,将图像从RGB颜色空间转换...
  • SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel)。 基本思想 算法大致思想是这样的,将图像从RGB颜色空间转换到CIE-Lab颜色空间,对应每个像素的(L,a,b)颜色值和(x,y...
  • 今天介绍一种高效的分割算法,即 simple linear iterative clustering (SLIC) 算法,顾名思义,这是一种简单的迭代聚类算法,这个算法发表于 2012 年的 PAMI 上。 SLIC 算法有几个关键点, 1: 图像分割块的初始化...
  • SLIC超像素生成算法

    千次阅读 2015-10-09 18:58:48
    SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel)。 基本思想 算法大致思想是这样的,将图像从RGB颜色空间转换到CIE-Lab颜色空间,对应每个像素的(L,a,b)颜色...
  • 其次结合图像的纹理特征提出了一种自适应K值方法,并对图像利用SLIC算法进行粗分割,描绘出乳腺肿块的初始轮廓;最后,利用GVF Snake算法加大对轮廓边缘信息的捕捉范围,进行细分割得到分割结果图。实验验证表明,该分割...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 219
精华内容 87
关键字:

slic算法