【OpenCV】算数均值滤波,几何均值滤波,中值滤波,自适应滤波代码实现并比较

采用算数均值滤波器,几何均值滤波器,中值滤波,自适应滤波器对图像进行滤波操作,并输出图像。
首先放一下结果:
在这里插入图片描述
(1)算术均值滤波器
计算子窗口的和并求平均
在这里插入图片描述
程序中对图像边缘不处理。右图为加入椒盐噪声,左图为算数均值滤波图像。

在这里插入图片描述在这里插入图片描述
程序实现:

//算数均值滤波
void CImageRecoveryDlg::ArithAverFilter( Mat &src, Mat &dst) {
	if (src.channels() == 3)//彩色图像
	{
		for (int i = 1; i < src.rows; ++i) {
			for (int j = 1; j < src.cols; ++j) {
				if ((i - 1 >= 0) && (j - 1) >= 0 && (i + 1) < src.rows && (j + 1) < src.cols) {//边缘不进行处理
					dst.at<Vec3b>(i, j)[0] = (src.at<Vec3b>(i, j)[0] + src.at<Vec3b>(i - 1, j - 1)[0] + src.at<Vec3b>(i - 1, j)[0] + src.at<Vec3b>(i, j - 1)[0] +
						src.at<Vec3b>(i - 1, j + 1)[0] + src.at<Vec3b>(i + 1, j - 1)[0] + src.at<Vec3b>(i + 1, j + 1)[0] + src.at<Vec3b>(i, j + 1)[0] +
						src.at<Vec3b>(i + 1, j)[0]) / 9;
					dst.at<Vec3b>(i, j)[1] = (src.at<Vec3b>(i, j)[1] + src.at<Vec3b>(i - 1, j - 1)[1] + src.at<Vec3b>(i - 1, j)[1] + src.at<Vec3b>(i, j - 1)[1] +
						src.at<Vec3b>(i - 1, j + 1)[1] + src.at<Vec3b>(i + 1, j - 1)[1] + src.at<Vec3b>(i + 1, j + 1)[1] + src.at<Vec3b>(i, j + 1)[1] +
						src.at<Vec3b>(i + 1, j)[1]) / 9;
					dst.at<Vec3b>(i, j)[2] = (src.at<Vec3b>(i, j)[2] + src.at<Vec3b>(i - 1, j - 1)[2] + src.at<Vec3b>(i - 1, j)[2] + src.at<Vec3b>(i, j - 1)[2] +
						src.at<Vec3b>(i - 1, j + 1)[2] + src.at<Vec3b>(i + 1, j - 1)[2] + src.at<Vec3b>(i + 1, j + 1)[2] + src.at<Vec3b>(i, j + 1)[2] +
						src.at<Vec3b>(i + 1, j)[2]) / 9;
				}
				else {//边缘赋值
					dst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0];
					dst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1];
					dst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2];
				}
			}
		}
	}
	if (src.channels() == 1) {//灰度图像
		for (int i = 0; i < src.rows; i++) {
			for (int j = 0; j < src.cols; j++) {
				if ((i - 1 >= 0) && (j - 1) >= 0 && (i + 1) < src.rows && (j + 1) < src.cols) {//边缘不进行处理
					dst.at<uchar>(i, j) = (src.at<uchar>(i, j) + src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i - 1, j) + src.at<uchar>(i, j - 1) +
						src.at<uchar>(i - 1, j + 1) + src.at<uchar>(i + 1, j - 1) + src.at<uchar>(i + 1, j + 1) + src.at<uchar>(i, j + 1) +
						src.at<uchar>(i + 1, j)) / 9;
				}
				else {//边缘赋值
					dst.at<uchar>(i, j) = src.at<uchar>(i, j);
				}
			}
		}
	}
	imshow("arithAverFilter", dst);
}

(2)几何均值滤波
对子窗口的元素相乘(需要判断是否为零),并对乘积求1/m*n次幂。
在这里插入图片描述
程序中需要判断窗口中的元素是否为0,如果为0则去除该元素。
右图为加入椒盐噪声的图像,左图为几何均值滤波后图像。
在这里插入图片描述在这里插入图片描述
程序实现:

