OpenCV - Mat、滤波、卷积的实现

1、Mat数据类型

//创建图像
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));

//改变图像尺寸
M.create(4, 4, CV_8UC2);

//快速创建图像的集中方法
Mat E = Mat::eye(4, 4, CV_64F);
Mat F = Mat::ones(2, 2, CV_32F);
Mat G = Mat::zeros(3, 3, CV_8UC1);
Mat M(2, 2, CV_8UC3);
Mat M = Scalar::all(0);

Point p0(1, 2);
Point2f p(6, 2);
Point3f p1(1, 2, 3);

Scalar(10, 10, 10);//蓝 绿 红

//cv中的大小都可以用Size来替代,比如 Mat M(Size(2,2), CV_8UC3, Scalar(0, 0, 255));
Size(5, 5); //宽 高

Rect(10, 10, 20, 20); //左上角x,y,width,height

cvtColor(input,output,code)  //颜色空间转换

2、常用的几种滤波方法

2.1、方框滤波 

void boxFilter( InputArray src, OutputArray dst, int ddepth,
                             Size ksize, Point anchor=Point(-1,-1),
                             bool normalize=true,
                             int borderType=BORDER_DEFAULT );

//方框滤波
boxFilter(image, out, -1, Size(5, 5));

/*
参数1:输入要处理的图像。
参数2:得到处理后的的输出图像。
参数3:图像的深度。-1代表使用原图深度,即src.depth()。
参数4:内核的大小。一般用Size(w,h)来表示内核大小,其中w为像素宽度,h为像素高度,正奇数或0。例:Size(3,3)就代表3×3的核大小。
参数5:表示锚点,即被平滑的那个点。如果这个点坐标是负值的话,就表示取核的中心点为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
参数6:默认值为true,一个标识符,表示内核是否被其区域归一化了
参数7:用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT,我们一般不用管它。
*/

2.2、均值滤波

均值滤波,是最简单的一种线性滤波操作,输出图像的每一个像素是核窗口内输入图像对应像素的像素的平均值( 所有像素加权系数相等),其实说白了它就是归一化后的方框滤波。均值滤波算法比较简单,计算速度快,但是均值滤波本身存在着固有的缺陷,即它不能很好地保护图像细节,在图像去噪的同时,也破坏了图像的细节部分,从而使图像变得模糊,不能很好地去除噪声点。但均值滤波对周期性的干扰噪声有很好的抑制作用。

void blur( InputArray src, OutputArray dst,
                        Size ksize, Point anchor=Point(-1,-1),
                        int borderType=BORDER_DEFAULT );

//均值滤波
blur(image, out1, Size(7, 7));

/*
参数1:输入要处理的图像。
参数2:得到处理后的输出图像。
参数3:内核的大小。一般用Size(w,h)来表示内核大小,其中w为像素宽度,h为像素高度,正奇数或0。若核越大,则处理后的图像越模糊。
参数4:表示锚点,即被平滑的那个点。如果这个点坐标是负值的话,就表示取核的中心点为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
参数5:用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT,我们一般不用管它
*/

以下是自定义的均值滤波实现方式 

// 均值滤波.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

void salt(Mat& image, int num);
void AverFiltering(const Mat& src, Mat& dst);
int main()
{
	Mat image = imread("3.jpg");
	Mat Salt_Image;
	image.copyTo(Salt_Image);
	salt(Salt_Image, 3000);

	Mat image1(image.size(), image.type());
	Mat image2;
	AverFiltering(Salt_Image, image1);
	blur(Salt_Image, image2, Size(3, 3));//openCV库自带的均值滤波函数
	imshow("原图", image);
	imshow("自定义均值滤波", image1);
	imshow("openCV自带的均值滤波", image2);
	waitKey();

}

void AverFiltering(const Mat& src, Mat& dst)
{
	if (!src.data)
		return;
	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-1 && (j + 1) <= src.cols-1) {//边缘不进行处理
				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];
			}
}

