meanshift
1. 原理
用meanshift做图像平滑和分割,其实是一回事。其本质是经过迭代,将收敛点的像素值代替原来的像素值,从而去除了局部相似的纹理,同时保留了边缘等差异较大的特征。
OpenCV中自带有基于meanshift的分割方法pyrMeanShiftFiltering()。由函数名pyrMeanShiftFiltering可知,这里是将meanshift算法和图像金字塔相结合用来分割的。
[cpp] view plain copy
- <span style="font-size:18px;">void PyrMeanShiftFiltering( const CvArr* srcarr, //输入图像
- CvArr* dstarr, //输出图像
- double sp, //颜色域半径
- double sr, //空间域半径
- int max_level, //金字塔最大层数
- CvTermCriteria termcrit ) //迭代终止条件</span>
要求输入和输出图像都是CV_8UC3类型,而且两者尺寸一样。实际上并不需要去先定义dstarr,因为程序里会将srcarr的格式赋值给dstarr。
termcrit有三种情况,迭代次数、迭代精度和两者同时满足。默认为迭代次数为5同时迭代精度为1。termcrit是个结构体,其结构如下
[cpp] view plain copy
- <span style="font-size:18px;">typedef struct CvTermCriteria
- {
- int type; /*CV_TERMCRIT_ITER或CV_TERMCRIT_EPS 或二者都是*/
- int max_iter; /* 最大迭代次数 */
- double epsilon; /* 结果的精确性 */
- }
- CvTermCriteria;</span>
在实际操作时,为了使分割的结果显示得更明显,经常用floodFill( )将不同连通域涂上不同的颜色。具体情况参看下 面的实例。
2. 程序实例
来看看opencv自带的一个用meanshift进行分割的例子
原程序见 “ .\OpenCV249\sources\samples\cpp\meanshift_segmentation.cpp”
[cpp] view plain copy
- <span style="font-size:18px;">#include "opencv2/highgui/highgui.hpp"
- #include "opencv2/core/core.hpp"
- #include "opencv2/imgproc/imgproc.hpp"
- #include <iOStream>
- using namespace cv;
- using namespace std;
- static void help(char** argv)
- {
- cout << "\nDemonstrate mean-shift based color segmentation in spatial pyramid.\n"
- << "Call:\n " << argv[0] << " image\n"
- << "This program allows you to set the spatial and color radius\n"
- << "of the mean shift window as well as the number of pyramid reduction levels explored\n"
- << endl;
- }
- //This colors the segmentations
- static void floodFillPostprocess( Mat& img, const Scalar& colorDiff=Scalar::all(1) )
- {
- CV_Assert( !img.empty() );
- RNG rng = theRNG();
- Mat mask( img.rows+2, img.cols+2, CV_8UC1, Scalar::all(0) );
- for( int y = 0; y < img.rows; y++ )
- {
- for( int x = 0; x < img.cols; x++ )
- {
- if( mask.at<uchar>(y+1, x+1) == 0 )
- {
- Scalar newVal( rng(256), rng(256), rng(256) );
- floodFill( img, mask, Point(x,y), newVal, 0, colorDiff, colorDiff );
- }
- }
- }
- }
- string winName = "meanshift";
- int spatialRad, colorRad, maxPyrLevel;
- Mat img, res;
- static void meanShiftSegmentation( int, void* )
- {
- cout << "spatialRad=" << spatialRad << "; "
- << "colorRad=" << colorRad << "; "
- << "maxPyrLevel=" << maxPyrLevel << endl;
- pyrMeanShiftFiltering( img, res, spatialRad, colorRad, maxPyrLevel );
- //Mat imgGray;
- //cvtColor(res,imgGray,CV_RGB2GRAY);
- //imshow("res",res);
- floodFillPostprocess( res, Scalar::all(2) );
- imshow( winName, res );
- }
- int main(int argc, char** argv)
- {
- img = imread("rubberwhale1.png");
- //img = imread("pic2.png");
- if( img.empty() )
- return -1;
- spatialRad = 10;
- colorRad = 10;
- maxPyrLevel = 1;
- namedWindow( winName, WINDOW_AUTOSIZE );
- //imshow("img",img);
- createTrackbar( "spatialRad", winName, &spatialRad, 80, meanShiftSegmentation );
- createTrackbar( "colorRad", winName, &colorRad, 60, meanShiftSegmentation );
- createTrackbar( "maxPyrLevel", winName, &maxPyrLevel, 5, meanShiftSegmentation );
- meanShiftSegmentation(0, 0);
- //floodFillPostprocess( img, Scalar::all(2) );
- //imshow("img2",img);
- waitKey();
- return 0;
- }</span>
int floodFill( InputOutputArray image, Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 );
int floodFill( InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 );
InputOutputArray image 输入输出图像,要求格式为1通道或3通道,8位或浮点
InputOutputArray mask 掩膜,比image的宽和高各大两像素点
Point seedPoint 填充的起始点
Scalar newVal 像素点被染色的值
CV_OUT Rect* rect=0 可选参数,设置floodFill()要重绘区域的最小边界矩形区域
Scalar loDiff=Scalar() 定义当前像素值与起始点像素值的亮度或颜色负差的最大值
Scalar upDiff=Scalar() 定义当前像素值与起始点像素值的亮度或颜色正差的最大值
flags 操作标志符
程序结果
处理后一些细小的纹理都平滑掉了,例如图中绿色线条所指示的区域。未填充时,很多地方看得并不明显,填充后就能明显看出差别来了。填充后的图很好地体现了meanshift聚类的思想!
再来看一组更“夸张”的效果图
使用meanshift方法进行处理后,原来的三个矩形区域消失了!平滑掉了!
meanshift算法的两个关键参数是空间域半径sr和颜色域半径sp,别说max_level,那是构建图像金字塔的参数好吧。最后,我们来看看sr和sp对结果的影响。
显然颜色域半径sp对结果的影响比空间域半径sr对结果的影响大。sp和sr越小,细节保留得越多,sp和sr越大,平滑力度越大。边缘和颜色突变的区域的特征保留的较好。因为meanshift要对每个像素点进行操作,所以算法的时间花销很大。
相关阅读
本篇文章介绍了显著性图像的分割,这个方法最初来源于对于发掘一个自动生成图像三分图方法的兴趣,这个抠图算法用于关注前景和背景细
1. 将代码复制到txt文本中,后缀改成.vbs 2.和要分割的文件放在同一目录下并执行该程序 3.执行完后会弹框报错,不要紧。 Set fs =Cr
在所有的比例中黄金分割是最能引起人的美感的,0.618被公认为最具有审美意义的比例数字。黄金分割之所以那么普遍的流行,我猜一定跟
对于监督学习算法而言,数据决定了任务的上限,而算法只是在不断逼近这个上限。世界上最遥远的距离就是我们用同一个模型,但是却有不同
猴子吃香蕉, 分割数组(猴子吃香蕉可是掰成好几段来吃哦)把一个数组arr按照指定的数组大小size分割成若干个数组块。例如:chunk([1,2