Opencv均值漂移pyrMeanShiftFiltering彩色图像分割流程剖析
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彩色图像分割流程剖析的更多相关文章
- 使用Opencv中均值漂移meanShift跟踪移动目标
Mean Shift均值漂移算法是无参密度估计理论的一种,无参密度估计不需要事先知道对象的任何先验知识,完全依靠训练数据进行估计,并且可以用于任意形状的密度估计,在某一连续点处的密度函数值可由该点邻域 ...
- opencv2对读书笔记——使用均值漂移算法查找物体
一些小概念 1.反投影直方图的结果是一个概率映射,体现了已知图像内容出如今图像中特定位置的概率. 2.概率映射能够找到最初的位置,从最初的位置開始而且迭代移动,便能够找到精确的位置,这就是均值漂移算法 ...
- twemproxy代理主干流程——剖析twemproxy代码正编
在twemproxy的发送和接收流程剖析中,我们已经完全弄清楚twemproxy如何将客户端以及服务端发来的包切分成msg,获得一个独立的msg后twemproxy应该如何处理?这是本文这次需要重点介 ...
- Meanshift均值漂移算法
通俗理解Meanshift均值漂移算法 Meanshift车手?? 漂移?? 秋名山??? 不,不,他是一组算法, 今天我就带大家来了解一下机器学习中的Meanshift均值漂移. Mea ...
- Spring Security Oauth2 单点登录案例实现和执行流程剖析
Spring Security Oauth2 OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本.OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(au ...
- Jedis cluster命令执行流程剖析
Jedis cluster命令执行流程剖析 在Redis Cluster集群模式下,由于key分布在各个节点上,会造成无法直接实现mget.sInter等功能.因此,无论我们使用什么客户端来操作Red ...
- opencv 彩色图像分割(inrange)
灰度图像大多通过算子寻找边缘和区域生长融合来分割图像. 彩色图像增加了色彩信息,可以通过不同的色彩值来分割图像,常用彩色空间HSV/HSI, RGB, LAB等都可以用于分割! 笔者主要介绍inran ...
- Cocos Creator 资源加载流程剖析【一】——cc.loader与加载管线
这系列文章会对Cocos Creator的资源加载和管理进行深入的剖析.主要包含以下内容: cc.loader与加载管线 Download部分 Load部分 额外流程(MD5 Pipe) 从编辑器到运 ...
- Kafka控制器选举流程剖析
1.概述 平时在使用Kafka的时候,可能关注的更多的是Kafka系统层面的.今天来给大家剖析一下Kafka的控制器,了解一下Kafka控制器的选举流程. 2.内容 Kafka控制器,其实就是一个Ka ...
随机推荐
- 8.Swift教程翻译系列——控制流之条件
3.条件语句 常常会须要依据不同的情况来运行不同的代码. 你可能想要在错误发生的时候运行一段额外的代码,或者当某个值变得太高或者太低的时候给他输出出来.要实现这些需求,你能够使用条件分支. Swift ...
- C# exe文件 添加到windows 服务
我们运行.net的发布工具installutil.exe来添加到windows服务里面(该工具默认在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727目录下) ...
- android Email总结文档
目录:src\com.android.email.activity 一. Welcome.java 根据AndroidManifest.xml可知该文件为程序入口文件: 加载该文件时,查询数据库账户列 ...
- sql for xml query sample
sample 1: declare @x xml select @x='<ArrayOfScheduledTime> <ScheduledTime> <Recurrenc ...
- TCP的可靠传输机制(简单好理解:分段与流,滑窗,连接,流量控制,重新发送,堵塞控制)
TCP的几大模块:分段与流,滑窗,连接,流量控制,重新发送,堵塞控制. 1.checksum:在发送TCP报文的时候,里面的信息可能会因为环境的问题,发送变化,这时,接收信号的时候就需要通过check ...
- C# AutoMapper
http://www.cnblogs.com/xlhblogs/p/3356748.html
- v-if和updated钩子结合使用 渲染echart图表
项目需求是这样的,用户可以自定选择echart 曲线图 是横向还是竖向显示.我的做法是 写了一个横向的echart图表,也写了一个竖向的echart图表,然后两者共用存在store里的图表数据,就能实 ...
- 如何在Win8/Win10上开启 dotNetFramework 2.0/3.5 功能
问题: 在Windows 8.Windows 10上安装一些软件时,系统可能会报出如下错误:你的电脑上的应用需要使用以下Windows功能: 解决方式: 首先呢,你需要准备好一个Win8/ ...
- 从零开始使用git第三篇:git撤销操作、分支操作和常见冲突
从零开始使用git 第三篇:git撤销操作.分支操作和常见冲突 第一篇:从零开始使用git第一篇:下载安装配置 第二篇:从零开始使用git第二篇:git实践操作 第三篇:从零开始使用git第三篇:gi ...
- 自定义npm包的创建、发布、更新和撤销
大纲 1.准备2.自定义npm包3.发布自定义npm包4.引用npm包5.更新npm包6.撤销发布的npm包 简书原文 https://www.jianshu.com/p/d737bc5df5b7 1 ...