【图像处理笔记】图像分割之聚类和超像素

本文介绍了基于k均值聚类的图像区域分割原理,包括OpenCV中的cv::kmeans函数及其源码分析,以及超像素分割的概念,强调了超像素在保留图像边缘信息和降低处理复杂度上的优势。并提到了SLIC算法作为超像素分割的典型应用。
摘要由CSDN通过智能技术生成

🚀 优质资源分享 🚀

学习路线指引(点击解锁) 知识定位 人群定位
🧡 Python实战微信订餐小程序 🧡 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统
目录

回到顶部## 0 引言

大多数分割算法都基于图像灰度值的两个基本性质之一:不连续性相似性第一类方法根据灰度的突变(如边缘)将图像分割为多个区域:首先寻找边缘线段,然后将这些线段连接为边界的方法来识别区域。第二类方法根据一组预定义的准则把一幅图像分割为多个区域。本节讨论两种相关的区域分割方法:(1)在数据中寻找聚类的经典方法,它与亮度和颜色等变量有关;(2)用聚类从图像中提取“超像素”的现代方法。

回到顶部## 1 使用k均值聚类的区域分割

1.1 原理

**聚类方法的思想****是将样本集合按照其特征的相似性划分为若干类别,使同一类别样本的特征具有较高的相似性,不同类别样本的特征具有较大的差异性。**令{z1, z2, z3 …, zn}是样本集合,在图像分割中,样本向量z的每个分量表示一个数值像素属性。例如,分割只基于灰度尺度时,z是一个表示像素灰度的标量。分割的如果是RGB彩色图像,z通常是一个三维向量,这个三维向量的每个分量是RGB三通道的灰度值。k均值聚类的目的就是将样本集合划分为k个满足如下最优准则的不相交的聚类集合C={C1, C2, …, Ck}:

式中,mi是集合Ci中样本的均值向量(或质心),||z-mi||项是Ci中的一个样本到均值mi的欧式距离。换言之,这个公式说,我们感兴趣的是找到集合C={C1, C2, …, Ck},集合中的每个点到该集合的均值的距离之和是最小的。

基于聚类的区域分割,就是基于图像的灰度颜色纹理形状等特征,用聚类算法把图像分成若干类别或区域,使每个点到聚类中心的均值最小。k 均值(k-means)是一种无监督聚类算法。基于 k 均值聚类算法的区域分割,算法步骤为:

(1)初始化算法:规定一组初始均值

(2)将样本分配给聚类:对所有的像素点,计算像素到每个聚类中心的距离,将像素分类到距离最小的一个聚类中;

(3)更新聚类中心:根据分类结果计算出新的聚类中心;

(4)完备性验证:计算当前步骤和前几步中平均向量之间的差的欧几里得范数。计算残差E,即k个范数之和。若E≤T,其中T是一个规定的非负阈值,则停止。否则,返回步骤2。

1.2 cv::kmeans函数

OpenCV提供了函数cv::kmeans来实现 k-means 聚类算法。函数cv::kmeans不仅可以基于灰度、颜色对图像进行区域分割,也可以基于样本的其它特征如纹理、形状进行聚类。

double cv::kmeans(InputArray data,  //用于聚类的数据,类型为 CV\_32F
                  int K, //设定的聚类数量
                  InputOutputArray bestLabels,  //输出整数数组,用于存储每个样本的聚类类别索引
                  TermCriteria criteria,  //算法终止条件:即最大迭代次数或所需精度
                  int attempts,  //用于指定使用不同初始标记执行算法的次数
                  int flags,  //初始化聚类中心的方法:0=随机初始化 1=kmeans++方法初始化 2=第一次用用户指定的标签初始化,后面attempts-1都用随机或版随机的初始化
                  OutputArray centers = noArray()  //聚类中心的输出矩阵,每个聚类中心占一行
                  )

示例 图像分割之k均值聚类

?

| 123456789101112131415 | Mat src = imread(``"./14.tif"``, 0);``Mat dataPixels = src.reshape(0, 1);``//可以是一列,每一行表示一个样本;或者一行,一列是一个样本;样本的分量数为通道数``dataPixels.convertTo(dataPixels, CV_32FC1);``//输入需要是32位浮点型``int numCluster = 3;``Mat labels;``Mat centers;``kmeans(dataPixels, numCluster, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1), 3, KMEANS_PP_CENTERS, centers);``Mat dst = Mat::zeros(src.size(), CV_8UC1);``float``* pdata = dataPixels.ptr<``float``>(0);``for (``int i = 0; i < src.rows * src.cols; i++) {``int k = labels.ptr<``int``>(i)[0];``//每个像素对应的标签k,即属于集合k``pdata[i] = centers.ptr<``float``>(k)[0];``//用集合中心替换该像素``}``dataPixels.convertTo(dataPixels, CV_8UC1);``dst = dataPixels.reshape(0, src.rows); |

1.3 cv::kmeans源码

当图片非常大时,对图像进行简单的计算操作,耗时就会变得非常大,常用的加速方法如OpenMp,TBB,OpenCL和CUDA。使用cuda时,图片在cpu和gpu之间的传输时间就达到上百ms,不适合本来就是ms级的计算。在kmeans源码中使用**parallel_for_**并行计算各个样本到聚类中心的距离。这边写一个简单的例子,了解下parallel_for_的用法。

示例 利用并行计算加速图片旋转

?

| 1234567891011121314151617181920212223242526272829303132333435363738 | // 方法1:并行 将该方法写成一个类,继承ParallelLoopBody,然后重写(),利用parallel_for_可以开启并行``class trans :``public ParallelLoopBody {``public``:``trans(``const uchar* _src, uchar*_dst, int _dims, int _istep):src(_src), dst(_dst), dims(_dims), istep(_istep) {}``void operator()(``const Range& range) const //重载操作符()``{``for (``int n = range.start; n < range.end; ++n)``{``for (``int i = 0; i < dims; i++)``dst[i * istep + (istep - 1 - n)] = src[n * dims + i];``}``}``private``:``const uchar* src;``uchar* dst;``int dims;``int istep;``};``// 方法2 for循环`

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值