OpenCV函数简记_第三章数字图像的滤波处理(方框,均值,高斯,中值和双边滤波)

系列文章目录

  1. OpenCV函数简记_第一章数字图像的基本概念(邻域,连通,色彩空间)
  2. OpenCV函数简记_第二章数字图像的基本操作(图像读写,图像像素获取,图像ROI获取,图像混合,图形绘制)
  3. OpenCV函数简记_第三章数字图像的滤波处理(方框,均值,高斯,中值和双边滤波)【本文】
  4. OpenCV函数简记_第四章数字图像的形态学处理和图像金字塔。(腐蚀、膨胀、开,闭运算、形态学梯度、顶帽和黑帽以及图像金字塔)


前言

本系列文章仅是本人学习OpenCV过程中,针对常用的OpenCV函数的相关参数和简单用法进行记录,方便随时查询和使用。对于具体算法原理不做过多介绍。
本文C++使用的OpenCV版本是的4.5.5。
python使用的OpenCV版本是4.5.3.56
官方文档参考的是 4.6.0-dev
需要查找具体函数可以直接从目录中查询。[]符号内为函数名称,包含C++和python两个语言的代码


本文内容:
本文描述了数字图像的滤波处理,主要由线性滤波和非线性滤波两个部分组成。其中,
线性滤波包含:方框滤波,均值滤波,高斯滤波。
非线性滤波包含: 中值滤波,双边滤波。

参考资料:
1.OpenCV官方文档
2.毛星云-OpenCV3编程入门
3. 作者SongpingWang:OpenCV—Python 图像滤波(均值、中值、高斯、高斯双边、高通等滤波)
4.作者沈子恒:双边滤波算法原理


1.滤波的概念

将图像看作数字信号,则图像也可以分为高频部分和低频部分,高频部分常指图像变化迅速的细节部分,即边界,噪声等变化较大的特征,低频部分则指的是图像变化平缓的主体部分,即背景等特征。(图像的大部分内容集中在低频和中频部分)。

而滤波则是针对所选频率进行过滤的操作,可以分为高通,低通,带通,带阻等方式。

高通是指高频部分通过,低频部分阻止,在图像上表现为锐化操作。
低通是指低频部分通过,高频部分阻止,在图像上表现为平滑(模糊) 操作,常用来消除噪声。
带通是指允许指定范围内的频率通过。
带阻是指阻止指定范围内的频率通过。

我们可以用下图来理解:
在这里插入图片描述

在图像中滤波的实现方式,是利用邻域算子来实现的。即将目标像素的值是由周边邻域像素的值经过加权或非线性处理获取的。
在这里插入图片描述
黄色方框为目标像素和它的邻域像素,绿色方框是滤波核,实现过程即为领域算子。

2.线性滤波

2.1 方框滤波[boxFilter]

boxFilter()
作用:
使用方框滤波对图像进行模糊。当归一化参数(normalize)为true时,其作用等价于均值滤波。如果从源码中就可以看到,均值滤波是基于boxFilter来实现的。

扫描二维码关注公众号,回复: 15492991 查看本文章

该函数使用内核如下公式:
K = { 1 K . h e i g h t ∗ K . w i d t h [ 1 1 . . . 1 1 1 . . . 1 . . . . . . . . . . . . 1 1 . . . 1 ] n o r m a l i z e = t r u e [ 1 1 . . . 1 1 1 . . . 1 . . . . . . . . . . . . 1 1 . . . 1 ] n o r m a l i z e = f a l s e K =\left\{ \begin{aligned} \frac{1}{K.height*K.width} \left[ \begin{matrix} 1 & 1 & ... & 1\\ 1 & 1 & ... & 1 \\ ... & ... & ... & ... \\ 1 & 1 & ... & 1 \end{matrix} \right] & &normalize=true \\\left[ \begin{matrix} 1 & 1 & ... & 1\\ 1 & 1 & ... & 1 \\ ... & ... & ... & ... \\ 1 & 1 & ... & 1 \end{matrix} \right] & & normalize=false \end{aligned} \right. K= K.heightK.width1 11...111...1............11...1 11...111...1............11...1 normalize=truenormalize=false

函数形式:

C++:
void cv::boxFilter (InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor = Point(-1,-1), bool normalize = true, int borderType = BORDER_DEFAULT)
Python:
cv.boxFilter( src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]] ) -> dst

参数解释(以C++展示的参数为例):
1.InputArray src: 输入图像,待处理的图像深度应该是CV_8U, CV_16U, CV_16S, CV_32F 或者 CV_64F。
2. OutputArray dst: 输出图像,类型和src一致。
3. int ddepth: 输出图像深度(输入-1,表示和原图像一致,即src.depth())。
4. Size ksize:Size类型ksize,表示核的尺寸。
5. Point anchor = Point(-1,-1): 锚点(被平滑的的点)在核上的位置,默认是Point(-1,-1),坐标为负值表示在核的中心。
6. bool normalize = true: 是否归一化,默认为true,其作用等同于均值滤波。
7. int borderType = BORDER_DEFAULT:用于推断图像边界框的类型,默认BORDER_DEFAULT。其他参考下表:

