冈萨雷斯数字图像处理第三章空间滤波

3.5.1均值滤波

1.数学原理
概念:“把每个像素都用周围的8个像素来做均值操作 ”
作用:平滑图像的用处, 有的图像的锐度很高,用这样的均值算法,可以把锐度降低。使得图像看上去更加自然
两种3x3的平滑滤波器(线性滤波):3x3平均滤波器R=1/9 ∑Zi------滤波器模板系数都为1; 3x3加权滤波器R=1/16∑Zi,滤波器模板系数不同,中心点影响最大。
均值滤波就是将滤波器模板和图像像素进行卷积。

需要注意的是滤波模板(卷积核)必须为奇数
在这里插入图片描述

2.实现代码
第一;opencv自带的均值滤波函数为
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
参数解释:
. InputArray src: 输入图像,可以是Mat类型,图像深度是CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F其中的某一个。
. OutputArray dst: 输出图像,深度和类型与输入图像一致
. Size ksize: 滤波模板(卷积核)kernel的尺寸,一般使用Size(w, h)来指定,如Size(3,3)
. Point anchor=Point(-1, -1): 字面意思是锚点(卷积核的中心点),也就是处理的像素位于kernel的什么位置,默认值为(-1, -1)即位于kernel中心点,如果没有特殊需要则不需要更改
. int borderType=BORDER_DEFAULT: 用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <vector>
#include <opencv2/imgproc/imgproc.hpp>
#include<cmath>
using namespace std;
using namespace cv;
int main() 
{
	Mat src, dst;
	src = imread("c:/users/征途/desktop/vs-cpp/project3/04.jpg");
	blur(src,dst, Size(3, 3));
	imshow("src", src);
	imshow("dst", dst);
	waitKey(0);
	return 0;
}

第二,自己用c++实现均值滤波函数

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <vector>
#include <opencv2/imgproc/imgproc.hpp>
#include<cmath>
using namespace std;
using namespace cv;


void myblur(const Mat& src, Mat& dst, Size ksize)
{
	//若模板不是奇数模板,则报错退出
	if (ksize.width % 2 == 0 || ksize.height % 2 == 0)
	{
		cout << "please input odd ksize" << endl;
		exit(-1);
	}

	//根据ksize大小扩充模板边界
	int awidth = (ksize.width - 1) / 2;  //公式中的a 
	int aheight = (ksize.height - 1) / 2;// 公式中的b
	Mat asrc;
	//图像边界扩充函数copyMakeBorder,void copyMakeBorder( const Mat& src, Mat& dst,int top, int bottom, int left, int right,int borderType, const Scalar& value = Scalar() );
	copyMakeBorder(src, asrc, aheight, aheight, awidth, awidth, BORDER_DEFAULT);
	//根据图像通道数遍历图像求均值
	//通道数为1

	if (src.channels() == 1)
	{
		for (int i = 0+aheight; i < src.rows + aheight; i++) // i < 扩充后图片高
		{
			for (int j = 0+awidth; j < src.cols + awidth; j++)
			{
				int sum = 0;	//sum是成绩和
				int mean = 0;   //mean是均值
				for (int k = i - aheight; k <= i + aheight; k++) 
				{
					for (int l = j - awidth; l <= j + awidth; l++)
					{
						sum += asrc.at<uchar>(k, l);  //模板系数和像素乘积的和,采用的是3x3都为1的平均滤波模板
					}
				}
				mean = sum / (ksize.width*ksize.height); // 乘积和/模板系数和
				dst.at<uchar>(i - aheight, j - awidth) = mean;
			}
		}
	}
	//通道数为3	
	if (src.channels() == 3)
	{
		for (int i = aheight; i < src.rows + aheight; i++)
		{
			for (int j = awidth; j < src.cols + awidth; j++)
			{
				int sum[3] = { 0 };
				int mean[3] = { 0 };
				for (int k = i - aheight; k <= i + aheight; k++)
				{
					for (int l = j - awidth; l <= j + awidth; l++) //依次根据通道读取每个通道上的,模板系数和像素乘积的和
					{
						sum[0] += asrc.at<Vec3b>(k, l)[0];
						sum[1] += asrc.at<Vec3b>(k, l)[1];
						sum[2] += asrc.at<Vec3b>(k, l)[2];
					}
				}
				for (int m = 0; m < 3; m++) //依次循环按照通道数将卷积后的结果赋予dst
				{
					mean[m] = sum[m] / (ksize.width*ksize.height);
					dst.at<Vec3b>(i - aheight, j - awidth)[m] = mean[m];
				}
			}
		}
	}
}

