【OpenCV 学习笔记】第七章: 图像平滑处理

第七章: 图像平滑处理

  • 1、什么是图像平滑处理
    图像平滑处理就是,将图像中与 周围像素点的像素值差异较大的像素点 调整成 和周围像素点像素值 相近的值。
    例如:【OpenCV 学习笔记】第七章: 图像平滑处理_第1张图片

  • 2、为什么要进行平滑处理?
    因为图像在采集(生成)、传输、处理的过程中常常会存在一定的噪声干扰,比如,在图像拍摄的时候,也就是图像生成的时候,实际中往往会出现比如镜头污染、光线较强、较弱、大气折射、镜头角度等问题,导致拍摄出来的图片某些像素亮度变化过大,比如过亮或者过暗,导致人眼看到的图像画面不清晰,也就是图像有噪声,影响画质。为了抑制这种噪声,改善图像质量,我们就要对图像进行平滑处理。
    说明:图像经过平滑处理smoothing后,虽然可以抑制一些噪音,但同时也会把图像中的边缘线条弄模糊blurring了。

  • 3、如何进行平滑处理?
    用滤波器(filtering)过滤掉某些波,保留另一些波。
    滤波器是物理学中的概念,是物理学中对波的处理方式,有非常深入的研究和多种处理波的方法。
    我们拿物理学中的这个方法用到图像处理上,就叫图像滤波。在图像领域,实现图像滤波的滤波器我们叫滤波核filter,也叫卷积核。 图像滤波是图像处理和计算机视觉中最常用、最基本的操作。
    我们首先要明白,计算机视觉不是让人眼去看图片的,是让计算机去看图片的,让它去识别图像中特定物体、去检测图像中的特定目标、去分割图像中特定的目标,而计算机看图片和人眼看图片完全不是一回事,人眼看到的是图像,计算机看到的是一个数字矩阵。而让计算机去"图像识别",就是让它从一大堆数字中找出规律。而我们对图像进行的一些处理是便于计算机找出这个规律。
    图像滤波可以实现对图像的各种处理,比如实现图像的平移、旋转、镜面、分割、检测等等几乎所有的处理方式。本章中对图像进行平滑处理更是可以通过图像滤波去实现。
    所以,如果我们想对一张图像进行滤波处理,
    一是要确定你的滤波核也就是卷积核,包括卷积核的尺寸和卷积核内的数值,其中,尺寸是必须考虑的,而数值有些情况下是不需要考虑的,因为有时我们只需要一个没有数据只有尺寸的核即可);
    二是要确定卷积的方式,比如是进行均值运算还是加权运算还是中值运算等等方法;
    三是要确定是否要padding以及如果padding,padding就是对图像边缘进行填充,图像边缘的像素点是没法卷积运算的,此时就要特别处理,就是填充,而填充也有多种填充方式比如0填充、常量填充、镜像填充等。

一、均值滤波

用一个只有尺寸没有数值的卷积核去卷积原图像,得到对应位置上的每个像素点的值是被卷积核套住的像素的均值。
API: cv2.blur(img, ksize)

#例7.1 观察均值滤波对图像的处理效果 
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\25584\Desktop\lenaNoise.png')

img_blur1 = cv2.blur(img, (5,5))   #使用5x5的卷积核
img_blur2 = cv2.blur(img, (30,30)) #使用30x30的卷积核

fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[1].imshow(img_blur1[:,:,::-1])
axes[2].imshow(img_blur2[:,:,::-1])
plt.show()

【OpenCV 学习笔记】第七章: 图像平滑处理_第2张图片

说明:均值滤波的卷积核越大,参与到均值运算中的像素就会越多,也就是当前像素点的值是更多像素点的值的均值,
所以,卷积核越大去噪效果越好,但图像实真也越严重。所以,我们要选择合适尺寸的卷积核,在失真和去噪之间取得平衡。

二、方框滤波

用一个有尺寸有数值的卷积核去卷积原图像。
API: cv2.boxFilter(img, ddepth, ksize [, anchor, normalize, borderType])
img:要卷积的图像
ddepth:默认值是-1,表示与原始图像的深度一样。
ksize: 卷积核的尺寸
anchor: 锚点,默认值是(-1,-1),表示卷积运算完后的点位于核的中心点位置。该参数通常使用默认即可。
normalize: 表示卷积后要不要归一化处理。默认值是1。
      当normalize=1时,要进行平均归一化处理。此时计算结果就是均值滤波。
      当normalize=0时,表示不进行均值归一化处理,直接使用卷积结果值。当卷积结果超过255时,就截断为最大值255。
borderType:边界处理方式

#例7.2 观察方框滤波对图像的处理效果 
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\25584\Desktop\lenaNoise.png')

img_boxfilter1 = cv2.boxFilter(img, -1, (5,5))   #使用5x5的卷积核,normalize=1,默认使用均值归一化。
img_boxfilter2 = cv2.boxFilter(img, -1, (2,2), normalize=0) #不使用均值归一化

fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[1].imshow(img_boxfilter1[:,:,::-1])
axes[2].imshow(img_boxfilter2[:,:,::-1])
plt.show()

【OpenCV 学习笔记】第七章: 图像平滑处理_第3张图片

三、高斯滤波

用一个有尺寸有数值的卷积核去卷积原图像。高斯滤波和均值滤波和方框滤波不同的是,在均值滤波和方框滤波中,被卷积核套住的区域中,每个像素点的权重是相同的,但是在高斯滤波处理中,被高斯卷积核套住的区域中,中心点的权重值最大,越远离中心点的权重值就越小。
或者另外一种说法:高斯滤波和均值滤波、方框滤波的卷积核是不一样的。就是因为有不同的卷积核才实现不同的处理效果,才会有不同的滤波处理方式。

  • 高斯滤波核的特点:
    尺寸:尺寸必须是奇数,就是核的高和宽可以不相等,但都必须是奇数。
    数值:高斯核是必须有数值的,核里面的数值符合高斯分布。另外,卷积核里面的数值是要经过归一化处理的,没有归一化处理计算结果是错误的!!!【OpenCV 学习笔记】第七章: 图像平滑处理_第4张图片
  • 帮助理解文档:高斯模糊的算法(高斯卷积 高斯核)_Farmwang的博客-CSDN博客_高斯卷积

API: cv2.GaussianBlur(img, ksize, sigmaX, sigmaY [, borderType])
img:要卷积的图像
ksize: 卷积核的尺寸,记住必须是奇数!
sigmaX: 卷积核在x轴方向(水平方向)上的标准差
sigmaY: 卷积核在y轴方向(垂直方向)上的标准差
   sigmX和sigmY默认值都是0
       如果sigmaX不设为0,sigmaY设为0,则sigmay采用sigmaX的值;
       如果二者都默认为0,则sigmaX=0.3[(ksize.width-1)*0.5-1]+0.8, sigmaY=0.3[(ksize.height-1)*0.5-1]+0.8
borderType:边界处理方式,一般使用默认值即可。

#例7.3 观察高斯滤波对图像的处理效果 
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\25584\Desktop\lenaNoise.png')
​
img_gaussianBlur1 = cv2.GaussianBlur(img, (5,5), 0, 0)   
img_gaussianBlur2 = cv2.GaussianBlur(img, (5,5), 3,3) 
​
fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[1].imshow(img_gaussianBlur1[:,:,::-1])
axes[2].imshow(img_gaussianBlur2[:,:,::-1])
plt.show()

四、中值滤波

用一个有尺寸没有数值的卷积核去卷积原图像。卷积的方式是选取被卷积核套住的区域中的中间值作为当前像素点的像素值。
API: cv2.medianBlur(img, ksize)
img:要卷积的图像
ksize: 卷积核的尺寸,这个尺寸必须是大于1的奇数,因为这样卷积操作的时候才有中间值!

#例7.4 观察中值滤波对图像的处理效果 
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\25584\Desktop\lenaNoise.png')

img_medianBlur1 = cv2.medianBlur(img, 5)   
img_medianBlur2 = cv2.medianBlur(img, 3) 

fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[1].imshow(img_medianBlur1[:,:,::-1])
axes[2].imshow(img_medianBlur2[:,:,::-1])
plt.show()

五、双边滤波

前面讲的均值滤波、方框滤波、高斯滤波、中值滤波都是只单纯的考虑了像素点之间的空间信息而进行滤波的方式,这些方式都或多或少都会模糊图像的边缘信息,就是把图像里面的边缘线条弄模糊了,就是在去除图像噪音的同时把图像也弄模糊了,造成图像边界信息的部分丢失,这是这些方法的一个共同的缺点。
双边滤波就可以很好的规避这个缺点。因为双边滤波不仅考虑了像素的空间信息(距离越远权重越小)同时还考虑了像素的色彩信息(色彩差别越大,权重越小),这样就可以很好的去除噪声还可以较好的保护边缘信息,所以双边滤波器也是一个保边去噪的滤波器。如下图所示,左图是原图,中间是均值滤波的结果,右图是双边滤波的计算结果。【OpenCV 学习笔记】第七章: 图像平滑处理_第5张图片