//几何均值滤波
void CImageRecoveryDlg::GeoAverFliter(const Mat &src, Mat &dst) {
	Mat _dst(src.size(), CV_32FC1);
	double power = 1.0 / 9;
	cout << "power:" << power << endl;
	double geo = 1;
	if (src.channels() == 1) {
		for (int i = 0; i < src.rows; i++) {
			for (int j = 0; j < src.cols; j++) {
				if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
					if (src.at<uchar>(i, j) != 0) geo = geo * src.at<uchar>(i, j);
					if (src.at<uchar>(i+1, j+1) != 0) geo = geo * src.at<uchar>(i+1, j+1);
					if (src.at<uchar>(i+1, j) != 0) geo = geo * src.at<uchar>(i+1, j);
					if (src.at<uchar>(i, j+1) != 0) geo = geo * src.at<uchar>(i, j+1);
					if (src.at<uchar>(i+1, j-1) != 0) geo = geo * src.at<uchar>(i+1, j-1);
					if (src.at<uchar>(i-1, j+1) != 0) geo = geo * src.at<uchar>(i-1, j+1);
					if (src.at<uchar>(i-1, j) != 0) geo = geo * src.at<uchar>(i-1, j);
					if (src.at<uchar>(i, j-1) != 0) geo = geo * src.at<uchar>(i, j-1);
					if (src.at<uchar>(i-1, j-1) != 0) geo = geo * src.at<uchar>(i-1, j-1);					
					/*geo = src.at<uchar>(i, j)* src.at<uchar>(i + 1, j + 1)* src.at<uchar>(i + 1, j)* src.at<uchar>(i, j + 1)*
						src.at<uchar>(i + 1, j - 1)* src.at<uchar>(i - 1, j + 1)* src.at<uchar>(i - 1, j)*
						src.at<uchar>(i, j - 1)* src.at<uchar>(i - 1, j - 1);*/
					_dst.at<float>(i, j)= pow(geo, power);
					geo = 1;
					//if (i % 10 == 0&&j%10==0)
						//printf("_dst.at<float>(%d, %d)=%f\n", i, j, _dst.at<float>(i, j));
					

				}
				else
					_dst.at<float>(i, j) = src.at<uchar>(i, j);
			}
		}
	}
	_dst.convertTo(dst, CV_8UC1);

	//_dst.copyTo(dst);//拷贝
	imshow("geoAverFilter", dst);

}

(3)中值滤波
找出子窗口中的中值,将其赋值给中心点。
在这里插入图片描述
程序中Median为寻找中值的函数,参数为子窗口的元素值。
左图为加入椒盐噪声的图像,右图为中值滤波的图像。

在这里插入图片描述在这里插入图片描述
可以看出中值滤波对椒盐噪声有很好的滤波效果。
程序实现:

//返回中值
uchar Median(uchar n1, uchar n2, uchar n3, uchar n4, uchar n5,
	uchar n6, uchar n7, uchar n8, uchar n9) {
	uchar arr[9];
	arr[0] = n1;
	arr[1] = n2;
	arr[2] = n3;
	arr[3] = n4;
	arr[4] = n5;
	arr[5] = n6;
	arr[6] = n7;
	arr[7] = n8;
	arr[8] = n9;
	for (int gap = 9 / 2; gap > 0; gap /= 2)//希尔排序
		for (int i = gap; i < 9; ++i)
			for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap)
				swap(arr[j], arr[j + gap]);
	return arr[4];//返回中值
}
//中值滤波
void CImageRecoveryDlg::MedianFliter(const Mat &src, Mat &dst) {
	if (!src.data)return;
	Mat _dst(src.size(), src.type());
	if (src.channels() == 3) {
		for (int i = 0; i < src.rows; ++i) {
			for (int j = 0; j < src.cols; ++j) {
				if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
					_dst.at<Vec3b>(i, j)[0] = Median(src.at<Vec3b>(i, j)[0], src.at<Vec3b>(i + 1, j + 1)[0],
						src.at<Vec3b>(i + 1, j)[0], src.at<Vec3b>(i, j + 1)[0], src.at<Vec3b>(i + 1, j - 1)[0],
						src.at<Vec3b>(i - 1, j + 1)[0], src.at<Vec3b>(i - 1, j)[0], src.at<Vec3b>(i, j - 1)[0],
						src.at<Vec3b>(i - 1, j - 1)[0]);
					_dst.at<Vec3b>(i, j)[1] = Median(src.at<Vec3b>(i, j)[1], src.at<Vec3b>(i + 1, j + 1)[1],
						src.at<Vec3b>(i + 1, j)[1], src.at<Vec3b>(i, j + 1)[1], src.at<Vec3b>(i + 1, j - 1)[1],
						src.at<Vec3b>(i - 1, j + 1)[1], src.at<Vec3b>(i - 1, j)[1], src.at<Vec3b>(i, j - 1)[1],
						src.at<Vec3b>(i - 1, j - 1)[1]);
					_dst.at<Vec3b>(i, j)[2] = Median(src.at<Vec3b>(i, j)[2], src.at<Vec3b>(i + 1, j + 1)[2],
						src.at<Vec3b>(i + 1, j)[2], src.at<Vec3b>(i, j + 1)[2], src.at<Vec3b>(i + 1, j - 1)[2],
						src.at<Vec3b>(i - 1, j + 1)[2], src.at<Vec3b>(i - 1, j)[2], src.at<Vec3b>(i, j - 1)[2],
						src.at<Vec3b>(i - 1, j - 1)[2]);
				}
				else
					_dst.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);
			}
		}
	}
	if (src.channels() == 1) {
		for (int i = 0; i < src.rows; ++i) {
			for (int j = 0; j < src.cols; ++j) {
				if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
					_dst.at<uchar>(i, j) = Median(src.at<uchar>(i, j), src.at<uchar>(i + 1, j + 1),
						src.at<uchar>(i + 1, j), src.at<uchar>(i, j + 1), src.at<uchar>(i + 1, j - 1),
						src.at<uchar>(i - 1, j + 1), src.at<uchar>(i - 1, j), src.at<uchar>(i, j - 1),
						src.at<uchar>(i - 1, j - 1));
		
				}
				else
					_dst.at<uchar>(i, j) = src.at<uchar>(i, j);
			}
		}
	}
	_dst.copyTo(dst);//拷贝
	imshow("mediaFilter", dst);
}

