[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波

[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波

上课老师没有详细讲,自己查阅了一下然后做了点程序验证一下

目录

  • [OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波
    • 中值滤波
    • 快速中值滤波
      • 直方图
      • 快速中值滤波过程
    • OpenCV中API
    • 总结:

中值滤波

简单来说就是用一个nn的框框(n为奇数)去框一个图像,框选中的nn个像素点中取中值,然后用中值取代替框框中间的那个点的像素
哎说起来简单,代码有点烧脑,直接撸上来

void MF(Mat& image_input, Mat& image_output, int diameter)
{
	int  radius = (diameter - 1) / 2;//向下取整
	int i, j, p, q, t,sum = 0,median;
	uchar val;
	int Hist[256] = { 0 };
	int Threshold = (diameter*diameter + 1) / 2;

	for (i = 0; i < image_input.rows; i++)
	{
		for (j = 0; j <radius; j++)
		{
			image_output.at<uchar>(i, j) = image_input.at<uchar>(i, j);
			image_output.at<uchar>(i, (image_input.cols - j - 1)) = image_input.at<uchar>(i, (image_input.cols - j - 1));
		}
	}
	for (j = 0; j < image_input.cols; j++)
	{
		for (i = 0; i < radius; i++)
		{
			image_output.at<uchar>(i, j) = image_input.at<uchar>(i, j);
			image_output.at<uchar>((image_input.rows - i - 1), j) = image_input.at<uchar>((image_input.rows - i - 1), j);
		}
	}
	//处理完了边缘部分
	for (i = radius; i < (image_input.rows - radius); i++)
	{
		for (j = radius; j < (image_input.cols - radius); j++)
		{
			memset(Hist, 0, sizeof(Hist));  //初始化直方图;
			for (p = i - radius; p <= i + radius; p++)
			{
				for (q = j - radius; q <= j + radius; q++)
				{
					val = image_input.at<uchar>(p, q);
					Hist[val]++;
				}
			}
			for (t = 0; t < 256; t++)
			{
				sum = sum + Hist[t];
				if (sum >= Threshold)
				{
					sum = 0;
					median = t;
					break;
				}
			}
			//中值找到,需要替换
			image_output.at<uchar>(i, j) = (uchar)median;
		}
	}
}

快速中值滤波

这里推荐一篇文献:A Fast Two-Dimensional Median Filtering Algorithm
在这里插入图片描述
这个讲的就是中值滤波是对每个可以滤波的点进行一遍中值计算,运算量还是比较大的。
但是,快速中值滤波简化了中值计算
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第1张图片

如图所示,对于同一行的像素点,其实它是向右平移一格,其实不需要计算所有像素点的中值:只要去掉原来的最左边一列,然后加上新的最右边一列,就可以形成新的直方图

直方图

这里用直方图的方法计算中值,对于灰度图来说一共有256级,可以新建一个数组作为直方图,当出现某一级灰度,变在这个直方图数组对应的灰度数组上加1;

快速中值滤波过程

一:建立第一个窗口的直方图,并找到中值。然后计算这个窗口里小于中值的像素个数作为itmdn
二:移动到下一个窗口,然后删掉最左边一列,加上最右边一列,然后更新灰度直方图,更新itmdn,这时候的itmdn存储的是当前窗口中小于上一个窗口中值的像素个数。
三:比较itmdn和(窗口像素数/2),会有三种可能,
若itmdn大,中值左移;
若itmdn小,中值右移;
若一样大,中值不移动;
四:重复步骤二

上代码:

void  FMF(Mat& image_input, Mat& image_output,int diameter)  //用于提取边缘,保留不动
{
	int  radius = (diameter-1)/2;//向下取整
	int i, j, p, q, s, t;
	uchar val;
	int sum = 0;
	int Hist[256] = { 0 };
	int Threshold = (diameter*diameter + 1) / 2;
	int median;

	for (i = 0; i < image_input.rows; i++)
	{
		for (j = 0; j <radius; j++)
		{
			image_output.at<uchar>(i,j) = image_input.at<uchar>(i, j);
			image_output.at<uchar>(i,(image_input.cols-j-1)) = image_input.at<uchar>(i, (image_input.cols - j - 1));
		}
	}
	for (j = 0; j < image_input.cols; j++)
	{
		for (i = 0; i < radius; i++)
		{
			image_output.at<uchar>(i,j) = image_input.at<uchar>(i, j);
			image_output.at<uchar>((image_input.rows-i-1),j) = image_input.at<uchar>((image_input.rows - i - 1), j);
		}
	}
	//处理完了边缘部分
   
	for (i =  radius; i < (image_input.rows - radius); i++)
	{
		for (j =  radius; j < (image_input.cols - radius); j++)
		{
			if (j == radius)  //表示每行第一个需要滤波的元素,需要对它进行直方图建立
			{
				memset(Hist, 0, sizeof(Hist));  //初始化直方图;
				for ( p = i - radius; p <=i + radius; p++)
				{
					for ( q = j - radius; q <= j + radius; q++)
					{
					    val = image_input.at<uchar>(p, q);
						Hist[val]++;
					}
				}
			}//判断结束表示直方图建立完毕,后面要做的是更新直方图
			//如果j != radius表示直方图处于更新中
			if (j != radius)
			{
				for (s = i - radius; s <= i + radius; s++)
				{
					val = image_input.at<uchar>(s, j - radius - 1);
					Hist[val]--;
					val = image_input.at<uchar>(s, j + radius);
					Hist[val]++;
				}//直方图更新完毕
			}
			//寻找中值
			for ( t = 0; t < 256; t++)
			{
				sum = sum + Hist[t];
				if (sum >= Threshold)
				{
					sum = 0;
					median = t;
					break;
				}
			}
         //中值找到,需要替换
			image_output.at<uchar>(i, j) = (uchar)median;
		}
	}
}

OpenCV中API

简单的很,一句话搞定

medianBlur(src, dst, 9);

总结:

对外输出图片的中值滤波处理时间

    Mat dst = Mat::zeros(src.size(),CV_8UC1);
	start = clock();
	FMF(src, dst, 9);
	end = clock();
	start = (end - start) / CLOCKS_PER_SEC;
	printf("the fast filter consume time is %f s\n", start);
	namedWindow("fast filter", WINDOW_AUTOSIZE);
	imshow("fast filter", dst);
	dst = Mat::zeros(src.size(), CV_8UC1);

上结果
当窗口直径为3时
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第2张图片
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第3张图片

当窗口直径为9时
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第4张图片
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第5张图片

当窗口仍然为9时,将图变大
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第6张图片
[OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波_第7张图片

所以啊:
1、快速中值滤波有一定的加快计算速度的效果,

2、而且随着窗口的变大,中值滤波和快速中值滤波所用的时间相差的越大,

3、当图片的大小越大,中值滤波和快速中值滤波所用的时间相差的越大(快速中值滤波比中值滤波快的更明显),

4、滤波窗口越大,图像越模糊,计算量越大

5、当然,都没有API快,hhhh

最后,钢炼yyds

你可能感兴趣的:([OpenCV+VS2015]中值滤波、快速中值滤波和OpenCV中API中值滤波)