int main() 
{
	Mat src, dst1,dst2;
	src = imread("c:/users/征途/desktop/vs-cpp/project3/04.jpg");
	//opencv自带函数
	blur(src,dst1, Size(3, 3));
	imshow("src", src);
	imshow("dst1", dst1);
	//自己编写的函数
	dst2 = Mat::zeros(src.size(), src.type());
	myblur(src, dst2, Size(3, 3));
	imshow("dst2", dst2);
	waitKey(0);
	return 0;
}

结果:1为原图,2为系统自带函数结果,3为自己编写结果
在这里插入图片描述

3.5.2中值滤波

1.数学原理
先将掩膜中欲求的像素及其领域内的像素值排序,确定出中值,并将中值赋予给该像素点。
针对随机噪声,椒盐噪声效果好,可以保护图像尖锐边缘;对高斯噪声效果差。
应用:中值滤波对脉冲噪声(如椒盐噪声)的抑制十分有用。

缺点:易造成图像的不连续性
在这里插入图片描述

2.实现代码
实现的思路:用一个数组存储模板中所有的像素值,进行排序,取数组中的中间值赋值给当前像素点

1.opencv自带函数为medianBlur()
2.c++自己编写函数mymedianBlur()
代码实现:
在这里插入图片描述
在这里插入图片描述

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <vector>
#include <opencv2/imgproc/imgproc.hpp>
#include<cmath>
using namespace std;
using namespace cv;
//首先进行编写冒泡排序算法
void bublle_sort(std::vector<int> &arr)
{
	bool flag = true;//默认数组排序已经排好
	for (int i = 0; i < arr.size() - 1; ++i)
	{
		while (flag = false)   //如果排序错误,则执行循环体中的语句
		{
			for (int j = 0; j < arr.size() - 1 - i; ++j)
			{
				if (arr[j] > arr[j + 1])
				{
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
					flag = true;
				}
			}
		}
	}
}

//用c++实现中值滤波
void mymedianBlur(cv::Mat& src, cv::Mat& dst, cv::Size ksize)//Mat &a的意思是创建一个矩阵a的引用
{
	//图像边界扩充
	if (ksize.width % 2 == 0 || ksize.height % 2 == 0) //卷积核要取奇数
	{
		fprintf(stderr, "Please enter odd size!");
		exit(-1);
	}
	int hh = (ksize.height - 1) / 2;
	int hw = (ksize.width - 1) / 2;
	cv::Mat Newsrc;
	cv::copyMakeBorder(src, Newsrc, hh, hh, hw, hw, cv::BORDER_REFLECT_101);//图像扩充,以边缘为轴,对称
	dst = cv::Mat::zeros(src.rows, src.cols, src.type());

	//编写中值滤波,先进行滑窗遍历图像来获取像素点像素
	//首先遍历图像源
	for (int i = hh; i < src.rows + hh; i++)
	{
		uchar* ptrdst = dst.ptr(i - hh);   //滑窗内的ptrdst
		for (int j = hw; i < src.cols + hw; j++)
		{
			std::vector<int> pix; //定义一个std内存中的容器,容器名叫pix,容器内的值为int类型,与数组类似,但是容器的大小可变

		//使用滑窗算法遍历
			for (int r = i - hh; r <= i + hh; r++)   //定义滑窗的高
			{
				const uchar* ptrsrc = Newsrc.ptr(r);  //指针取原图位于滑窗内的位置r处的元素
				for (int c = j - hw; c <= j + hw; c++) // 滑窗的宽
				{
					pix.push_back(ptrsrc[c]);  //在r的前提下,利用指针获取处的元素
				}
			}
			//对读取出来的元素冒泡排序
			bublle_sort(pix);
			ptrdst[j - hw] = pix[(ksize.area() - 1) / 2];//将排序后的中值映射给输出图像

		}
	}
}
	// 主函数应用
int main()
{
	cv::Mat src = cv::imread("c:/users/征途/desktop/vs-cpp/project3/09中值.jpg");
	Mat dst1;
	Mat dst2;
	cv::Size ksize(5, 5);
	mymedianBlur(src, dst1, ksize);
	medianBlur(src, dst2, (5,5));
	imshow("my", dst1);
	imshow("cv", dst2);
	waitKey(0);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值