SLIC超像素分割算法研究(代码可下载)

来源:互联网 发布:显示登录成功和链接php 编辑:程序博客网 时间:2024/05/16 16:15

超像素概念是2003年Xiaofeng Ren[1]提出和发展起来的图像分割技术,是指具有相似纹理、颜色、亮度等特征的相邻像素构成的有一定视觉意义的不规则像素块。它利用像素之间特征的相似性将像素分组,用少量的超像素代替大量的像素来表达图片特征,很大程度上降低了图像后处理的复杂度,所以通常作为分割算法的预处理步骤。已经广泛用于图像分割、姿势估计、目标跟踪、目标识别等计算机视觉应用[2],而目前,OpenCV于超像素生成,没有发现网上有相关代码,但其实在opencv_contrib目录下面的未稳定功能模块SLICSEEDSLSC算法相关实现,如果想要使用这个目录的功能,需要自己重新进行OpenCV的编译[8],有关编译和配置方法可参考[8 ,9]。一些常用的超像素分割算法和性能对比如下表[3]:


本文介绍SLIC(simple lineariterativeclustering)超像素分割算法,即简单的线性迭代聚类,该算法是目前执行速度最快的超像素分割方法[3],2015年实现并行执行速度达250FPS [4,5]。SLIC算法是2010年Achanta[6]提出的一种思想简单、实现方便的算法,将彩色图像转化为CIELAB颜色空间和XY坐标下的5维特征向量,然后对5维特征向量构造距离度量标准,对图像像素进行局部聚类的过程[2],算法基本思路与Kmeans聚类算法类似。算法具体实现过程如下[6,2]:


1.  初始化种子点(聚类中心):按照设定的超像素个数,在图像内均匀的分配种子点。假设图片总共有 N 个像素点,预分割为 K 个相同尺寸的超像素,那么每个超像素的大小为N/ K ,则相邻种子点的距离(步长)近似为S=sqrt(N/K)。

2.  在种子点的n*n邻域内重新选择种子点(一般取n=3)。具体方法为:计算该邻域内所有像素点的梯度值,将种子点移到该邻域内梯度最小的地方。这样做的目的是为了避免种子点落在梯度较大的轮廓边界上,以免影响后续聚类效果。

3.  在每个种子点周围的邻域内为每个像素点分配类标签(即属于哪个聚类中心)。和标准的k-means在整张图中搜索不同,SLIC的搜索范围限制为2S*2S,可以加速算法收敛,如下图。在此注意一点:期望的超像素尺寸为S*S,但是搜索的范围是2S*2S。

4.  距离度量。包括颜色距离和空间距离。对于每个搜索到的像素点,分别计算它和该种子点的距离。距离计算方法如下:

其中,dc代表颜色距离,ds代表空间距离,Ns是类内最大空间距离,定义为Ns=S=sqrt(N/K),适用于每个聚类。最大的颜色距离Nc既随图片不同而不同,也随聚类不同而不同,所以我们取一个固定常数m(取值范围[1,40],一般取10)代替。最终的距离度量D'如下:


由于每个像素点都会被多个种子点搜索到,所以每个像素点都会有一个与周围种子点的距离,取最小值对应的种子点作为该像素点的聚类中心。

5.  迭代优化。理论上上述步骤不断迭代直到误差收敛(可以理解为每个像素点聚类中心不再发生变化为止),实践发现10次迭代对绝大部分图片都可以得到较理想效果,所以一般迭代次数取10。

6.  增强连通性。经过上述迭代优化可能出现以下瑕疵:出现多连通情况、超像素尺寸过小,单个超像素被切割成多个不连续超像素等,这些情况可以通过增强连通性解决。主要思路是:新建一张标记表,表内元素均为-1,按照“Z”型走向(从左到右,从上到下顺序)将不连续的超像素、尺寸过小超像素重新分配给邻近的超像素,遍历过的像素点分配给相应的标签,直到所有点遍历完毕为止。