borderType枚举类型 解释[各种边界类型,图像边界用"|"表示]
BORDER_CONSTANT
Python: cv.BORDER_CONSTANT
iiiiii|abcdefgh|iiiiiii 带有一些特定的 i值
BORDER_REPLICATE
Python: cv.BORDER_REPLICATE
aaaaaa|abcdefgh|hhhhhhh
BORDER_REFLECT
Python: cv.BORDER_REFLECT
fedcba|abcdefgh|hgfedcb
BORDER_WRAP
Python: cv.BORDER_WRAP
cdefgh|abcdefgh|abcdefg
BORDER_REFLECT_101
Python: cv.BORDER_REFLECT_101
gfedcb|abcdefgh|gfedcba
BORDER_TRANSPARENT
Python: cv.BORDER_TRANSPARENT
uvwxyz|abcdefgh|ijklmno
BORDER_REFLECT101
Python: cv.BORDER_REFLECT101
和 BORDER_REFLECT_101一样
BORDER_DEFAULT
Python: cv.BORDER_DEFAULT
和BORDER_REFLECT_101一样
BORDER_ISOLATED
Python: cv.BORDER_ISOLATED
不看ROI之外的值

代码示例:

//C++
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
int main()
{
    
    
	Mat dog = imread("./dog.jpg");
	namedWindow("srcImg", WINDOW_NORMAL);
	imshow("srcImg", dog);
	
	//生成高斯噪声
	Mat gaussianNoise = Mat::zeros(dog.rows, dog.cols, dog.type());
	randn(gaussianNoise, (15, 15, 15), (30, 30, 30));
	Mat gaussianNoiseDst;
	add(dog, gaussianNoise, gaussianNoiseDst);
	namedWindow("gaussianNoiseDst", WINDOW_NORMAL);
	imshow("gaussianNoiseDst", gaussianNoiseDst);
	//imwrite("cpp_gaussianNoiseDst.jpg", gaussianNoiseDst);
	
	//方框滤波
	Mat boxBlurImg;
	boxFilter(gaussianNoiseDst, boxBlurImg, gaussianNoiseDst.depth(), Size(7, 7), Point(-1,-1),true);
	namedWindow("boxBlurImg", WINDOW_NORMAL);
	imshow("boxBlurImg", boxBlurImg);
	waitKey(0);
	//imwrite("cpp_boxBlurImg.jpg", boxBlurImg);

	return 0;
}

原始图像
在这里插入图片描述

加入高斯噪声在这里插入图片描述
方框滤波(normalize)后的结果。
在这里插入图片描述

# PYTHON
import cv2
import numpy as np

def main():
    dog = cv2.imread("./dog.jpg")
    cv2.namedWindow("srcImg", cv2.WINDOW_NORMAL)
    cv2.imshow("srcImg", dog)
    #添加高斯噪声
    mean = np.array([15, 15, 15])
    std = np.array([30, 30, 30])
    gaussianNoise = np.zeros(dog.shape)
    cv2.randn(gaussianNoise, mean, std)
    gaussianNoiseDog = cv2.add(dog, gaussianNoise.astype(np.uint8))
    cv2.namedWindow("gaussianNoiseDog", cv2.WINDOW_NORMAL)
    cv2.imshow("gaussianNoiseDog", gaussianNoiseDog)
    # cv2.imwrite("py_gaussianNoiseDog.jpg", gaussianNoiseDog)
    #方框滤波(normalize=true)
    boxFilterImg = cv2.boxFilter(gaussianNoiseDog, 3, (7, 7)).astype(np.uint8)
    cv2.namedWindow("boxFilterImg", cv2.WINDOW_NORMAL)
    cv2.imshow("boxFilterImg", boxFilterImg)
    cv2.waitKey(0)
    #cv2.imwrite("py_boxFilterImg.jpg", boxFilterImg)

if __name__ =="__main__":
    main()

运行结果同上。

2.2 均值滤波[blur]

bulr()
作用:
对输入的src图像进行均值滤波,后输出滤波后的dst图像。
归一化的方框滤波就是均值滤波,均值滤波是特殊情况的方框滤波。
即blur(src, dst, ksize, anchor, borderType) 函数等同于boxFilter(src, dst, src.type(), ksize, anchor, true, borderType).[normalize参数为true]

公式:
K = 1 K . h e i g h t ∗ K . w i d t h [ 1 1 . . . 1 1 1 . . . 1 . . . . . . . . . . . . 1 1 . . . 1 ] K = \frac{1}{K.height*K.width} \left[ \begin{matrix} 1 & 1 & ... & 1\\ 1 & 1 & ... & 1 \\ ... & ... & ... & ... \\ 1 & 1 & ... & 1 \end{matrix} \right] K=K.heightK.width1 11...111...1............11...1

函数形式:

C++:
void cv::blur (InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT)
Python:
cv.blur(src, ksize[, dst[, anchor[, borderType]]] ) ->dst

参数解释(以C++展示的参数为例):
1.InputArray src: 输入图像,待处理的图像深度应该是CV_8U, CV_16U, CV_16S, CV_32F 或者 CV_64F。
2. OutputArray dst: 输出图像,类型和src一致。
3. Size ksize: Size类型ksize,表示核的尺寸。
4. Point anchor=Point(-1,-1): 锚点(被平滑的的点)在核上的位置,默认是Point(-1,-1),坐标为负值表示在核的中心。
5. int borderType=BORDER_DEFAULT:用于推断图像边界框的类型,默认BORDER_DEFAULT。其他类型请查看方框滤波中表格内容。

代码示例:

//C++
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
int main()
{
    
    
	Mat dog = imread("./dog.jpg");
	namedWindow("srcImg", WINDOW_NORMAL);
	imshow("srcImg", dog);
	//生成高斯噪声
	Mat gaussianNoise = Mat::zeros(dog.rows, dog.cols, dog.type());
	randn(gaussianNoise, (15, 15, 15), (30, 30, 30));
	Mat gaussianNoiseDst;
	add(dog, gaussianNoise, gaussianNoiseDst);
	namedWindow("gaussianNoiseDst", WINDOW_NORMAL);
	imshow("gaussianNoiseDst", gaussianNoiseDst);
	//均值滤波
	Mat BlurImg;
	blur(gaussianNoiseDst, BlurImg, Size(7, 7));
	namedWindow("BlurImg", WINDOW_NORMAL);
	imshow("BlurImg", BlurImg);
	waitKey(0);
	return 0;
}

原始图像
在这里插入图片描述

加入高斯噪声
在这里插入图片描述

均值滤波:可以看出效果和设置normalize=true的方框滤波是一样的
在这里插入图片描述

# PYTHON
import cv2
import numpy as np

def main():
    dog = cv2.imread("./dog.jpg")
    cv2.namedWindow("srcImg", cv2.WINDOW_NORMAL)
    cv2.imshow("srcImg", dog)
    #添加高斯噪声
    mean = np.array([15, 15, 15])
    std = np.array([30, 30, 30])
    gaussianNoise = np.zeros(dog.shape)
    cv2.randn(gaussianNoise, mean, std)
    gaussianNoiseDog = cv2.add(dog, gaussianNoise.astype(np.uint8))
    cv2.namedWindow("gaussianNoiseDog", cv2.WINDOW_NORMAL)
    cv2.imshow("gaussianNoiseDog", gaussianNoiseDog)
    # cv2.imwrite("py_gaussianNoiseDog.jpg", gaussianNoiseDog)
    #均值滤波
    blurFilterImg = cv2.blur(gaussianNoiseDog,(7, 7)).astype(np.uint8)
    cv2.namedWindow("blurFilterImg", cv2.WINDOW_NORMAL)
    cv2.imshow("blurFilterImg", blurFilterImg)
    cv2.waitKey(0)
    # cv2.imwrite("py_blurFilterImg.jpg", blurFilterImg)

if __name__ =="__main__":
    main()

运行结果同上。

2.3 高斯滤波[GaussianBlur]

GaussianBlur()
作用:
使用高斯滤波器来模糊图像。

该函数将源图像与指定的高斯核卷积,可用于消除高斯噪声。
函数形式:

C++:
void cv::GaussianBlur (InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT)
Python:
cv.GaussianBlur( src, ksize, sigmaX[, dst[, sigmaY[, borderType]]] ) ->dst

参数解释(以C++展示的参数为例):
1.InputArray src:输入图像,待处理的图像深度应该是CV_8U, CV_16U, CV_16S, CV_32F 或者 CV_64F。
2. OutputArray dst:输出图像,类型和src一致。
3. Size ksize:Size类型ksize,表示核的尺寸。必须为正数和奇数。
4. double sigmaX:X方向上的高斯核标准偏差。
5. double sigmaY = 0: Y方向上的高斯核标准偏差,如果值为零,则设为sigmaX。如果sigmaX和sigmaY都为0,则有ksize.width和ksize.height计算出来
6. int borderType=BORDER_DEFAULT:用于推断图像边界框的类型,默认BORDER_DEFAULT。其他类型请查看方框滤波中表格内容。

代码示例:

//C++
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
int main()
{
    
    
	Mat dog = imread("./dog.jpg");
	namedWindow("srcImg", WINDOW_NORMAL);
	imshow("srcImg", dog);
	//生成高斯噪声
	Mat gaussianNoise = Mat::zeros(dog.rows, dog.cols, dog.type());
	randn(gaussianNoise, (15, 15, 15), (30, 30, 30));
	Mat gaussianNoiseDst;
	add(dog, gaussianNoise, gaussianNoiseDst);
	namedWindow("gaussianNoiseDst", WINDOW_NORMAL);
	imshow("gaussianNoiseDst", gaussianNoiseDst);

	//高斯滤波
	Mat gaussianBlurImg;
	GaussianBlur(gaussianNoiseDst, gaussianBlurImg, Size(7, 7), 30, 30);
	namedWindow("gaussianBlurImg", WINDOW_NORMAL);
	imshow("gaussianBlurImg", gaussianBlurImg);
	waitKey(0);
	// imwrite("cpp_gaussianBlurImg.jpg", gaussianBlurImg);
	return 0;
}

原始图像在这里插入图片描述

加入高斯噪声
在这里插入图片描述

高斯滤波后的结果:
在这里插入图片描述

# PYTHON
import cv2
import numpy as np

