百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

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

bigegpt 2024-09-08 11:32 5 浏览

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

[html] view plain copy

  1. #if 1
  2. #include <iostream>
  3. #include <time.h>
  4. #include "opencv2/opencv.hpp"
  5. #include "SLIC.h"
  6. using namespace std;
  7. using namespace cv;
  8. int imgOpenCV2SLIC(Mat img, int &height, int &width, int &dim, unsigned int * &image);
  9. int imgSLIC2openCV(unsigned int *image, int height, int width, int dim, Mat &imgSLIC);
  10. int main()
  11. {
  12. Mat imgRGB;
  13. time_t tStart,tEnd,exeT;
  14. imgRGB= imread("0_0_77rgb.jpg");
  15. if (imgRGB.empty() == true){
  16. cout<<"can not open rgb image!"<<endl;
  17. }
  18. unsigned int *image;
  19. int height;
  20. int width;
  21. int dim;
  22. long imgSize;
  23. int numlabels(0);
  24. SLIC slic;
  25. int m_spcount= 100 ;
  26. int m_compactness=10;
  27. imgOpenCV2SLIC(imgRGB, height, width, dim, image);//OpenCV 图像数据转换成SLIC图像数据
  28. imgSize = height* width;
  29. int* labels = new int[imgSize];
  30. tStart=clock();
  31. //SLIC超像素分割,代码下载网站:http://ivrl.epfl.ch/research/superpixels#SLICO
  32. slic.DoSuperpixelSegmentation_ForGivenNumberOfSuperpixels(image, width, height, labels, numlabels, m_spcount, m_compactness);
  33. slic.DrawContoursAroundSegments(image, labels, width, height, 0);
  34. tEnd=clock();
  35. exeT=tEnd-tStart;
  36. Mat imgSLIC;
  37. imgSLIC2openCV(image, height,width,dim,imgSLIC);//SLIC结果:SLIC图像数据转换成OpenCV 图像数据
  38. //结果显示
  39. cout<<"SLIC执行时间exeT:"<<exeT<<"毫秒"<<endl;
  40. imshow("imgRGB",imgRGB);
  41. imshow("imgSLIC1",imgSLIC);
  42. waitKey();
  43. return 0;
  44. }
  45. //OpenCV Mat图像数据转换为SLIC图像数据
  46. //输入:Mat img, int &height, int &width, int &dim,
  47. //输出:unsigned int * &image,同时返回转换是否成功的标志:成功为0,识别为1
  48. int imgOpenCV2SLIC(Mat img, int &height, int &width, int &dim, unsigned int * &image)
  49. {
  50. int error=0;
  51. if( img.empty() ) //请一定检查是否成功读图
  52. {
  53. error =1;
  54. }
  55. dim=img.channels();//图像通道数目
  56. height=img.rows;
  57. width=img.cols;
  58. int imgSize=width*height;
  59. unsigned char *pImage = new unsigned char [imgSize*4];
  60. if(dim==1){
  61. for(int j = 0; j < height; j++){
  62. uchar * ptr = img.ptr<uchar>(j);
  63. for(int i = 0; i < width; i++) {
  64. pImage[j * width*4 + 4*i+3] = 0;
  65. pImage[j * width*4 + 4*i+2] = ptr[0];
  66. pImage[j * width*4 + 4*i+1] = ptr[0];
  67. pImage[j * width*4 + 4*i] = ptr[0];
  68. ptr ++;
  69. }
  70. }
  71. }
  72. else{
  73. if(dim==3){
  74. for(int j = 0; j < height; j++){
  75. Vec3b * ptr = img.ptr<Vec3b>(j);
  76. for(int i = 0; i < width; i++) {
  77. pImage[j * width*4 + 4*i+3] = 0;
  78. pImage[j * width*4 + 4*i+2] = ptr[0][2];//R
  79. pImage[j * width*4 + 4*i+1] = ptr[0][1];//G
  80. pImage[j * width*4 + 4*i] = ptr[0][0];//B
  81. ptr ++;
  82. }
  83. }
  84. }
  85. else error=1;
  86. }
  87. image = new unsigned int[imgSize];
  88. memcpy( image, (unsigned int*)pImage, imgSize*sizeof(unsigned int) );
  89. delete pImage;
  90. return error;
  91. }
  92. //SLIC图像数据转换为OpenCV Mat图像数据
  93. //输入:unsigned int *image, int height, int width, int dim
  94. //输出:Mat &imgSLIC ,同时返回转换是否成功的标志:成功为0,识别为1
  95. int imgSLIC2openCV(unsigned int *image, int height, int width, int dim, Mat &imgSLIC)
  96. {
  97. int error=0;//转换是否成功的标志:成功为0,识别为1
  98. if(dim==1){
  99. imgSLIC.create(height, width, CV_8UC1);
  100. //遍历所有像素,并设置像素值
  101. for( int j = 0; j< height; ++j)
  102. {
  103. //获取第 i行首像素指针
  104. uchar * p = imgSLIC.ptr<uchar>(j);
  105. //对第 i行的每个像素(byte)操作
  106. for( int i = 0; i < width; ++i )
  107. p[i] =(unsigned char)(image[j*width+i]& 0xFF) ;
  108. }
  109. }
  110. else{
  111. if(dim==3){
  112. imgSLIC.create(height, width, CV_8UC3);
  113. //遍历所有像素,并设置像素值
  114. for( int j = 0; j < height; ++j)
  115. {
  116. //获取第 i行首像素指针
  117. Vec3b * p = imgSLIC.ptr<Vec3b>(j);
  118. for( int i = 0; i < width; ++i )
  119. {
  120. p[i][0] = (unsigned char)(image[j*width+i] & 0xFF ); //Blue
  121. p[i][1] = (unsigned char)((image[j*width+i] >> 8 ) & 0xFF ); //Green
  122. p[i][2] = (unsigned char)((image[j*width+i] >> 16) & 0xFF ) ; //Red
  123. }
  124. }
  125. }
  126. else error= 1 ;
  127. }
  128. return error;
  129. }
  130. #endif