SLIC算法的代码(C++和matlab版)可到该算法发明人Achanta的页面[7]下载,我们下载该代码的C++windows版本,并编写一个简单图像格式转换函数,将SLIC超像素算法与openCV一起实现了一个简单的应用实例,因为SLIC算法输入必须是RGB彩色图像,因此我们考虑了灰度图像和彩色图像两种不同情况下的数据转换,当输入为灰度图时,将该灰度图像复制三次到其他色彩通道,主程序如下(代码运行还需要从Achanta网站[7]下载的SLIC.cpp和SLIC.h文件,有关我们测试程序的所有代码(包括了SLIC.cpp和SLIC.h文件)下载网站:http://download.csdn.net/detail/zhouxianen1987/9778247):

#if 1#include <iostream>  #include <time.h>#include "opencv2/opencv.hpp" #include "SLIC.h"using namespace std;using namespace cv;int imgOpenCV2SLIC(Mat img, int &height, int &width, int &dim, unsigned int * &image);int imgSLIC2openCV(unsigned int *image, int height, int width, int dim, Mat &imgSLIC);int main(){Mat imgRGB;time_t tStart,tEnd,exeT;imgRGB= imread("0_0_77rgb.jpg");if (imgRGB.empty() == true){cout<<"can not open rgb image!"<<endl;}unsigned int *image; int height; int width; int dim;long imgSize;int numlabels(0);SLIC slic;int m_spcount= 100 ;int m_compactness=10;imgOpenCV2SLIC(imgRGB,  height,  width,  dim, image);//OpenCV 图像数据转换成SLIC图像数据imgSize = height* width;int* labels = new int[imgSize];tStart=clock();//SLIC超像素分割,代码下载网站:http://ivrl.epfl.ch/research/superpixels#SLICOslic.DoSuperpixelSegmentation_ForGivenNumberOfSuperpixels(image, width, height, labels, numlabels, m_spcount, m_compactness);slic.DrawContoursAroundSegments(image, labels, width, height, 0);tEnd=clock();exeT=tEnd-tStart;Mat imgSLIC;imgSLIC2openCV(image, height,width,dim,imgSLIC);//SLIC结果:SLIC图像数据转换成OpenCV 图像数据//结果显示cout<<"SLIC执行时间exeT:"<<exeT<<"毫秒"<<endl;imshow("imgRGB",imgRGB);imshow("imgSLIC1",imgSLIC);waitKey();return 0;}//OpenCV Mat图像数据转换为SLIC图像数据//输入:Mat img, int &height, int &width, int &dim,//输出:unsigned int * &image,同时返回转换是否成功的标志:成功为0,识别为1int imgOpenCV2SLIC(Mat img, int &height, int &width, int &dim, unsigned int * &image){int error=0;if( img.empty() ) //请一定检查是否成功读图 { error =1;} dim=img.channels();//图像通道数目height=img.rows;width=img.cols;int imgSize=width*height;unsigned char *pImage  = new unsigned char [imgSize*4];if(dim==1){for(int j = 0; j < height; j++){uchar * ptr = img.ptr<uchar>(j);for(int i = 0; i < width; i++) {pImage[j * width*4 + 4*i+3] = 0;pImage[j * width*4 + 4*i+2] = ptr[0];pImage[j * width*4 + 4*i+1] = ptr[0];pImage[j * width*4 + 4*i] = ptr[0];ptr ++;}}}else{if(dim==3){for(int j = 0; j < height; j++){Vec3b * ptr = img.ptr<Vec3b>(j);for(int i = 0; i < width; i++) {pImage[j * width*4 + 4*i+3] = 0;pImage[j * width*4 + 4*i+2] = ptr[0][2];//RpImage[j * width*4 + 4*i+1] = ptr[0][1];//GpImage[j * width*4 + 4*i]   = ptr[0][0];//Bptr ++;}}}else  error=1;}image = new unsigned int[imgSize];memcpy( image, (unsigned int*)pImage, imgSize*sizeof(unsigned int) );delete pImage;return error;}//SLIC图像数据转换为OpenCV Mat图像数据//输入:unsigned int *image, int height, int width, int dim//输出:Mat &imgSLIC ,同时返回转换是否成功的标志:成功为0,识别为1int imgSLIC2openCV(unsigned int *image, int height, int width, int dim, Mat &imgSLIC){int error=0;//转换是否成功的标志:成功为0,识别为1if(dim==1){imgSLIC.create(height, width, CV_8UC1);//遍历所有像素,并设置像素值 for( int j = 0; j< height; ++j) { //获取第 i行首像素指针 uchar * p = imgSLIC.ptr<uchar>(j); //对第 i行的每个像素(byte)操作 for( int i = 0; i < width; ++i ) p[i] =(unsigned char)(image[j*width+i]& 0xFF)  ; } }else{if(dim==3){imgSLIC.create(height, width, CV_8UC3);//遍历所有像素,并设置像素值 for( int j = 0; j < height; ++j) { //获取第 i行首像素指针 Vec3b * p = imgSLIC.ptr<Vec3b>(j); for( int i = 0; i < width; ++i ) { p[i][0] = (unsigned char)(image[j*width+i]          & 0xFF ); //Blue p[i][1] = (unsigned char)((image[j*width+i] >>  8 ) & 0xFF ); //Green p[i][2] = (unsigned char)((image[j*width+i] >>  16) & 0xFF ) ; //Red } }}else  error= 1 ;}return error;}#endif

执行效果如下:




参考资料:

[1]Ren X,Malik J.  Learning a classification model forsegmentation[C]/ /Proceedings of the IEEE International Conference on Computer  Vision.   Washington  DC, USA:   IEEE,2003:   10-17.[DOI:  10. 1109 /ICCV. 2003. 1238308]

[2] http://blog.csdn.net/electech6/article/details/45509779

[3]Achanta,Radhakrishna, et al. "SLICsuperpixels compared to state-of-the-artsuperpixel methods." PatternAnalysis and Machine Intelligence, IEEETransactions on 34.11 (2012): 2274-2282.

[4] http://www.robots.ox.ac.uk/~victor/gslicr/

[5] gSLICr: SLIC superpixels at over 250Hz

[6] Achanta,Radhakrishna, et al. Slicsuperpixels. No. EPFL REPORT 149300. 2010.

[7] http://ivrl.epfl.ch/research/superpixels#SLICO

[8] http://blog.csdn.net/zhangyonggang886/article/details/52854219?locationNum=5&fps=1

[9] http://blog.csdn.net/jarvischu/article/details/8468894


1 0
原创粉丝点击