def main():
    dog = cv2.imread("./dog.jpg")
    cv2.namedWindow("srcImg", cv2.WINDOW_NORMAL)
    cv2.imshow("srcImg", dog)
    #添加高斯噪声
    mean = np.array([15, 15, 15])
    std = np.array([30, 30, 30])
    gaussianNoise = np.zeros(dog.shape)
    cv2.randn(gaussianNoise, mean, std)
    gaussianNoiseDog = cv2.add(dog, gaussianNoise.astype(np.uint8))
    cv2.namedWindow("gaussianNoiseDog", cv2.WINDOW_NORMAL)
    cv2.imshow("gaussianNoiseDog", gaussianNoiseDog)
    # cv2.imwrite("py_gaussianNoiseDog.jpg", gaussianNoiseDog)
    #高斯滤波
    gaussianBlurImg = cv2.GaussianBlur(gaussianNoiseDog,(7, 7), 30, 30).astype(np.uint8)
    cv2.namedWindow("gaussianBlurImg", cv2.WINDOW_NORMAL)
    cv2.imshow("gaussianBlurImg", gaussianBlurImg)
    cv2.waitKey(0)
    # cv2.imwrite("py_blurFilterImg.jpg", gaussianBlurImg)
if __name__ =="__main__":
    main()

结果同上。

3. 非线性滤波

3.1 中值滤波[medianBlur]

medianBlur()
作用:
与均值滤波获取邻域像素的平均值相比,中值滤波则是获取领域像素的中位值来作为输出值的。具体操作分别是:

  1. 将领域像素按照大小排序。
  2. 选择位于中间的像素作为输出。如果领域像素为偶数,则选取中间两个像素的平均值。

这种方式可以有效消除脉冲噪声和椒盐噪声。这两种噪声与高斯噪声不同,它是一个个孤立的噪声点,在使用高斯滤波的情况下往往不能达到很好的效果,而中值滤波则可以在消除噪声的同时,相对减轻图像的模糊效果,保护一定程度的边缘信息。代价就是处理速度是均值滤波的5倍以上。

函数形式:

C++:
void cv::medianBlur (InputArray src, OutputArray dst, int ksize)
Python:
cv.medianBlur(src, ksize[, dst]) -> dst

参数解释(以C++展示的参数为例):
1.InputArray src: 输入图像,输入1、3、4通道图像;当ksize为3或5时,图像深度应该为CV_8U、CV_16U或CV_32F,对于较大的孔径尺寸,只能为CV_8U。
2. OutputArray dst: 输出图像,类型和src一致。
3. int ksize: 孔径的线性尺寸;它必须是奇数且大于1,例如:3,5,7…

代码示例:
列出均值滤波和中值滤波对椒盐噪声消除的对比效果。

//C++
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
int main()
{
    
    
	Mat dog = imread("./dog.jpg");
	namedWindow("srcImg", WINDOW_NORMAL);
	imshow("srcImg", dog);
	//添加椒盐噪声
	RNG rng(12345);
	Mat impulseNoise = dog.clone();
	int nums = 10000;
	for (int i = 0; i < nums; i++) {
    
    
		int x = rng.uniform(0, impulseNoise.cols);
		int y = rng.uniform(0, impulseNoise.rows);
		if (i % 2 == 1) {
    
    
			impulseNoise.at<Vec3b>(y, x) = Vec3b(255, 255, 255);
		}
		else {
    
    
			impulseNoise.at<Vec3b>(y, x) = Vec3b(0, 0, 0);
		}
	}
	namedWindow("impulseNoise", WINDOW_NORMAL);
	imshow("impulseNoise", impulseNoise);
	//imwrite("cpp_impulseNoise.jpg", impulseNoise);
	//均值滤波
	Mat BlurImg;
	blur(impulseNoise, BlurImg, Size(7, 7));
	namedWindow("BlurImg", WINDOW_NORMAL);
	imshow("BlurImg", BlurImg);
	//imwrite("cpp_impulseNoiseBlurImg.jpg", BlurImg);
	//中值滤波
	Mat medianBlurImg;
	medianBlur(impulseNoise, medianBlurImg, 7);
	namedWindow("medianBlurImg", WINDOW_NORMAL);
	imshow("medianBlurImg", medianBlurImg);
	//imwrite("cpp_medianBlurImg.jpg", medianBlurImg);
	waitKey(0);
	return 0;
}

原始图像
在这里插入图片描述

加入椒盐(脉冲)噪声
在这里插入图片描述

均值滤波的结果
在这里插入图片描述

中值滤波的结果中值滤波相比均值滤波可以更好的去除椒盐噪声,并且模糊程度相对较低
在这里插入图片描述

# PYTHON
import cv2
import numpy as np
def main():
    dog = cv2.imread("./dog.jpg")
    cv2.namedWindow("srcImg", cv2.WINDOW_NORMAL)
    cv2.imshow("srcImg", dog)
    #添加椒盐噪声
    impulseNoiseDog = dog.copy()
    nums = 10000
    for i in range(0, nums):
        x = np.random.randint(0, impulseNoiseDog.shape[1])
        y = np.random.randint(0, impulseNoiseDog.shape[0])
        if (i % 2 == 1):
            impulseNoiseDog[y, x, :]= (255, 255, 255)

        else:
            impulseNoiseDog[y, x, :] = (0, 0, 0)
    cv2.namedWindow("impulseNoise", cv2.WINDOW_NORMAL)
    cv2.imshow("impulseNoise", impulseNoiseDog)
    # cv2.imwrite("py_gaussianNoiseDog.jpg", gaussianNoiseDog)
    # 均值滤波
    blurFilterImg = cv2.blur(impulseNoiseDog,(7, 7))
    cv2.namedWindow("impulseBlurFilterImg", cv2.WINDOW_NORMAL)
    cv2.imshow("impulseBlurFilterImg", blurFilterImg)
    # cv2.imwrite("py_impulseBlurFilterImg.jpg", blurFilterImg)
    # 中值滤波
    medianBlurImg = cv2.medianBlur(impulseNoiseDog, 7)
    cv2.namedWindow("medianBlurImg", cv2.WINDOW_NORMAL)
    cv2.imshow("medianBlurImg", medianBlurImg)
    # cv2.imwrite("py_medianBlurImg.jpg", medianBlurImg)
    cv2.waitKey(0)
    