(4)自适应滤波
Zmin=Sxy中的最小灰度值;
Zmax=Sxy中的最大灰度值;
Zmed=Sxy中的中值;
Zxy=坐标(x,y)处的灰度值;
Smax=Sxy所允许的最大尺寸(在程序中,用kernal_size表示);
左图为加入椒盐噪声的图像,右图为自适应滤波的图像。
在这里插入图片描述在这里插入图片描述
可以看出自适应滤波效果最好,但计算时间比中值滤波长很多,有很长的时延。
程序实现:

#define CV_ROI_ELEM(src,vector,m,n,ks)  \
{                                      \
    uchar* kn;                         \
    int st0=src.step[0];\
    int st1=src.step[1];\
    for(int k=0;k<(ks);k++)            \
    {                                  \
        for(int s=0;s<(ks);s++)        \
        {                              \
            kn =src.data+(k+m)*st0+(s+n)*st1;   \
            vector.push_back(*kn);              \
        }                                       \
    }                                           \
}

#define CV_MAT_ELEM2(src,dtype,y,x) \
    (dtype*)(src.data+src.step[0]*(y)+src.step[1]*(x))
//自适应滤波
void selfAdaptiveFilter(Mat&src, Mat&dst, int kernal_size)
{
	CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8U);
	if (dst.empty())
	{
		dst.create(src.rows, src.cols, CV_8UC1);
	}
	uchar* pdst = dst.data;
	uchar Zmin, Zmax, Zmed, Zxy;
	int step0 = src.step[0];
	int step1 = src.step[1];
	for (int i = kernal_size / 2; i < src.rows - kernal_size / 2; i++)
	{
		for (int j = kernal_size / 2; j < src.cols - kernal_size / 2; j++)
		{
			int ks = 3;//kernal_size;
			int count = 0;
			Zxy = *CV_MAT_ELEM2(src, uchar, i, j);//Sxy覆盖区域的中心点像素值,即锚点像素值
			vector<uchar> v;//将模板覆盖区域的像素,压入矢量v中
			do {
				if (count == 0)
				{//获取模板ks*ks覆盖区域的像素,压入矢量v中
					CV_ROI_ELEM(src, v, i - ks / 2, j - ks / 2, ks);
				}
				else
				{
					/****************下面的for循环,将外扩的四个边的像素添加到v中**************/
					uchar* p = src.data + (i - ks / 2)*step0 + (j - ks / 2)*step1;
					for (int u = 0; u < ks; u++)
					{
						v.push_back(*(p + u * step1));//向外扩展的四个边的上边
						v.push_back(*(p + (ks - 1)*step0 + u * step1));//向外扩展的四个边的下边
						if (u != 0 && u != ks - 1)
						{
							v.push_back(*(p + u * step0));//向外扩展的四个边的左边
							v.push_back(*(p + u * step0 + (ks - 1)*step1));//向外扩展的四个边的右边
						}
					}
				}

				//对v的元素排序
				//排序后,Sxy覆盖区域内,最大值为Zmax=v[v.size-1],最小值为Zmin=v[0]
				std::sort(v.begin(), v.end());
				Zmin = v[0], Zmax = v[v.size() - 1], Zmed = v[ks*ks / 2];
				pdst = CV_MAT_ELEM2(dst, uchar, i, j);
				if (Zmin < Zmed&&Zmed < Zmax)
				{
					if (Zmin < Zxy&&Zxy < Zmax)
					{
						*pdst = Zxy; break;
					}
					else
					{
						*pdst = Zmed; break;
					}
				}
				else
				{
					ks += 2;
				}
				count++;
			} while (ks <= kernal_size);

			*pdst = Zmed;
		}
	}
	imshow("selfAdaptiveFilter", dst);
}