void salt(Mat& image, int num)
{
	if (!image.data) return;
	int i, j;
	srand(time(NULL));
	for (int x = 0; x < num; ++x) {
		i = rand() % image.rows;
		j = rand() % image.cols;
		if (x % 2 == 0) {
			image.at<Vec3b>(i, j)[0] = 255;
			image.at<Vec3b>(i, j)[1] = 255;
			image.at<Vec3b>(i, j)[2] = 255;
		}
		else
		{
			image.at<Vec3b>(i, j)[0] = 0;
			image.at<Vec3b>(i, j)[1] = 0;
			image.at<Vec3b>(i, j)[2] = 0;
		}
	}
}

 

2.3、高斯滤波

高斯滤波(高斯平滑)是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。
通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。
高斯滤波是最有用的滤波器 (尽管不是最快的)。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。

void GaussianBlur(InputArray src,OutputArray dst, Size ksize,
						double sigmaX, double sigmaY = 0,
						int borderType = BORDER_DEFAULT);

//高斯滤波	
GaussianBlur(image, out2, Size(3, 3), 0, 0);

/*
参数1:输入要处理的图像。
参数2:得到处理后的输出图像,与输入图像有一样的尺寸和类型。
参数3:高斯内核的大小。一般用Size(w,h)来表示内核大小,其中w为像素宽度,h为像素高度,w与h可以不同,但必须是正数和奇数,或者为0.
参数4:表示高斯核函数在X方向的标准偏差。
参数5:表示高斯核函数在Y方向的标准偏差。
参数6:用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT,我们一般不用管它。
*/

2.4、中值滤波

中值滤波法是一种非线性平滑技术,将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的中值代替 ,常用于消除图像中的椒盐噪声。
与低通滤波不同的是,中值滤波对脉冲噪声有良好的滤除作用,特别是在滤除噪声的同时,能够保护信号的边缘,使之不被模糊,但它会洗去均匀介质区域中的纹理。这些优良特性是线性滤波方法所不具有的。
 

中值滤波能减弱或消除傅里叶空间的高频分量,同时也影响低频分量。中值滤波去除噪声的效果依赖于两个要素:邻域的空间范围和中值计算中涉及的像素数。一般说来,小于滤波器面积一半的亮或暗的物体基本上会被滤除,而较大的物体几乎会原封不动地保存下来,因此中值滤波器的空间尺寸必须根据现有的问题来进行调整。

void medianBlur( InputArray src, OutputArray dst, int ksize);

//中值滤波
medianBlur(image, out3, 7);

/*
参数1:输入要处理的图像。
参数2:得到处理后的输出图像,与输入图像有一样的尺寸和类型。
参数3:int类型的ksize,表示内核大小,必须设置为奇数。
*/

自定义中值滤波, 卷积实现 

void MedianFlitering(const Mat& src, Mat& dst)
{
	if (!src.data) return;
	Mat _dst(src.size(), src.type());
	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 - 1 && (j + 1) <= src.cols - 1) {//边缘不进行处理
				_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);
			}
	_dst.copyTo(dst);
}

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];//返回中值
}

 

2.5、双边滤波

1、双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的,具有简单、非迭代、局部的特点。
2、双边滤波器的好处是可以做边缘保存,这个特点对于一些图像模糊来说很有用。一般过去用的维纳滤波或者高斯滤波去降噪,都会较明显地模糊边缘,对于高频细节的保护效果并不明显。一般的高斯模糊在进行采样时主要考虑了像素间的空间距离关系,但是却并没有考虑像素值之间的相似程度,因此这样我们得到的模糊结果通常是整张图片一团模糊。
3、双边滤波的改进就在于在采样时不仅考虑像素在空间距离上的关系,同时加入了像素间的相似程度考虑。双边滤波器比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。对于脉冲噪声,双边滤波会把它当成边缘从而不能去除。

void bilateralFilter( InputArray src, OutputArray dst, int d,
                                   double sigmaColor, double sigmaSpace,
                                   int borderType=BORDER_DEFAULT );


/*
参数1:输入要处理的图像,需要为8位或者浮点型单通道、三通道的图像。
参数2:得到处理后的输出图像,与输入图像有一样的尺寸和类型。
参数3:表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
参数4:表示颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
参数5:double类型的sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。它的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
参数6:用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT,我们一般不用管它。
*/

