系列文章目录

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

文章目录

  • 系列文章目录
  • 前言
  • 1.滤波的概念
  • 2.线性滤波
    • 2.1 方框滤波[boxFilter]
    • 2.2 均值滤波[blur]
    • 2.3 高斯滤波[GaussianBlur]
  • 3. 非线性滤波
    • 3.1 中值滤波[medianBlur]
    • 3.2 双边滤波[bilateralFilter]
  • 总结
  • 附录

前言

本系列文章仅是本人学习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来实现的。

该函数使用内核如下公式:
K={1K.height∗K.width[11...111...1............11...1]normalize=true[11...111...1............11...1]normalize=falseK =\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.height∗K.width1​⎣⎡​11...1​11...1​............​11...1​⎦⎤​⎣⎡​11...1​11...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 npdef 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=1K.height∗K.width[11...111...1............11...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.height∗K.width1​⎣⎡​11...1​11...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 npdef 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 npdef 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 = 10000for 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,lf(k,l)w(i,j,k,l)∑k,lw(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,l​w(i,j,k,l)∑k,l​f(k,l)w(i,j,k,l)​
其中i,ji, ji,j表示目标像素的坐标位置,k,lk, lk,l表示邻域像素的像素位置。w(i,j,k,l)w(i,j,k,l)w(i,j,k,l)表示对应目标像素和邻域像素的双边滤波核值。

双边滤波核www是由空间滤波核wdw_dwd​和像素相似度滤波核wrw_rwr​的乘积形成。其中空间滤波核wdw_dwd​和像素相似度滤波核wrw_rwr​的表达式如下:
wd(i,j,k,l)=exp⁡(−(i−k)2+(j−l)22σd2)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​(i−k)2+(j−l)2​)
wr(i,j,k,l)=exp⁡(−(f(i,j)−f(k,l))22σr2)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)表示邻域像素的像素值。从像素相似度滤波核wrw_rwr​的公式中可以看出,像素之间差异越大,核值越小,反之像素差异越小,核值越大。这样在图像边界处就形成了一个"瀑布形状"的滤波核,这样就可以有效的保持图像的边缘信息。

从一个示例了解核的内容:
假设原始图像尺度为7×77\times77×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|

那么,经过公式计算,其wdw_dwd​和wrw_rwr​的滤波核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|

双边滤波核www的公式如下:
w(i,j,k,l)=exp⁡(−(i−k)2+(j−l)22σd2−(f(i,j)−f(k,l))22σr2)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​(i−k)2+(j−l)2​−2σ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;
}

原始图像

加入高斯噪声

高斯滤波后的结果

双边滤波后的结果(==可以明显看出,双边滤波可以在保持边缘的情况下去除高斯噪声)

卡通效果