(5)整体比较
算数均值滤波在去除椒盐噪声的同时,图像也变得模糊。
几何均值滤波去除噪声同时,相对于算数均值滤波锐化。
中值滤波相比于前两者效果最好,去除了噪声,且图像损失较小。
自适应滤波是四者中效果最好的,但是计算复杂,有很大的延迟。
在这里插入图片描述
程序为MFC对话框程序,对话框程序如下:

//显示图像
void CImageRecoveryDlg::showMat(Mat &image, int i, String name) {
	CRect pic_rect;
	int width, height;
	//CWnd  *pWnd = GetDlgItem(i);
	GetDlgItem(i)->GetClientRect(&pic_rect);
	width = pic_rect.right;
	height = pic_rect.bottom;
	resize(image, image, Size(width, height));
	imshow(name, image);
}
//打开图像按钮
void CImageRecoveryDlg::OnBnClickedOpenBtn()
{

	CFileDialog fileDlg(true, _T("png"), NULL, 0, _T("image Files(*.bmp; *.jpg;*.png;*.tif)|*.JPG;*.PNG;*.BMP;*.TIF|All Files (*.*) |*.*||"), this);
	fileDlg.DoModal();
	//获取图片路径和图片名称
	strFilePath = fileDlg.GetPathName();
	strFileName = fileDlg.GetFileName();
	//判断路径不为空
	if (strFilePath == _T(""))
	{
		return;
	}

	image.Load(strFilePath);
	CImageToMat(image, src_Mat);

	//cvtColor(src_Mat, gray_Mat, COLOR_BGR2GRAY);//将图像转换为灰度图像
	imshow("src_image", src_Mat);
	showImage(image, IDC_INPUT_PIC);//显示图像
}

//加入噪声函数
void CImageRecoveryDlg::OnBnClickedNoiseBtn()
{
	Mat temp;
	//salt_Mat = src_Mat.clone();
	src_Mat.copyTo(salt_Mat);
	salt(salt_Mat, 0.1);
	temp = salt_Mat.clone();
	showMat(temp, IDC_NOSIC_PIC, "salt_image");
}

//算数均值滤波
void CImageRecoveryDlg::OnBnClickedCouBtn()
{
	Mat temp;
	Mat ArithAver_Mat(salt_Mat.size(), salt_Mat.type());
	cout << "salt_Mat.channels= " << salt_Mat.channels() << endl;//第一个元素值
	ArithAverFilter(salt_Mat, ArithAver_Mat);
	temp = ArithAver_Mat.clone();
	showMat(temp, IDC_COU_MEAN, "arithAver_image");
}

//中值滤波按钮
void CImageRecoveryDlg::OnBnClickedMediaBtn()
{
	Mat temp;
	Mat MedianFilter_Mat(salt_Mat.size(), salt_Mat.type());
	MedianFliter(salt_Mat, MedianFilter_Mat);
	temp = MedianFilter_Mat.clone();
	showMat(temp, IDC_MEDIA_FILTER, "medianFilter_image");
}

//几何均值滤波按钮
void CImageRecoveryDlg::OnBnClickedGeoBtn()
{
	Mat temp;
	Mat GeoAverFilter_Mat(salt_Mat.size(), salt_Mat.type());
	GeoAverFliter(salt_Mat, GeoAverFilter_Mat);
	temp = GeoAverFilter_Mat.clone();
	showMat(temp, IDC_GEO_MEAN, "GeoAverFliter_image");
}

//自适应滤波按钮
void CImageRecoveryDlg::OnBnClickedAdapteBtn()
{
	Mat temp;
	Mat selfAdaptiveFilter_Mat(salt_Mat.size(), salt_Mat.type());
	selfAdaptiveFilter(salt_Mat, selfAdaptiveFilter_Mat, 7);
	temp = selfAdaptiveFilter_Mat.clone();
	showMat(temp, IDC_ADAPT_FILTER, "selfAdaptiveFliter_image");
}

其中下列变量为全局变量

#define	PI	3.141592
CString strFilePath;
CString strFileName;
CImage image;
Mat src_image, gray_image, src_Mat, gray_Mat;
Mat salt_Mat,ArithAver_Mat;

以上为四种滤波函数的对比。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值