meanShfit均值漂移算法是一种通用的聚类算法,它的基本原理是:对于给定的一定数量样本,任选其中一个样本,以该样本为中心点划定一个圆形区域,求取该圆形区域内样本的质心,即密度最大处的点,再以该点为中心继续执行上述迭代过程,直至最终收敛。

可以利用均值偏移算法的这个特性,实现彩色图像分割,Opencv中对应的函数是pyrMeanShiftFiltering。这个函数严格来说并不是图像的分割,而是图像在色彩层面的平滑滤波,它可以中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域,所以在Opencv中它的后缀是滤波“Filter”,而不是分割“segment”。先列一下这个函数,再说一下它“分割”彩色图像的实现过程。

void pyrMeanShiftFiltering( InputArray src, OutputArray dst,
double sp, double sr, int maxLevel=1,
TermCriteria termcrit=TermCriteria(
TermCriteria::MAX_ITER+TermCriteria::EPS,5,1) );

第一个参数src,输入图像,8位,三通道的彩色图像,并不要求必须是RGB格式,HSV、YUV等Opencv中的彩色图像格式均可;

第二个参数dst,输出图像,跟输入src有同样的大小和数据格式;

第三个参数sp,定义的漂移物理空间半径大小;

第四个参数sr,定义的漂移色彩空间半径大小;

第五个参数maxLevel,定义金字塔的最大层数;

第六个参数termcrit,定义的漂移迭代终止条件,可以设置为迭代次数满足终止,迭代目标与中心点偏差满足终止,或者两者的结合;

pyrMeanShiftFiltering函数的执行过程是这样的:

1. 迭代空间构建:

以输入图像上src上任一点P0为圆心,建立物理空间上半径为sp,色彩空间上半径为sr的球形空间,物理空间上坐标2个—x、y,色彩空间上坐标3个—R、G、B(或HSV),构成一个5维的空间球体。

其中物理空间的范围x和y是图像的长和宽,色彩空间的范围R、G、B分别是0~255。

2. 求取迭代空间的向量并移动迭代空间球体后重新计算向量,直至收敛:

在1中构建的球形空间中,求得所有点相对于中心点的色彩向量之和后,移动迭代空间的中心点到该向量的终点,并再次计算该球形空间中所有点的向量之和,如此迭代,直到在最后一个空间球体中所求得的向量和的终点就是该空间球体的中心点Pn,迭代结束。

3. 更新输出图像dst上对应的初始原点P0的色彩值为本轮迭代的终点Pn的色彩值,如此完成一个点的色彩均值漂移。

4. 对输入图像src上其他点,依次执行步骤1,、2、3,遍历完所有点位后,整个均值偏移色彩滤波完成,这里忽略对金字塔的讨论。

在这个过程中,关键参数是sp和sr的设置,二者设置的值越大,对图像色彩的平滑效果越明显,同时函数耗时也越多。以下对一幅图像执行pyrMeanShiftFiltering操作,来看一下效果:

原始图像:

物理空间半径sp=10,色彩空间半径sr=10时色彩滤波效果:

物理空间半径sp=50,色彩空间半径sr=50时色彩滤波效果:

对比可以发现,半径为10时,图像色彩细节大部分存在,半径为50时,山体和草地的色彩细节基本都已经丢失。

到这里,meanShift均值偏移算法对彩色图像的平滑操作就完成了,为了达到分割的目的,需要借助另外一个漫水填充函数的进一步处理来实现,那就是floodFill:

int floodFill( InputOutputArray image, InputOutputArray mask,
Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0,
Scalar loDiff=Scalar(), Scalar upDiff=Scalar(),
int flags=4 );

第一个参数image,输入三通道8bit彩色图像,同时作为输出。

第二个参数mask,是掩模图像,它的大小是输入图像的长宽左右各加1个像素,mask一方面作为输入的掩模图像,另一方面也会在填充的过程中不断被更新。floodFill漫水填充的过程并不会填充mask上灰度值不为0的像素点,所以可以使用一个图像边缘检测的输出作为mask,这样填充就不会填充或越过边缘轮廓。mask在填充的过程中被更新的过程是这样的:每当一个原始图上一个点位(x,y)被填充之后,该点位置对应的mask上的点(x+1,y+1)的灰度值随机被设置为1(原本该点的灰度值为0),代表该点已经被填充处理过。

第三个参数seedPoint,是漫水填充的起始种子点。

第四个参数newVal,被充填的色彩值。

第五个参数rect,可选的参数,用于设置floodFill函数将要重绘区域的最小矩形区域;

第六个参数loDiff和第七个参数upDiff,用于定义跟种子点相比色彩的下限值和上限值,介于种子点减去loDiff和种子点加上upDiff的值会被填充为跟种子点同样的颜色。