# PYTHONdef 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;}}//用来打印分割符号“-”*30vector<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 npd = 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()

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

  1. 【OpenCV】邻域滤波:方框、高斯、中值、双边滤波

    邻域滤波(卷积) 邻域算子值利用给定像素周围像素的值决定此像素的最终输出.如图左边图像与中间图像卷积禅城右边图像.目标图像中绿色的像素由原图像中蓝色标记的像素计算得到. 通用线性邻域滤波是一种常用的邻 ...

  2. 第三章微分中值定理及导数应用(柯西中值和泰勒公式)

    1.柯西中值定理 2.皮亚诺型余项泰勒公式和拉格朗日型泰勒公式 2.1 定义 2.2共同点 1.多项式逼近,2.函数和高阶的关系 2.3 不同点 皮亚诺型余项泰勒公式:局部,如极限.极值 .拉格朗日型 ...

  3. 9、opencv 方盒、均值、高斯、中值、双边滤波 2022-08-22

    import cv2 import numpy as npimg_path = "./R-C.jpg"img = cv2.imread(img_path) 方盒滤波 # norma ...

  4. Lync Server 2010的部署系列_第三章 证书、架构、DNS规划

    Lync Server 2010的部署系列_第三章 证书.架构.DNS规划 一.证书规划 组件 使用者名称 使用者备用名称条目/顺序 证书颁发机构 (CA) 备注 边缘外部接口 Sip.Giantha ...

  5. c语言中,x-y,'105',ab,7f8那个是正确的,C语言程序设计_第三章 数据.ppt

    C语言程序设计_第三章 数据 * 运算符功能 与运算量关系 要求运算量个数 要求运算量类型 运算符优先级别 结合方向 结果的类型 学习运算符应注意 * 基本算术运算符: + - * / % 结合方向: ...

  6. 管理系统中计算机应用课件,管理系统中计算机应用_第三章课件.ppt

    <管理系统中计算机应用_第三章课件.ppt>由会员分享,提供在线免费全文阅读可下载,此文档格式为ppt,更多相关<管理系统中计算机应用_第三章课件.ppt>文档请在天天文库搜索 ...

  7. 《精通数据仓库设计》中英对照_第三章

    <精通数据仓库设计>中英对照_第三章 第二部分 模型开发 数据仓库应该表示企业数据的各个方面,这些方面以主题域和业务数据模型开始.我们将在第3章使用一个假想的公司,指导一步一步地开发这两个 ...

  8. ArcGIS for Desktop入门教程_第三章_Desktop软件安装 - ArcGIS知乎-新一代ArcGIS问答社区...

    原文:ArcGIS for Desktop入门教程_第三章_Desktop软件安装 - ArcGIS知乎-新一代ArcGIS问答社区 1 软件安装 1.1 安装前准备 请确认已经收到来自Esri中国( ...

  9. OpenCV学习笔记(六):非线性滤波-中值、双边:medianBlur(),bilateralFilter()

    OpenCV学习笔记(六):非线性滤波-中值.双边:medianBlur(),bilateralFilter() 1.中值滤波(Median filter) 是一种典型的非线性滤波技术,基本思想是用像 ...

最新文章

  1. js如何实现扫描身份证识别_人脸识别是如何实现的
  2. Kubernetes — 基于层级命名空间的多租户隔离
  3. Maven发布jar包到Nexus私库
  4. php怎么关联默认打开程序,win10系统打开文件时提示“请在默认程序控制面板中创建关联”如何解决...
  5. WebKit Loader模块介绍
  6. 收集程序员的几幅对联
  7. 2010年11月编程语言排行榜:手机里的代码
  8. python qt5 designer 免费安装_PyCharm离线安装PyQt5_tools(QtDesigner)
  9. (转)SpringMVC学习(七)——Controller类的方法返回值
  10. 开心猫序列C语言,开心猫的读后感作文
  11. Sqoop 使用指南
  12. 过去25年八大计算机病毒:冲击波和震荡波入选
  13. 计算机手机共享上网,上网教程_电脑wifi怎么实现手机共享上网 - 驱动管家
  14. Found existing installation:xxxx
  15. 光耦主要参数和高速光耦如何选型
  16. 百度、太一云、趣链、星合、达令共议什么是真区块链【区块链打假】
  17. matlab gui 作者,MATLAB GUI设计学习手记(第4版)
  18. 什么是 ECC 内存?(memory with ECC)
  19. 硬件设计之一——电源设计05:过电流能力
  20. 动态红包封面来了,特效拉满超炫酷

热门文章

  1. 海马苹果助手ipad版_海马手机助手下载|海马手机助手 5.0.1 官方版
  2. 腾讯、阿里、小米...互联网企业进入组织架构调整期
  3. 查询公司的详细情况的办法 - 应届生
  4. 未曾上市先“上火”,威马汽车IPO前变“危马”?
  5. 帧同步与状态同步的区别
  6. 怎么样说一口地道的英语
  7. 必须收藏的学位论文免费下载网站!!各国论文免费下载!英国 欧洲 美国 芬兰 日本 加拿大 等等20余
  8. “FCoE全解系列”之增强型以太网技术
  9. 阿里云服务器挂载云盘
  10. 企业四要素核验-企业四要素核验接口-api接口