也就是说,当远离边界时,即颜色十分相近,颜色权基本一样时,类似于高斯滤波,这样变可平滑处理图像。当处在边界时(所谓边界,就是颜色反差极大的地方),边界上的点互相颜色相近,会取极大的权值,而边界外的的点,颜色距离很远,权值取的很小(甚重可以忽略不计),这样就保护了边缘。

  • 如何实现既考虑空间信息又考虑色彩信息?
    简单!空间信息的考虑还是上面的高斯核思路,空间距离远的像素的权值调小,空间距离近的像素权值调大。 而色彩信息直接简单粗暴用if then判断语句实现呗。我们先设定一个色差范围,比如100,当被双边滤波核套住的原图区域中,某个像素点的值大于或者小于要计算的那个像素点值的100范围外,直接将那个像素点的权值置为0即可。也就是说,当差值的绝对值大于100的像素点就不参与平滑运算,这样就保留住了边缘线条了。
    ​​​​​​​一句话就是:双边滤波的滤波核还是一个高斯核,但是加了一个判断条件,就是:逐个遍历高斯核的每个元素,如果这个元素的像素值和中间元素的像素值差值大于100,那这个元素的权重就置为0,就是这个像素点就不参与滤波计算,这样就保住了和周围像素差值较大的边缘像素点。

  • API: cv2.bilateralFilter(img, d, sigmaColor, sigmaSpace)
    img:要卷积的图像
    d: 类似前面的ksize,该参数是正整数,如果该参数设置为非正数,则会自动从参数sigmaSpace中计算得到。
    sigmaColor: 滤波处理时选取的颜色差值范围,该值决定被卷积核套住的原图区域中,有哪些点能够参与到滤波中来。 如果sigmaColor=0,就表示其他所有的点都权值为0,都没法参与运算,所以该参数设置为0是毫无意义的。如果sigmaColor=255,就表示所有的点都可以参与滤波运算。
    sigmaSpace: 当参数d设置为正整数,这个参数就无意义,可以随便设置。当参数d设置为非正数,这个参数才有意义,就是相当于ksize,就是卷积核的尺寸。

    #例7.5 观察双边滤波对图像的处理效果 
    import cv2
    import matplotlib.pyplot as plt
    img = cv2.imread(r'C:\Users\25584\Desktop\lenaNoise.png')
    
    img_bilateralFilter1 = cv2.bilateralFilter(img, 15, 150, 1000)   
    img_bilateralFilter2 = cv2.bilateralFilter(img, 3, 255, 1000) 
    
    fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
    axes[0].imshow(img[:,:,::-1])
    axes[1].imshow(img_bilateralFilter1[:,:,::-1])
    axes[2].imshow(img_bilateralFilter2[:,:,::-1])
    plt.show()

    【OpenCV 学习笔记】第七章: 图像平滑处理_第6张图片

     说明:如果sigmaColor较小,比如10,滤波效果不太明显,如果较大比如150,滤波效果就非常明显,会产生卡通的效果。

    #例7.6 观察双边滤波对边缘信息的处理效果 
    import cv2
    import matplotlib.pyplot as plt
    img = cv2.imread(r'C:\Users\25584\Desktop\bilTest.bmp')
    
    img_GaussianBlur = cv2.GaussianBlur(img, (55, 55), 0, 0)   #高斯滤波
    img_bilateralFilter = cv2.bilateralFilter(img, 55, 100, 1000)   #双边滤波
    
    fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
    axes[0].imshow(img[:,:,::-1])
    axes[1].imshow(img_GaussianBlur[:,:,::-1])
    axes[2].imshow(img_bilateralFilter[:,:,::-1])
    plt.show()

    【OpenCV 学习笔记】第七章: 图像平滑处理_第7张图片

    说明:可见高斯核把边缘信息模糊虚化了,而双边滤波核较好的保存了边缘信息。

    六、自定义卷积核实现卷积操作---2D卷积

    如果上面的均值滤波卷积核、方框滤波卷积核、高斯滤波卷积核、中值滤波卷积核、双边滤波卷积核都不能满足我们对图像的处理要求,此时我们就需要一个更加灵活的核——即我们自定义一个我们想要的核。如何实现呢?opencv中的cv2.filter2D()函数可以帮助我们实现:
    cv2.filter2D(img, ddepth, kernel, anchor, delta, borderType)
    img:要处理的原图
    ddepth: 默认-1,表示与原图图像相同的深度
    kernel: 卷积核,是一个单通道的数组。如果想处理彩图时,让每个通道使用不同的核,就必须先将彩图分解后再使用不同的核去卷积操作。
    anchor: 默认值是(-1,-1),默认计算的结果位于核的中心位置。
    delta:这个参数可写可不写。如果写,表示在卷积操作时,对应位置相乘相加后再加一个偏置,这个偏置就是delta。
    borderType: 边界处理方式,一般用默认值即可。

    #例7.7 自定义卷积核,练习cv2.filter2D()函数   
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    img = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp')
    
    kernel = np.ones((9,9), np.float32)/81   #在实际中我们可以定义更复杂的卷积核,以实现自定义滤波操作
    img_filter = cv2.filter2D(img, -1, kernel)  #这个滤波器就相当于均值滤波
    img_blur = cv2.blur(img, (9,9))   #均值滤波
    
    fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100)
    axes[0].imshow(img[:,:,::-1])
    axes[1].imshow(img_filter[:,:,::-1])
    axes[2].imshow(img_blur[:,:,::-1])
    plt.show()

    【OpenCV 学习笔记】第七章: 图像平滑处理_第8张图片

你可能感兴趣的:(opencv,学习,计算机视觉)