if __name__ =="__main__":
    main()

运行结果同上

3.2 双边滤波[bilateralFilter]

bilateralFilter()
作用:
双边滤波器是在同时在空间邻域和像素值(颜色值)上做高斯滤波处理。即,双边滤波不仅考虑了空间距离上的影响(同之前的高斯滤波),还考虑了像素相似度上(可以有效保存边缘)的影响。我们可以从如下公式中理解:

双边滤波公式:
g ( i , j ) = ∑ k , l f ( k , l ) w ( i , j , k , l ) ∑ k , l w ( i , j , k , l ) g(i,j) = \frac{\sum_{k, l}^{} f(k,l)w(i,j,k,l)}{\sum_{k,l}w(i,j,k,l)} g(i,j)=k,lw(i,j,k,l)k,lf(k,l)w(i,j,k,l)
其中 i , j i, j i,j表示目标像素的坐标位置, k , l k, l k,l表示邻域像素的像素位置。 w ( i , j , k , l ) w(i,j,k,l) w(i,j,k,l)表示对应目标像素和邻域像素的双边滤波核值。

双边滤波核 w w w是由空间滤波核 w d w_d wd和像素相似度滤波核 w r w_r wr的乘积形成。其中空间滤波核 w d w_d wd和像素相似度滤波核 w r w_r wr的表达式如下:
w d ( i , j , k , l ) = exp ⁡ ( − ( i − k ) 2 + ( j − l ) 2 2 σ d 2 ) w_d(i,j,k,l) = \exp(-\frac{(i-k)^2+(j-l)^2}{2\sigma_d^2}) wd(i,j,k,l)=exp(2σd2(ik)2+(jl)2)
w r ( i , j , k , l ) = exp ⁡ ( − ( f ( i , j ) − f ( k , l ) ) 2 2 σ r 2 ) w_r(i,j,k,l) = \exp(-\frac{(f(i,j)-f(k,l))^2}{2\sigma_r^2}) wr(i,j,k,l)=exp(2σr2(f(i,j)f(k,l))2)
其中 f ( i , j ) f(i,j) f(i,j)表述目标像素的像素值, f ( k , l ) f(k,l) f(k,l)表示邻域像素的像素值。从像素相似度滤波核 w r w_r wr的公式中可以看出,像素之间差异越大,核值越小,反之像素差异越小,核值越大。这样在图像边界处就形成了一个"瀑布形状"的滤波核,这样就可以有效的保持图像的边缘信息。

从一个示例了解核的内容:
假设原始图像尺度为 7 × 7 7\times7 7×7,值为:

2D像素 3D图像
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
|0.000|0.000|0.000|0.000|255.000|255.000|255.000|
在这里插入图片描述

那么,经过公式计算,其 w d w_d wd w r w_r wr的滤波核2D和3D图像如下图所示:
空间位置滤波核如下图:

2D像素 3D图像
|0.368|0.486|0.574|0.607|0.574|0.486|0.368|
|0.486|0.641|0.757|0.801|0.757|0.641|0.486|
|0.574|0.757|0.895|0.946|0.895|0.757|0.574|
|0.607|0.801|0.946|1.000|0.946|0.801|0.607|
|0.574|0.757|0.895|0.946|0.895|0.757|0.574|
|0.486|0.641|0.757|0.801|0.757|0.641|0.486|
|0.368|0.486|0.574|0.607|0.574|0.486|0.368|
在这里插入图片描述

像素相似度滤波核:

2D像素 3D图像
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
|1.000|1.000|1.000|1.000|0.000|0.000|0.000|
在这里插入图片描述

双边滤波核 w w w的公式如下:
w ( i , j , k , l ) = exp ⁡ ( − ( i − k ) 2 + ( j − l ) 2 2 σ d 2 − ( f ( i , j ) − f ( k , l ) ) 2 2 σ r 2 ) w(i,j,k,l) = \exp(-\frac{(i-k)^2+(j-l)^2}{2\sigma_d^2}-\frac{(f(i,j)-f(k,l))^2}{2\sigma_r^2}) w(i,j,k,l)=exp(2σd2(ik)2+(jl)22σr2(f(i,j)f(k,l))2)

双边滤波器滤波核:

2D像素 3D图像
|0.368|0.486|0.574|0.607|0.000|0.000|0.000|
|0.486|0.641|0.757|0.801|0.000|0.000|0.000|
|0.574|0.757|0.895|0.946|0.000|0.000|0.000|
|0.607|0.801|0.946|1.000|0.000|0.000|0.000|
|0.574|0.757|0.895|0.946|0.000|0.000|0.000|
|0.486|0.641|0.757|0.801|0.000|0.000|0.000|
|0.368|0.486|0.574|0.607|0.000|0.000|0.000|
在这里插入图片描述

从双边滤波核中可以看到,双边滤波器可以在消除噪声的同时尽可能地保留图像的边界信息。但是如果高频噪声处于边界附近的话,则不能很好的过滤,所以该滤波器最好是处理图像低频信息。