执行效果如下:

相关推荐

恢复软件6款汇总推荐,帮你减轻数据恢复压力!

在当今数字化生活中,数据丢失的风险如影随形。无论是误删文件、硬盘故障,还是遭遇病毒攻击,丢失的数据都可能给我们带来不小的麻烦。此时,一款优秀的数据恢复软件就成为了挽救数据的关键。今天,为大家汇总推荐...

中兴星星一号刷回官方原版recovery的教程

【搞科技教程】中兴星星一号的官方recovery也来说一下了,因为之前给大家分享过了第三方的recovery了,之前给大家分享的第三方recovery也是采用一键刷入的方式,如果细心的朋友会发现,之前...

新玩机工具箱,Uotan柚坛工具箱软件体验

以前的手机系统功能比较单调,各厂商的重视程度不一样,所以喜欢玩机的朋友会解锁手机系统的读写权限,来进行刷机或者ROOT之类的操作,让使用体验更好。随着现在的手机系统越来越保守,以及自身功能的增强,...

三星g906k刷recovery教程_三星g906k中文recovery下载

【搞科技教程】看到有一些机友在找三星g906k的第三方recovery,下面就来说一下详细的recovery的刷入方法了,因为手机只有有了第三方的recovery之后才可以刷第三方的root包和系统包...

中兴星星2号刷recovery教程_星星二号中文recovery下载

【搞科技教程】咱们的中兴星星2手机也就是中兴星星二号手机的第三方recovery已经出来了,并且是中文版的,有了这个recovery之后,咱们的手机就可以轻松的刷第三方的系统包了,如果没有第三方的re...

数据恢复软件有哪些值得推荐?这 6 款亲测好用的工具汇总请收好!

在数字生活中,数据丢失的阴霾常常突如其来。无论是误删工作文档、格式化重要磁盘,还是遭遇系统崩溃,都可能让我们陷入焦虑。关键时刻,一款得力的数据恢复软件便是那根“救命稻草”。今天,为大家精心汇总6...

中兴u956刷入recovery的教程(中兴e5900刷机)

【搞科技教程】这次主要来给大家说说中兴u956手机如何刷入第三方的recovery,因为第三方的recovery工具是咱们刷第三方rom包的基础,可是很我欠却不会刷,所以太这里来给大家整理了一下详细的...

联想A850+刷recovery教程 联想A850+第三方recovery下载

【搞科技教程】联想A850+的第三方recovery出来了,这个第三方的recovery是非常的重要的,比如咱们的手机要刷第三方的系统包的时候,都是需要用到这个第三方的recovery的,在网上也是有...

工具侠重大更新 智能机上刷机一条龙完成

工具侠是针对玩机的机油开发的一款工具,不管是发烧级别的粉丝,还是普通小白用户,都可以在工具侠上找到你喜欢的工具应用。这不,最新的工具侠2.0.16版本,更新了专门为小白准备的刷机助手工具,以及MTK超...

shift+delete删除的文件找回6种硬盘数据恢复工具

硬盘作为电脑的重要存储设备,如同一个巨大的数字仓库,承载着我们日常工作、学习和生活中的各种文件,从珍贵的照片、重要的工作文档到喜爱的视频、音乐等,都依赖硬盘来安全存放。但有时,我们可能会不小心用sh...

使用vscode+Deepseek 实现AI编程 基于Cline和continue

尊敬的诸位!我是一名专注于嵌入式开发的物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与AI的无尽可能。这两天deepseek3.0上线,据说编程能力比肩Cl...

详解如何使用VSCode搭建TypeScript环境(适合小白)

搭建Javascript环境因为TypeScript不能直接在浏览器上运行。它需要编译器来编译并生成JavaScript文件。所以需要首先安装好javascript环境,可以参考文章:https://...

使用VSCode来书写你的Jupyter Notebooks

现在你可以在VScode里面来书写你的notebook了,使用起来十分的方便。下面来给大家演示一下环境的搭建。首先需要安装一个jupyter的包,使用下面的命令安装:pip3install-ih...

使用VSCode模板提高Vue开发效率(vscode开发vue插件)

安装VSCode安装Vetur和VueHelper插件,安装完成后需要重启VScode。在扩展插件搜索框中找到如下Vetur和VueHelper两个插件,注意看图标。添加Vue模板打...

干货!VsCode接入DeepSeek实现AI编程的5种主流插件详解

AI大模型对编程的影响非常之大,可以说首当其冲,Cursor等对话式编程工具渐渐渗透到开发者的工作中,作为AI编程的明星产品,Cursor虽然好用,但是贵啊,所以咱们得找平替,最好免费那种。俗话说,不...