中值滤波的原理很简单:就是用滑动窗口中灰度中值代替窗口中心像素的灰度值


高效中值滤波:

高效中值滤波的方法及实现_第1张图片

高效中值滤波的方法及实现_第2张图片

高效中值滤波的方法及实现_第3张图片

代码实现:
//中值滤波
//窗口大小为width_Aperture*width_Aperture的正方形
void MedianBlur(const Mat &p_w_picpath_Src, Mat &p_w_picpath_Dst, int width_Aperture)
{
	/////////////重新分配图像(如果需要)/////////////////////
	int width_Dst=p_w_picpath_Src.cols;
	int height_Dst=p_w_picpath_Src.rows;
	p_w_picpath_Dst.create(Size(width_Dst,height_Dst),CV_8UC1);//如果重新分配,之前的空间会扔掉
	p_w_picpath_Dst.setTo(Scalar(0));//置为0

	//滑动窗口
	int pixelCount=width_Aperture*width_Aperture;//窗口内像素总个数
	Mat p_w_picpath_Aperture(width_Aperture,width_Aperture,CV_8UC1);//滑动窗口图像

	//直方图
	Mat histogram;
	int histogramSize=256;//灰度等级
	int thresholdValue=pixelCount/2+1;//step1.设置阈值(步骤参考:图像的高效编程要点之四)

	//计算起点坐标
	int startX=width_Aperture/2;
	int startY=width_Aperture/2;

	//第一行
	//这里需要设置3个指针:这三个指针绑定在一起,一起滑动
	//1.源图像中被处理的像素 
	//2.目标图像被处理的像素 
	//3.源图像滑动窗口
	uchar *row_Src=p_w_picpath_Src.data+startY*width_Dst+startX;//源图像
	uchar *row_Dst=p_w_picpath_Dst.data+startY*width_Dst+startX;//目标图像
	uchar *row_Aperture_Src=p_w_picpath_Src.data;//源图像中的滑动窗口
	
	for (int y=startY;y<=height_Dst-startY-1;++y)
	{
		//列
		uchar *col_Src=row_Src;
		uchar *col_Dst=row_Dst;
		uchar *col_Aperture_Src=row_Aperture_Src;//操作整个滑动窗口
		
		///////////////对滑动窗口操作//////////////////
		//计算每行第一个滑动窗口直方图
		//提取滑动窗口图像
		uchar *row_Aperture=p_w_picpath_Aperture.data;
		uchar *row_Aperture_Src_2=col_Aperture_Src;
		for (int k=0;k<=width_Aperture-1;++k)
		{
			//列
			uchar *col_ApertureImage=row_Aperture;
			uchar *col_Aperture_Src_2=row_Aperture_Src_2;
			
			for (int w=0;w<=width_Aperture-1;++w)
			{
				//处理每个像素
				col_ApertureImage[0]=col_Aperture_Src_2[0];
				
				//下一个像素
				col_ApertureImage++;
				col_Aperture_Src_2++;
			}

			//下一行
			row_Aperture+=width_Aperture;
			row_Aperture_Src_2+=width_Dst;
		}

		//step 2.确定中值,并记录亮度<=中值的像素点个数
		//求直方图
		calcHist(&p_w_picpath_Aperture, 
			1,//Mat的个数
			0,//用来计算直方图的通道索引,通道索引依次排开
			Mat(),//Mat()返回一个空值,表示不用mask,
			histogram, //直方图
			1, //直方图的维数,如果计算2个直方图,就为2
			&histogramSize, //直方图的等级数(如灰度等级),也就是每列的行数
			0//分量的变化范围
			);
		
		//求亮度中值和<=中值的像素点个数
		int medianValue,pixleCountLowerMedian;
		CalculateImage_MedianGray_PixelCount(histogram,pixelCount,medianValue,pixleCountLowerMedian);
		////////////滑动窗口操作结束///////////////////////
		
		//滤波
		col_Dst[0]=(uchar)medianValue;
		
		//滑动一个像素(三个指针在一起移动)
		col_Dst++;
		col_Src++;
		col_Aperture_Src++;
		for (int x=startX+1;x<=width_Dst-startX-1;++x)//从每行第二个滤波像素开始
		{
			////////////维持滑动窗口直方图//////////////
			//step 3.去掉左侧
			uchar *col_Left=col_Aperture_Src-1;
			float *data=(float*)histogram.data;
			for (int k=0;k<=width_Aperture-1;++k)
			{
				int gray=col_Left[0];
				data[gray]-=1.0;
				if (gray<=medianValue)
				{
					pixleCountLowerMedian--;
				}
				col_Left+=width_Dst;
			}
			
			//step 4.增加右侧
			uchar *col_Right=col_Aperture_Src+width_Aperture-1;
			for (int k=0;k<=width_Aperture-1;++k)
			{
				int gray=col_Right[0];
				data[gray]+=1.0;
				if (gray<=medianValue)
				{
					pixleCountLowerMedian++;
				}
				col_Right+=width_Dst;
			}

			//搜索新的中值
			if (pixleCountLowerMedian>thresholdValue)//step 6.
			{
				while(1)
				{
					pixleCountLowerMedian-=data[medianValue];
					medianValue--;
					if (pixleCountLowerMedian<=thresholdValue)
					{
						break;
					}
				}
				
			}
			else
			{
				while(pixleCountLowerMedianpixelCount)
		{
			medianValue=i;
			pixleCountLowerMedian=sum;
			break;
		}
	}
}

使用窗口大小为3*3的窗口,运行效果图:

高效中值滤波的方法及实现_第4张图片

高效中值滤波的方法及实现_第5张图片

运行这段代码之前,需要配置一下OpenCV,算法核心和OpenCV没有太大关联。

注意:算法没有处理边界的情况,还不太清楚怎么处理边界,有会的朋友,希望能够一起分享一下边界处理的一些技巧

代码写的不是特别规范,大家有什么看不懂的地方,可以一起讨论讨论