如果读者也想生成双边滤波器的核,则可以通过附录代码,自行测试。

官方文档中解释:
1.双边滤波可以很好地减少不必要的噪声,同时保持边缘相当尖锐。然而,与大多数过滤器相比,它非常慢。
2.sigma值:为简单起见,可以将两个sigma值设为相同。如果它们很小(< 10),过滤不会有太大的效果,而如果它们很大(> 150),它们会有非常强的效果,使图像看起来“卡通化”。
3.过滤器大小:较大的过滤器(d > 5)非常慢,因此建议对实时应用程序使用d=5,对需要大量噪声过滤的离线应用程序可能使用d=9。

函数形式:

C++:
void cv::bilateralFilter (InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT)
Python:
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) -> dst

参数解释(以C++展示的参数为例):
1.InputArray src: 输入图像,需要为8位或浮点类型的单通道或3通道图像。
2. OutputArray dst: 输出图像,类型和src一致。
3. int d:滤波时每个像素邻域的直径。如果它是非正的,则从sigmspace计算。
4. double sigmaColor:颜色空间滤波器的sigma值(高斯核的标准差,标准差约大,高斯核越扁平),这个参数值越大,就表明像素邻域有越宽广的颜色会被混合在一起,产生较大的半相等颜色空间。
5. double sigmaSpace:坐标空间滤波器的sigma值(高斯核的标准差,标准差约大,高斯核越扁平)。这个参数值越大,就表明有着更远的像素将相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色。当d>0时,d指定邻域大小,与sigmspace无关。否则,d与sigmspace成比例。
6. int borderType = BORDER_DEFAULT:用于推断图像边界框的类型,默认BORDER_DEFAULT。其他类型请查看方框滤波中表格内容。

代码示例:

列出双边滤波和高斯滤波对高斯噪声消除的对比效果,同时也展示出官网所说的卡通效果。

//C++
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
int main()
{
    
    
	Mat dog = imread("./dog.jpg");
	namedWindow("srcImg", WINDOW_NORMAL);
	imshow("srcImg", dog);
	//生成高斯噪声
	Mat gaussianNoise = Mat::zeros(dog.rows, dog.cols, dog.type());
	randn(gaussianNoise, (15, 15, 15), (30, 30, 30));
	Mat gaussianNoiseDst;
	add(dog, gaussianNoise, gaussianNoiseDst);
	namedWindow("gaussianNoiseDst", WINDOW_NORMAL);
	imshow("gaussianNoiseDst", gaussianNoiseDst);
	//高斯滤波
	Mat gaussianBlurImg;
	GaussianBlur(gaussianNoiseDst, gaussianBlurImg, Size(7, 7), 100, 100);
	namedWindow("gaussianBlurImg", WINDOW_NORMAL);
	imshow("gaussianBlurImg", gaussianBlurImg);
	//双边滤波
	Mat bilateralBlurImg;
	bilateralFilter(gaussianNoiseDst, bilateralBlurImg, 7, 100, 100);
	namedWindow("bilateralBlurImg", WINDOW_NORMAL);
	imshow("bilateralBlurImg", bilateralBlurImg);
	//imwrite("cpp_bilateralBlurImg.jpg", bilateralBlurImg);
	//卡通效果
	Mat animeImg;
	bilateralFilter(dog, animeImg, 50, 200, 200);
	namedWindow("animeImg", WINDOW_NORMAL);
	imshow("animeImg", animeImg);
	//imwrite("cpp_animeImg.jpg", animeImg);
	waitKey(0);

	return 0;
}

原始图像
在这里插入图片描述

加入高斯噪声
在这里插入图片描述

高斯滤波后的结果
在这里插入图片描述

双边滤波后的结果(==可以明显看出,双边滤波可以在保持边缘的情况下去除高斯噪声)
在这里插入图片描述

卡通效果
在这里插入图片描述

# PYTHON

def main():
    dog = cv2.imread("./dog.jpg")
    cv2.namedWindow("srcImg", cv2.WINDOW_NORMAL)
    cv2.imshow("srcImg", dog)
    #添加高斯噪声
    mean = np.array([15, 15, 15])
    std = np.array([30, 30, 30])
    gaussianNoise = np.zeros(dog.shape)
    cv2.randn(gaussianNoise, mean, std)
    gaussianNoiseDog = cv2.add(dog, gaussianNoise.astype(np.uint8))
    cv2.namedWindow("gaussianNoiseDog", cv2.WINDOW_NORMAL)
    cv2.imshow("gaussianNoiseDog", gaussianNoiseDog)
    # cv2.imwrite("py_gaussianNoiseDog.jpg", gaussianNoiseDog)
    #高斯滤波
    gaussianBlurImg = cv2.GaussianBlur(gaussianNoiseDog,(7, 7), 30, 30).astype(np.uint8)
    cv2.namedWindow("gaussianBlurImg", cv2.WINDOW_NORMAL)
    cv2.imshow("gaussianBlurImg", gaussianBlurImg)
    # cv2.imwrite("py_GaussianBlurImg.jpg", gaussianBlurImg)
	# 双边滤波
    bilateralBlurImg = cv2.bilateralFilter(gaussianNoiseDog, 30, 180, 300)
    cv2.namedWindow("bilateralBlurImg", cv2.WINDOW_NORMAL)
    cv2.imshow("bilateralBlurImg", bilateralBlurImg)
    # cv2.imwrite("py_bilateralBlurImg.jpg", bilateralBlurImg)
	#卡通效果
    animeImg = cv2.bilateralFilter(dog, 50, 200, 200)
    cv2.namedWindow("animeImg", cv2.WINDOW_NORMAL)
    cv2.imshow("animeImg", animeImg)
    # cv2.imwrite("py_animeImg.jpg", animeImg)
    cv2.waitKey(0)
