前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【手撕算法】C++实现超像素分割算法

【手撕算法】C++实现超像素分割算法

作者头像
周旋
发布2022-08-07 12:20:35
7160
发布2022-08-07 12:20:35
举报
文章被收录于专栏:行走的机械人行走的机械人

写完这篇,图像分割的传统方法就快全了,传统图像分割大体有基于阈值的,这类就没啥算法可以写,所以直接略过了;然后就是K-means这种聚类/分裂的,从几个点开始进行聚类分割,或者一张图不断分裂达到分割目的;

【手撕算法】K-means算法实现主题色提取

再有就是区域生长这类的;

【手撕算法】基于队列实现的区域增长分割算法

以及分水岭算法,分水岭算法代码写好有一段时间了,但实在不知道文章咋写...就再放放吧;最后就是超像素分割了,超像素分割有k-means算法的影子,所以可以先看看k-means算法的代码实现过程。

算法原理

  1. 初始化种子点(聚类中心):按照设定的超像素个数,在图像内均匀的分配种子点。假设图片总共有 N 个像素点,预分割为 K 个相同尺寸的超像素,那么每个超像素的大小为N/ K ,则相邻种子点的距离(步长)近似为S=sqrt(N/K)。
  2. 在种子点的n*n邻域内重新选择种子点(一般取n=3)。具体方法为:计算该邻域内所有像素点的梯度值,将种子点移到该邻域内梯度最小的地方。这样做的目的是为了避免种子点落在梯度较大的轮廓边界上,以免影响后续聚类效果。
  3. 在每个种子点周围的邻域内为每个像素点分配类标签(即属于哪个聚类中心)。和标准的k-means在整张图中搜索不同,SLIC的搜索范围限制为2S2S,可以加速算法收敛,如下图。在此注意一点:期望的超像素尺寸为SS,但是搜索的范围是2S*2S。
  1. 距离度量。包括颜色距离和空间距离。对于每个搜索到的像素点,分别计算它和该种子点的距离。距离计算方法如下:

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

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

  1. 迭代优化。理论上上述步骤不断迭代直到误差收敛(可以理解为每个像素点聚类中心不再发生变化为止),实践发现10次迭代对绝大部分图片都可以得到较理想效果,所以一般迭代次数取10。
  2. 增强连通性。经过上述迭代优化可能出现以下瑕疵:出现多连通情况、超像素尺寸过小,单个超像素被切割成多个不连续超像素等,这些情况可以通过增强连通性解决。主要思路是:新建一张标记表,表内元素均为-1,按照“Z”型走向(从左到右,从上到下顺序)将不连续的超像素、尺寸过小超像素重新分配给邻近的超像素,遍历过的像素点分配给相应的标签,直到所有点遍历完毕为止。

伪算法描述

程序介绍

程序声明了一个SLIC算法类,类的具体程序太长了,就不贴了。

就看一下主程序吧:

代码语言:javascript
复制
int main()
{
  【1】读取原图并显示
  Mat image = imread("千矢.png",33);
  if (image.empty())
  {
    printf_s("图片读取失败");
    return -1;
  }
  imshow("原图", image);
  【2】转换为LAB颜色空间  方便计算距离
  Mat lab_image = image.clone();
  cvtColor(image, lab_image, COLOR_BGR2Lab);

  //定义超像素数以及权重
  int w = image.cols, h = image.rows;
  int nr_superpixels = 300;//超像素数
  int nc = 40;//权重m
  double step = sqrt((w * h) / (double)nr_superpixels);
  【3】执行SLIC超像素算法
  SLIC slic;
  slic.generate_superpixels(&lab_image, step, nc);
  slic.create_connectivity(&lab_image);

  【4】显示分割轮廓和分割结果图
  //该三个函数可以分别注释单独显示查看
  slic.colour_with_cluster_means(&image);//颜色均值填充
  slic.display_contours(&image, Scalar(0, 0, 255));//显示轮廓
  //slic.display_center_grid(&image, Scalar(255, 0, 0));//显示中心点
  imshow("result", image);
  waitKey(0);
}

一共是四个步骤。其中步骤【2】中需要自己定义两个变量nr_superpixels和nc。

  • nr_superpixels为超像素个数,你可以根据图像大小自己定义,如果图像x方向10个超像素块,y方向30个超像素块,那就是300。
  • 权重变量nc,即上文【算法原理】第4步中的固定常数m,一般取1-40范围内的整数。

效果展示

THE END

本文原创内容有限,就是整合了一下自己看的超像素分割的博客,两篇不错的链接放这儿了:

代码语言:javascript
复制
https://blog.csdn.net/zhj_matlab/article/details/52986700
https://blog.csdn.net/qq_26129959/article/details/90760028
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 周旋机器视觉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档