保边滤波之Mean shift filter

﹥>﹥吖頭↗ 提交于 2020-12-08 16:29:00

                          Mean shift filter

目录

                          Mean shift filter

一、算法原理

二、练手实现的算法代码如下:

三、实现结果




一、算法原理

在OpenCV中,meanshift filter函数为 pyrMeanShiftFiltering, 它的函数调用格式如下:

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

Parameters:

  • src – The source 8-bit, 3-channel image. //三通道的输入图像
  • dst – The destination image of the same format and the same size as the source. //相同尺寸格式输出图像
  • sp – The spatial window radius.  //空间域半径
  • sr – The color window radius.  //颜色域半径
  • maxLevel – Maximum level of the pyramid for the segmentation. //分割用金字塔的最大层数
  • termcrit – Termination criteria: when to stop meanshift iterations. //迭代终止的条件

算法的描述大致如下:

对于输入图像的每个像素点(X,Y) ,在它的半径为sp的空间域,执行meanshift迭代算法,

像素点(X,Y)的颜色值为(R,G,B), 它的空间邻域点(x,y)的颜色值为(r,g,b),如果点(x,y)的到(X,Y)的颜色距离小于sr,则满足条件,最终我们求得满足条件点的平均空间坐标(X’,Y’)以及平均颜色向量(R',G',B'),并把它们作为下一次迭代的输入。

迭代结果后,我们把最初输入位置的颜色值用最终迭代的颜色值代替。

 

二、练手实现的算法代码如下:

#include"cv.h"
#include"highgui.h"
#include"cxcore.h"
#include<stdio.h>
#include<stdlib.h>
#include"types.h"

void ToYIQ(IplImage *img,IplImage *YIQ_img)
{
	int i,j;
	float Y,I,Q;
	uchar R,G,B;

	for(i = 0 ;i < img->height;i++)
	{
		for(j = 0; j < img->width;j++)
		{
			B = ((uchar*)(img->imageData+i*img->widthStep))[j*img->nChannels+0];
			G = ((uchar*)(img->imageData+i*img->widthStep))[j*img->nChannels+1];
			R = ((uchar*)(img->imageData+i*img->widthStep))[j*img->nChannels+2];


			Y = 0.299*R +0.587*G +0.114*B;
			I = 0.5957*R-0.2744*G-0.3212*B;
			Q = 0.2114*R-0.5226*G+0.3111*B;

			((float*)(YIQ_img->imageData+i*YIQ_img->widthStep))[j*YIQ_img->nChannels+0] = Y;
			((float*)(YIQ_img->imageData+i*YIQ_img->widthStep))[j*YIQ_img->nChannels+1] = I;
			((float*)(YIQ_img->imageData+i*YIQ_img->widthStep))[j*YIQ_img->nChannels+2] = Q;

		}
	}
}


void Mean_Shift_Filter(IplImage *img, IplImage *YIQ_img, IplImage *mean_shift_img,int rad,int colordis )
{
	float shift,Yc,Ic,Qc,YcOld,IcOld,QcOld,Y2,I2,Q2,dY,dI,dQ,mY,mI,mQ,num_;
	int i,j,ry,rx,y2,x2,xc,yc,xcOld,ycOld,num,dx,dy,iters,mx,my;
	uchar R,G,B;

	shift  = 0;
	iters  = 0;
	for(i = 0; i < img->height;i++)
	{
		for(j = 0; j < img->width;j++)
		{
			yc = i;
			xc = j;

			Yc = ((float*)(YIQ_img->imageData+yc*YIQ_img->widthStep))[xc*YIQ_img->nChannels+0];
			Ic = ((float*)(YIQ_img->imageData+yc*YIQ_img->widthStep))[xc*YIQ_img->nChannels+1];
			Qc = ((float*)(YIQ_img->imageData+yc*YIQ_img->widthStep))[xc*YIQ_img->nChannels+2];

			iters = 0;
			do{
				ycOld = yc;
				xcOld = xc;
				YcOld = Yc;
				IcOld = Ic;
				QcOld = Qc;

				mx = 0;
				my = 0;
				mY = 0;
				mI = 0;
				mQ = 0;
				num = 0;
/*///
假若是rad= 3,则其邻域范围为:其中#为包含在内需要计算的像素点
				***#***
				**###**
				*#####*
				#######
				*#####*
				**###**
				***#***
///*/
				for(ry = - rad;ry <= rad;ry++)
				{
					y2 = yc+ry;
					if(y2 >= 0 && y2 < img->height)
					{
						for(rx = -rad;rx <= rad;rx++)
						{
							x2 = xc+rx;
							if(x2 >= 0 && x2 < img->width)
							{
								if(ry*ry+rx*rx <= rad*rad)
								{
									Y2 = ((float*)(YIQ_img->imageData+y2*YIQ_img->widthStep))[x2*YIQ_img->nChannels+0];
									I2 = ((float*)(YIQ_img->imageData+y2*YIQ_img->widthStep))[x2*YIQ_img->nChannels+1];
									Q2 = ((float*)(YIQ_img->imageData+y2*YIQ_img->widthStep))[x2*YIQ_img->nChannels+2];

									dY = Yc-Y2;
									dI = Ic- I2;
									dQ = Qc- Q2;

									if(dY*dY + dI*dI+dQ*dQ <= colordis)//当前像素比周围邻域像素相差比较大的时候,在rad半径内(一个菱形领域),只有当前像素满足此条件,所以最后替换当前像素还是自己本身
									{
										mx += x2;
										my += y2;
										mY += Y2;
										mI += I2;
										mQ += Q2;
										num++;
									}

								}
							}
						}

					}
				}
			num_ = 1.0/num;
			Yc = mY*num_;
			Ic = mI*num_;
			Qc = mQ*num_;
			xc = (int)(mx*num_+0.5);
			yc = (int)(my*num_+0.5);
			
			dx = xc - xcOld;
			dy = yc -ycOld;
			dY = Yc - YcOld;
			dI = Ic- IcOld;
			dQ = Qc - QcOld;

			shift = dx*dx+dy*dy+dY*dY+dI*dI+dQ*dQ;
			iters++;
			}
			while(shift> 3 && iters < 100);

			R = CLIP((int)(Yc+0.9653*Ic+0.6210*Qc),0,255);
			G = CLIP((int)(Yc-0.2721*Ic-0.6473*Qc),0,255);
			B = CLIP((int)(Yc-1.1070*Ic+1.7046*Qc),0,255);

			((uchar*)(mean_shift_img->imageData+i*mean_shift_img->widthStep))[j*mean_shift_img->nChannels+0] = B;
			((uchar*)(mean_shift_img->imageData+i*mean_shift_img->widthStep))[j*mean_shift_img->nChannels+1] = G;
			((uchar*)(mean_shift_img->imageData+i*mean_shift_img->widthStep))[j*mean_shift_img->nChannels+2] = R;	
		}
	}

}
int main(void)
{
	int w,h;
	CvSize img_size;
	IplImage *src,*mean_shift_img,*YIQ_img;

	src = cvLoadImage("lg-image4.bmp",-1);
	if(src == NULL)
	{
		printf("Please insert image\n");
		return 0;
	}
	img_size = cvGetSize(src);
	w = img_size.width;
	h = img_size.height;


	YIQ_img = cvCreateImage(img_size,IPL_DEPTH_32F,3);
	mean_shift_img = cvCreateImage(img_size,IPL_DEPTH_8U,3);

	if((mean_shift_img == NULL) || (YIQ_img==NULL))
		printf("malloc for image memory failure!\n");


	cvZero(YIQ_img);
	cvZero(mean_shift_img);


	cvNamedWindow("Show original image",0);
	cvShowImage("Show original image",src);
	
	ToYIQ(src,YIQ_img);
	Mean_Shift_Filter(src, YIQ_img, mean_shift_img,5,15*15 );
	cvNamedWindow("Mean Shift image",0);
	cvShowImage("Mean Shift image",mean_shift_img);
	cvSaveImage("lg-image4_Mean_Shift_filter.bmp",mean_shift_img);

	cvWaitKey(0);
	cvDestroyWindow("Mean Shift image");
	cvDestroyWindow("Show original image");
	printf("done\n");
	cvReleaseImage(&YIQ_img);
	cvReleaseImage(&mean_shift_img);

	return 0;
}

三、实现结果

左侧为原始图像;右侧为结果图像
标题左侧为原始图像;右侧为结果图像

 

但是针对椒盐噪声,其效果基本无效:

左侧为原始图像;右侧为添加椒盐噪声图像

 

左侧为原始图像mean_shift filter之后结果;右侧为添加椒盐噪声图像mean_shift_filter处理结果

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!