if __name__ =="__main__":
    main()

原始图像
在这里插入图片描述

加入高斯噪声
在这里插入图片描述

高斯滤波后的结果
在这里插入图片描述

双边滤波后的结果(==可以明显看出,双边滤波可以在保持边缘的情况下去除高斯噪声)
在这里插入图片描述

卡通效果
在这里插入图片描述


总结

本文仅对我认为常用的函数进行总结,在查看Opencv官方文档中,还有许多函数未介绍。如果本文没有写的,可以查询OpenCV官方文档。感谢各位读者的阅读。如果您觉得对您有帮助的话,可以给小弟一个赞。

最后,缅怀毛星云先生,感谢毛星云先生的引领,本文主要参考了毛星云先生的《OpenCV3编程入门》。

附录

生成核函数

  1. 生成双边滤波器的核函数:
//C++
#include<opencv2/opencv.hpp>
#include<math.h>
#include<iostream>
#include<typeinfo>
#include<vector>
#include<string>

using namespace std;
using namespace cv;

int main()
{
    
    
	//-----------------------------
	// 只需修改此处的图像尺寸即可
	//-----------------------------
	int imgSize = 7;
	Mat srcImg(7, 7, CV_64FC1 ,Scalar::all(255));

	for (int row=0;row<srcImg.rows;row++)
	{
    
    
		double* PtrSrc = srcImg.ptr<double>(row);
		for (int col=0; col < (srcImg.cols * srcImg.channels())/2+1; col++)
		{
    
    
			PtrSrc[col] = 0;
		}
	}
	//用来打印分割符号“-”*30
	vector<char> spiltStr;
	for (int i = 0; i < 30; i++)
	{
    
    
		spiltStr.push_back('-');
	}
	cout << "原始图像:" << endl;
	//-----------------------------
	// 打印原始图像
	//-----------------------------
	for (int i = 0; i < 30; i++) cout << spiltStr[i];
	printf("\n");
	for (int row = 0; row < srcImg.rows; row++)
	{
    
    
		printf("|");
		for (int col = 0; col < srcImg.cols * srcImg.channels(); col++)
		{
    
    
			printf("%.3f|", srcImg.at<double>(row, col));
		}
		printf("\n");
		for (int i = 0; i < 30; i++) cout << spiltStr[i];
		printf("\n");
	}
	//-----------------------------
	//计算空间位置的高斯核
	//-----------------------------
	int sigmad = 3;
	Mat spaceKernel = Mat::zeros(imgSize, imgSize, CV_64FC1);
	for (int row = 0; row < spaceKernel.rows; row++)
	{
    
    
		double* PtrSpace = spaceKernel.ptr<double>(row);
		for (int col = 0; col < spaceKernel.cols * srcImg.channels(); col++)
		{
    
    
			PtrSpace[col] = exp(-(pow((row - imgSize / 2), 2) + pow((col - imgSize / 2), 2)) / (2 * pow((double)sigmad, 2)));		
		}
	}
	//-----------------------------
	//打印空间位置的高斯核的结果
	//-----------------------------
	cout << "空间位置滤波核:" << endl;
	for (int i = 0; i < 30; i++) cout << spiltStr[i];
	printf("\n");
	for (int row = 0; row < spaceKernel.rows; row++)
	{
    
    	
		printf("|");
		for (int col = 0; col < spaceKernel.cols * srcImg.channels(); col++)
		{
    
    
			printf("%.3f|", spaceKernel.at<double>(row, col));
		}
		printf("\n");
		for (int i = 0; i < 30; i++) cout << spiltStr[i];
		printf("\n");
	}
	//-----------------------------
	//计算像素相似度的高斯核
	//-----------------------------
	int sigmaR = sigmad;
	Mat pixKernel = Mat::zeros(imgSize, imgSize, CV_64FC1);
	for (int row = 0; row < pixKernel.rows; row++)
	{
    
    
		double* PtrPix = pixKernel.ptr<double>(row);
		for (int col = 0; col < pixKernel.cols * srcImg.channels(); col++)
		{
    
    
			PtrPix[col] = exp(-(pow(srcImg.at<double>(imgSize / 2, imgSize / 2) - srcImg.at<double>(row, col),2)) / (2 * pow((double)sigmaR, 2)));
		}
	}
	//-----------------------------
	//打印像素相似度的高斯核的结果
	//-----------------------------
	cout << "像素相似度滤波核:" << endl;
	for (int i = 0; i < 30; i++) cout << spiltStr[i];
	printf("\n");
	for (int row = 0; row < pixKernel.rows; row++)
	{
    
    
		printf("|");
		for (int col = 0; col < pixKernel.cols * srcImg.channels(); col++)
		{
    
    
			printf("%.3f|", pixKernel.at<double>(row, col));
		}
		printf("\n");
		for (int i = 0; i < 30; i++) cout << spiltStr[i];
		printf("\n");
	}
	//-----------------------------
	//计算双边滤波高斯核
	//-----------------------------
	Mat bilateralKernel = Mat::zeros(imgSize, imgSize, CV_64FC1);
	for (int row = 0; row < bilateralKernel.rows; row++)
	{
    
    
		double* PtrbiLateralKernel = bilateralKernel.ptr<double>(row);
		for (int col = 0; col < bilateralKernel.cols * srcImg.channels(); col++)
		{
    
    
			PtrbiLateralKernel[col] = pixKernel.at<double>(row, col) * spaceKernel.at<double>(row, col);
		}
	}
	//-----------------------------
	//打印双边滤波高斯核的结果
	//-----------------------------
	cout << "双边滤波器滤波核:" << endl;
	for (int i = 0; i < 30; i++) cout << spiltStr[i];
	printf("\n");
	for (int row = 0; row < bilateralKernel.rows; row++)
	{
    
    
		printf(",");
		for (int col = 0; col < bilateralKernel.cols * srcImg.channels(); col++)
		{
    
    
			printf("%.3f|", bilateralKernel.at<double>(row, col));
		}
		printf("\n");
		for (int i = 0; i < 30; i++) cout << spiltStr[i];
		printf("\n");
	}
};
  1. 3D显示C++生成的核函数