第八个参数,定义漫水填充的模式,用于连通性、扩展方向等的定义。

这里边好玩的参数是掩模mask,一个像素值全为0的mask在运算过程中,随着填充过程,mask上像素不断被置为1,代表当前位置被充填过了,仍以上图为例,看一下mask的变化:

这个是初始输入的值全为0的mask:

这个是漫水填充执行完后的mask:

OMG!怎么都是两个黑乎乎的图像,并且也没啥变化啊!难道是Opencv欺骗了我们?确实是有人欺骗了我们,这个人不是Opencv而是上帝~~,执行漫水填充后,mask上的值从原本全是0变成了全是1,置1就代表了该点被漫水填充执行过,之所以我们看不出来,是因为人眼对灰阶为1的亮度和灰阶为0的亮度完全区分不出来。

为了显示mask内像素值的变化过程,我们对mask执行0~255的归一化。这个是左上角第一个像素执行完漫水填充后归一化的mask显示:

左上角是天空的开始的图像上半部分是蓝色天空的颜色,占据了很大部分空间,都被填充成一个颜色,对应的在mask上这些区域的灰度值被从全黑的0置为了1。

中间过程1,这时候两朵云所在的区域也被填充:

中间过程2,这时候已经接近填充完成,除了底部,其余区域都被置为了1:

Opencv中自带例程文件meanshift_segmentation.cpp,位置在\opencv\sources\samples\cpp中,其中有一行代码是有关mask掩模的:

if( mask.at<uchar>(y+1, x+1) == 0 )  //非0处即为1,表示已经经过填充,不再处理
{
Scalar newVal( rng(256), rng(256), rng(256) );
floodFill( res, mask, Point(x,y), newVal, 0, Scalar::all(5), Scalar::all(5) ); //执行漫水填充
}

这里在填充过程中对mask的值做了判断,不为0表示已经填充过,不再重复填充,这就是对mask的巧妙运用,可以特高运算效率。当然仅仅是基于效率的考虑,如果不加这个判定条件,重复填充,最终的结果是一样的。

以下Opencv代码是对原有自带例程的简化,代码量较少,核心操作只有两个函数,但需要对这两个操作的逻辑阐述清楚,这就是以上啰嗦了这么多所试图做的事情。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp" using namespace cv; int main(int argc, char** argv)
{
Mat img = imread( argv[1] ); //读入图像,RGB三通道
imshow("原图像",img);
Mat res; //分割后图像
int spatialRad = 50; //空间窗口大小
int colorRad = 50; //色彩窗口大小
int maxPyrLevel = 2; //金字塔层数
pyrMeanShiftFiltering( img, res, spatialRad, colorRad, maxPyrLevel); //色彩聚类平滑滤波
imshow("res",res);
RNG rng = theRNG();
Mat mask( res.rows+2, res.cols+2, CV_8UC1, Scalar::all(0) ); //掩模
for( int y = 0; y < res.rows; y++ )
{
for( int x = 0; x < res.cols; x++ )
{
if( mask.at<uchar>(y+1, x+1) == 0 ) //非0处即为1,表示已经经过填充,不再处理
{
Scalar newVal( rng(256), rng(256), rng(256) );
floodFill( res, mask, Point(x,y), newVal, 0, Scalar::all(5), Scalar::all(5) ); //执行漫水填充
}
}
}
imshow("meanShift图像分割", res );
waitKey();
return 0;
}

经pyrMeanShiftFiltering处理过色彩平滑滤波后的结果:

进一步进过floodFill漫水填充后的最终分割效果:

Opencv均值漂移pyrMeanShiftFiltering彩色图像分割流程剖析的更多相关文章

  1. 使用Opencv中均值漂移meanShift跟踪移动目标

    Mean Shift均值漂移算法是无参密度估计理论的一种,无参密度估计不需要事先知道对象的任何先验知识,完全依靠训练数据进行估计,并且可以用于任意形状的密度估计,在某一连续点处的密度函数值可由该点邻域 ...

  2. opencv2对读书笔记——使用均值漂移算法查找物体

    一些小概念 1.反投影直方图的结果是一个概率映射,体现了已知图像内容出如今图像中特定位置的概率. 2.概率映射能够找到最初的位置,从最初的位置開始而且迭代移动,便能够找到精确的位置,这就是均值漂移算法 ...

  3. twemproxy代理主干流程——剖析twemproxy代码正编

    在twemproxy的发送和接收流程剖析中,我们已经完全弄清楚twemproxy如何将客户端以及服务端发来的包切分成msg,获得一个独立的msg后twemproxy应该如何处理?这是本文这次需要重点介 ...

  4. Meanshift均值漂移算法

      通俗理解Meanshift均值漂移算法  Meanshift车手?? 漂移?? 秋名山???   不,不,他是一组算法,  今天我就带大家来了解一下机器学习中的Meanshift均值漂移. Mea ...

  5. Spring Security Oauth2 单点登录案例实现和执行流程剖析

    Spring Security Oauth2 OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本.OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(au ...

  6. Jedis cluster命令执行流程剖析

    Jedis cluster命令执行流程剖析 在Redis Cluster集群模式下,由于key分布在各个节点上,会造成无法直接实现mget.sInter等功能.因此,无论我们使用什么客户端来操作Red ...

  7. opencv 彩色图像分割(inrange)

    灰度图像大多通过算子寻找边缘和区域生长融合来分割图像. 彩色图像增加了色彩信息,可以通过不同的色彩值来分割图像,常用彩色空间HSV/HSI, RGB, LAB等都可以用于分割! 笔者主要介绍inran ...

  8. Cocos Creator 资源加载流程剖析【一】——cc.loader与加载管线

    这系列文章会对Cocos Creator的资源加载和管理进行深入的剖析.主要包含以下内容: cc.loader与加载管线 Download部分 Load部分 额外流程(MD5 Pipe) 从编辑器到运 ...

  9. Kafka控制器选举流程剖析

    1.概述 平时在使用Kafka的时候,可能关注的更多的是Kafka系统层面的.今天来给大家剖析一下Kafka的控制器,了解一下Kafka控制器的选举流程. 2.内容 Kafka控制器,其实就是一个Ka ...

随机推荐

  1. Jenkins学习总结(1)——Jenkins详细安装与构建部署使用教程

    Jenkins是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能.Jenkins是基于Java开发的一种持续集成工具,用于监控持续重复的工作,功能包括: 1.持续的软件版本发 ...

  2. 边缘独立(marginal independent)的理解及举例

    1. 定义 ∀xi∈dom(X),yj∈dom(Y),yk∈dom(Y),如果满足, P(X=xi|Y=yj)==P(X=xi|Y=yk)P(X=Xi) 则称随机变量 X 边缘独立于随机变量 Y. 理 ...

  3. PyCharm下载主题以及个性化设置(详细)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 一.下载主题 1.在http://www.themesmap.com/theme.html上选择自己喜欢的主题点进去后进行下载. ...

  4. 微服务实战(四):服务发现的可行方案以及实践案例 - DockOne.io

    原文:微服务实战(四):服务发现的可行方案以及实践案例 - DockOne.io 这是关于使用微服务架构创建应用系列的第四篇文章.第一篇介绍了微服务架构的模式,讨论了使用微服务架构的优缺点.第二和第三 ...

  5. 【hdu 2328】Corporate Identity

    [链接]h在这里写链接 [题意] 找一个字典序最小的公共最长子串; [题解] 后缀数组. 把所有的串用不同的分隔符分开.(大于'z'的分隔符); 然后求出那几个固定的数组. 二分一下那个子串的长度. ...

  6. LA 5902 - Movie collection 树状数组(Fenwick树)

    看题传送门 题目大意:XXX喜欢看电影,他有好多好多的影碟,每个影碟都有个独立的编号.开始是从下往上影碟的顺序是n~1,他每次拿出影碟的时候,你需要输出压在该影碟上的有几个.(拿出后其他影碟顺序不变) ...

  7. swift项目第二天:初始化项目

    初始化项目 项目的部署版本 之后项目会运行在哪些系统中 横竖屏的支持 iPhone应用一般只支持横屏 iPhone游戏一般支持竖屏 iPad横竖屏都支持 设置项目的图标和启动图片 项目的图标(美工做好 ...

  8. UCloud上LAMP小型站点搭建与測试

    文件夹 介绍 LAMP环境搭建 打开UCloud防火墙 WordPress安装 应用測试 介绍 本篇博客旨在通过介绍搭建一个WordPress博客的过程介绍在UCloud的云主机(UHOST)上搭建单 ...

  9. js静态私有变量(将方法变成原型模式,被所有实例共享,而方法操作变量,故变量是静态)

    js静态私有变量(将方法变成原型模式,被所有实例共享,而方法操作变量,故变量是静态) 一.总结 1.js函数中的private和public:js函数中的私有变量 var 变量名,公有变量 this. ...

  10. hadoop调优之一:概述 分类: A1_HADOOP B3_LINUX 2015-03-13 20:51 395人阅读 评论(0) 收藏

    hadoop集群性能低下的常见原因 (一)硬件环境 1.CPU/内存不足,或未充分利用 2.网络原因 3.磁盘原因 (二)map任务原因 1.输入文件中小文件过多,导致多次启动和停止JVM进程.可以设 ...