2.6、导向滤波

参考:https://blog.csdn.net/sinat_36264666/article/details/77990790

引导滤波(导向滤波)的目的是,保持双边滤波的优势(有效保持边缘,非迭代计算),而克服双边滤波的缺点(设计一种时间复杂度为 O(1) 的快速滤波器,而且在主要边缘附近没有梯度的变形)。
引导滤波(导向滤波)不仅能实现双边滤波的边缘平滑,而且在检测到边缘附近有很好的表现,可应用在图像增强、HDR压缩、图像抠图及图像去雾等场景。

#include <opencv2/core/core.hpp>                  
#include <opencv2/highgui/highgui.hpp>      
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream>     
using namespace std;
using namespace cv;
 
//-------------------【全局函数声明部分】--------------------------------------
Mat guidedFilter(Mat &srcMat, Mat &guidedMat, int radius, double eps);//引导滤波器
 
int main()
{
	//------------【0】定义相关变量-------------
	Mat resultMat;	//最后结果图像
	vector<Mat> vSrcImage, vResultImage;
	//------------【1】读取源图像并检查图像是否读取成功------------     
	Mat srcImage = imread("D:\\OutPutResult\\ImageTest\\boat1.jpg");
	if (!srcImage.data)
	{
		cout << "读取图片错误,请重新输入正确路径!\n";
		system("pause");
		return -1;
	}
	imshow("【源图像】", srcImage);
	//-------【2】对源图像进行通道分离,并对每个分通道进行导向滤波操------
	split(srcImage, vSrcImage);
	for (int i = 0; i < 3; i++)
	{
		Mat tempImage;
		vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0);//将分通道转换成浮点型数据
		Mat cloneImage = tempImage.clone();	//将tempImage复制一份到cloneImage
		Mat resultImage = guidedFilter(tempImage, cloneImage, 5, 0.01);//对分通道分别进行导向滤波,半径为1、3、5...等奇数
		vResultImage.push_back(resultImage);//将分通道导向滤波后的结果存放到vResultImage中
	}
	//----------【3】将分通道导向滤波后结果合并-----------------------
	merge(vResultImage, resultMat);
	imshow("【引导滤波/导向滤波】", resultMat);
	waitKey(0);
	return 0;
}
 
//导向滤波器
Mat guidedFilter(Mat &srcMat, Mat &guidedMat, int radius, double eps)
{
	//------------【0】转换源图像信息,将输入扩展为64位浮点型,以便以后做乘法------------
	srcMat.convertTo(srcMat, CV_64FC1);
	guidedMat.convertTo(guidedMat, CV_64FC1);
	//--------------【1】各种均值计算----------------------------------
	Mat mean_p, mean_I, mean_Ip, mean_II;
	boxFilter(srcMat, mean_p, CV_64FC1, Size(radius, radius));//生成待滤波图像均值mean_p	
	boxFilter(guidedMat, mean_I, CV_64FC1, Size(radius, radius));//生成引导图像均值mean_I	
	boxFilter(srcMat.mul(guidedMat), mean_Ip, CV_64FC1, Size(radius, radius));//生成互相关均值mean_Ip
	boxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius));//生成引导图像自相关均值mean_II
	//--------------【2】计算相关系数,计算Ip的协方差cov和I的方差var------------------
	Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);
	Mat var_I = mean_II - mean_I.mul(mean_I);
	//---------------【3】计算参数系数a、b-------------------
	Mat a = cov_Ip / (var_I + eps);
	Mat b = mean_p - a.mul(mean_I);
	//--------------【4】计算系数a、b的均值-----------------
	Mat mean_a, mean_b;
	boxFilter(a, mean_a, CV_64FC1, Size(radius, radius));
	boxFilter(b, mean_b, CV_64FC1, Size(radius, radius));
	//---------------【5】生成输出矩阵------------------
	Mat dstImage = mean_a.mul(srcMat) + mean_b;
	return dstImage;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山渺渺

感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值