# PYTHON
import numpy as np

d = np.array([0.000,0.000,0.000,0.000,255.000,255.000,255.000,
0.000,0.000,0.000,0.000,255.000,255.000,255.000,
0.000,0.000,0.000,0.000,255.000,255.000,255.000,
0.000,0.000,0.000,0.000,255.000,255.000,255.000,
0.000,0.000,0.000,0.000,255.000,255.000,255.000,
0.000,0.000,0.000,0.000,255.000,255.000,255.000,
0.000,0.000,0.000,0.000,255.000,255.000,255.000])

a = np.array([0.368,0.486,0.574,0.607,0.574,0.486,0.368,
0.486,0.641,0.757,0.801,0.757,0.641,0.486,
0.574,0.757,0.895,0.946,0.895,0.757,0.574,
0.607,0.801,0.946,1.000,0.946,0.801,0.607,
0.574,0.757,0.895,0.946,0.895,0.757,0.574,
0.486,0.641,0.757,0.801,0.757,0.641,0.486,
0.368,0.486,0.574,0.607,0.574,0.486,0.368,])

b = np.array([1.000,1.000,1.000,1.000,0.000,0.000,0.000,
1.000,1.000,1.000,1.000,0.000,0.000,0.000,
1.000,1.000,1.000,1.000,0.000,0.000,0.000,
1.000,1.000,1.000,1.000,0.000,0.000,0.000,
1.000,1.000,1.000,1.000,0.000,0.000,0.000,
1.000,1.000,1.000,1.000,0.000,0.000,0.000,
1.000,1.000,1.000,1.000,0.000,0.000,0.000,])

c = np.array([00.368,0.486,0.574,0.607,0.000,0.000,0.000,
0.486,0.641,0.757,0.801,0.000,0.000,0.000,
0.574,0.757,0.895,0.946,0.000,0.000,0.000,
0.607,0.801,0.946,1.000,0.000,0.000,0.000,
0.574,0.757,0.895,0.946,0.000,0.000,0.000,
0.486,0.641,0.757,0.801,0.000,0.000,0.000,
0.368,0.486,0.574,0.607,0.000,0.000,0.000,])
#-----------------------------
#原始图像高斯核
#-----------------------------
#利用三维轴方法
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#定义图像和三维格式坐标轴
fig0 = plt.figure()  #定义新的三维坐标轴
ax0 = plt.axes(projection='3d')
xx = np.arange(-3, 4, 1)
yy = np.arange(-3, 4, 1)
X, Y = np.meshgrid(xx, yy)
Z = d.reshape(7, 7)
#作图
ax0.plot_surface(X, Y, Z, cmap='gray')
plt.show()
#-----------------------------
#空间位置高斯核
#-----------------------------
#定义图像和三维格式坐标轴
fig = plt.figure()  #定义新的三维坐标轴
ax1 = plt.axes(projection='3d')
xx = np.arange(-3, 4, 1)
yy = np.arange(-3, 4, 1)
X, Y = np.meshgrid(xx, yy)
Z = a.reshape(7, 7)
#作图
ax1.plot_surface(X, Y, Z, cmap='gray')
plt.show()
#-----------------------------
#像素相似度高斯核
#-----------------------------
fig2 = plt.figure()  #定义新的三维坐标轴
ax2 = plt.axes(projection='3d')
xx = np.arange(-3, 4, 1)
yy = np.arange(-3, 4, 1)
X, Y = np.meshgrid(xx, yy)
Z = b.reshape(7, 7)
#作图
ax2.plot_surface(X,Y,Z,cmap='gray')
plt.show()
#-----------------------------
#双边滤波高斯核
#-----------------------------
fig3 = plt.figure()  #定义新的三维坐标轴
ax3 = plt.axes(projection='3d')
xx = np.arange(-3, 4, 1)
yy = np.arange(-3, 4, 1)
X, Y = np.meshgrid(xx, yy)
Z = c.reshape(7, 7)
#作图
ax3.plot_surface(X,Y,Z,cmap='gray')
plt.show()

猜你喜欢

转载自blog.csdn.net/weixin_43610114/article/details/126383936