【OpenCV】Chapter5.空间域图像滤波

2022-09-23 15:50:13 浏览数 (1)

最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。 【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487】 程序仓库:https://github.com/zstar1003/OpenCV-Learning

图像边界扩充

边界扩充顾名思义就是扩大图像的边界。

OpenCV 中提供了函数 cv.copyMakeBorder 进行边界扩充方式。

cv.copyMakeBorder(src, top, bottom, left, right, borderType[, dst, value]) → dst

参数说明:

  • src:进行边界扩充的图像
  • top, bottom, left, right:上侧、下侧、左侧、右侧边界扩充的的宽度(像素数)
  • value:当 borderType 为 BORDER_CONSTANT 时,以常量(value)填充扩充的边界,默认值为 (0,0,0)
  • borderType 边界扩充的类型
    • cv2.BORDER_REPLICATE:复制,复制最边缘像素进行填充(aa | abcdefg | gg),中值滤波采用复制法
    • cv2.BORDER_REFLECT:对称法,以图像边缘为轴进行对称填充(cba| abcdefg | gfe)
    • cv2.BORDER_REFLECTT_101:倒映法,以图像最边缘像素为轴进行对称填充(dcb| abcdefg | fed),函数 filter2D, blur, GaussianBlur, bilateralFilter 中默认的边界处理方法
    • cv2.BORDER_WRAP:用另一侧元素来填充这一侧的扩充边界(efg| abcdefg | ab)
    • cv2.BORDER_CONSTANT:以常数(value)作为像素值进行扩充(vv | abcdefg | vv)

示例程序:

代码语言:javascript复制
"""
图像边界扩充
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/xdu.jpg")  # 读取彩色图像(BGR)

top = bottom = left = right = 50
imgReplicate = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REPLICATE)
imgReflect = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REFLECT)
imgReflect101 = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REFLECT_101)
imgWrap = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_WRAP)
imgConstant = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(200, 200, 200))

plt.figure(figsize=(9, 6))
plt.subplot(231), plt.axis([-50, 562, -50, 562]), plt.title('ORIGINAL'), plt.axis('off')
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.subplot(232), plt.axis('off'), plt.title('REPLICATE')
plt.imshow(cv2.cvtColor(imgReplicate, cv2.COLOR_BGR2RGB))
plt.subplot(233), plt.axis('off'), plt.title('REFLECT')
plt.imshow(cv2.cvtColor(imgReflect, cv2.COLOR_BGR2RGB))
plt.subplot(234), plt.axis('off'), plt.title('REFLECT_101')
plt.imshow(cv2.cvtColor(imgReflect101, cv2.COLOR_BGR2RGB))
plt.subplot(235), plt.axis('off'), plt.title('WRAP')
plt.imshow(cv2.cvtColor(imgWrap, cv2.COLOR_BGR2RGB))
plt.subplot(236), plt.axis('off'), plt.title('CONSTANT')
plt.imshow(cv2.cvtColor(imgConstant, cv2.COLOR_BGR2RGB))
plt.show()

图像二维卷积

二维卷积在计算机计算机视觉中非常常见,简单来说,就是定义一个卷积核矩阵,再将矩阵旋转180度(为了简化也可以不旋转),然后和图像各窗口相乘相加。

OpenCV 中提供了函数cv.filter2D来实现二维卷积运算。

cv.filter2D(src, ddepth, kernel[, dst[, anchor[, delta, borderType]]]) → dst

参数说明:

  • src:卷积处理的输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:卷积处理的输出图像,大小和类型与 src 相同
  • ddepth:目标图像每个通道的深度(数据类型),ddepth=-1 表示与输入图像的数据类型相同
  • kernel:卷积操作的模板(卷积核),二维实型数组
  • anchor:卷积核的锚点位置,默认值 (-1, -1) 表示以卷积核的中心为锚点
  • delta:输出图像的偏移量,可选项,默认值为 0
  • borderType:边界扩充的类型

示例程序:

代码语言:javascript复制
"""
图像二维卷积
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)  # # flags=0 读取为灰度图像

kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])  # Gx   j*Gy

kFlip = cv2.flip(kernel, -1)  # 将卷积核旋转180度
# 使用函数filter2D算出same卷积
imgConv1 = cv2.filter2D(img, -1, kFlip,
                        anchor=(0, 0), borderType=cv2.BORDER_CONSTANT)
imgConv2 = cv2.filter2D(img, -1, kFlip,
                        anchor=(0, 0), borderType=cv2.BORDER_REFLECT)

plt.figure(figsize=(9, 6))
plt.subplot(131), plt.axis('off'), plt.title('Original'), plt.axis('off')
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
plt.subplot(132), plt.axis('off'), plt.title('cv2.filter2D (BORDER_CONSTANT)')
plt.imshow(np.absolute(imgConv1), cmap='gray', vmin=0, vmax=255)
plt.subplot(133), plt.axis('off'), plt.title('cv2.filter2D (BORDER_REFLECT)')
plt.imshow(np.absolute(imgConv2), cmap='gray', vmin=0, vmax=255)
plt.tight_layout()
plt.show()

高斯低通滤波

低通滤波,可以抑制图像中的灰度突变,使图像变得模糊。

主要用于:

  • 模糊图像和图像降噪
  • 在图像重取样前平滑图像以减少混淆
  • 减少图像中无关的细节
  • 平滑因灰度级不足所导致的图像的伪轮廓

高斯核的数学表达式为:

OpenCV 提供了cv.GaussianBlur函数实现高斯核低通滤波器,cv.getGaussianKernel函数可以计算一维高斯滤波器的系数。

cv.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY, borderType]]) → dst cv.getGaussianKernel(ksize, sigma, ktype) → retval

参数说明:

  • src:低通滤波输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:低通滤波输出图像,大小和类型与 src 相同
  • ksize:模糊核的大小,元组 (width, height),宽度、高度应设为正奇数
  • sigmaX:x 轴方向的高斯核标准差
  • sigmaY:y 轴方向的高斯核标准差,可选项
  • borderType:边界扩充的类型
  • sigma:高斯核的标准差
  • retval:返回值,高斯滤波器的系数

中值滤波

中值滤波是一种非线性滤波方法,是基于统计排序方法的滤波器。中值滤波法将像素点的邻域内的所有像素点灰度值的中值作为该像素点的灰度值。

中值滤波对于消除图像中的椒盐噪声非常有效。(椒盐噪声也称为脉冲噪声,是随机出现的白点或者黑点,通常是由于影像讯号受到干扰而产生,如脉冲干扰、图像扫描)

OpenCV 提供了cv.medianBlur函数实现中值滤波算法。

cv.medianBlur(src, ksize, dst) → dst

参数说明:

  • src:输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:输出图像,大小和类型与 src 相同
  • ksize:模糊核的线性大小,大于 1 的奇数

示例程序:

代码语言:javascript复制
"""
中值滤波
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

imgMedianBlur1 = cv2.medianBlur(img, 3)
imgMedianBlur2 = cv2.medianBlur(img, 7)

plt.figure(figsize=(9, 6))
plt.subplot(131), plt.axis('off'), plt.title("Original")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.subplot(132), plt.axis('off'), plt.title("cv2.medianBlur(size=3)")
plt.imshow(cv2.cvtColor(imgMedianBlur1, cv2.COLOR_BGR2RGB))
plt.subplot(133), plt.axis('off'), plt.title("cv2.medianBlur(size=7)")
plt.imshow(cv2.cvtColor(imgMedianBlur2, cv2.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()

钝化掩蔽

图像锐化的目的是增强图像的灰度跳变部分,使模糊的图像变得清晰。图像锐化也称为高通滤波,通过和增强高频,衰减和抑制低频。

钝化掩蔽指的是从原始图像中减去一幅平滑处理的钝化图像,也可以实现图像锐化效果。

公式如下:

当k>1时,实现高提升滤波;当k=1时,实现钝化掩蔽;k<1时,减弱钝化掩蔽。

钝化掩蔽的实现过程是:

(1)对原始图像进行平滑处理,得到平滑图像;

(2)从原始图像中减去平滑图像,产生掩蔽模板;

(3)将原始图像与掩蔽模板加权相加,得到钝化掩蔽。

示例代码:

代码语言:javascript复制
"""
钝化掩蔽
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

# 对原始图像进行平滑,GaussianBlur(img, size, sigmaX)
imgGauss = cv2.GaussianBlur(img, (5, 5), sigmaX=5)
imgGaussNorm = cv2.normalize(imgGauss, dst=None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)

# 掩蔽模板:从原始图像中减去平滑图像
imgMask = img - imgGaussNorm

passivation1 = img   0.6 * imgMask  # k<1 减弱钝化掩蔽
imgPas1 = cv2.normalize(passivation1, None, 0, 255, cv2.NORM_MINMAX)
passivation2 = img   imgMask  # k=1 钝化掩蔽
imgPas2 = cv2.normalize(passivation2, None, 0, 255, cv2.NORM_MINMAX)
passivation3 = img   2 * imgMask  # k>1 高提升滤波
imgPas3 = cv2.normalize(passivation3, None, 0, 255, cv2.NORM_MINMAX)

plt.figure(figsize=(10, 7))
titleList = ["1. Original", "2. GaussSmooth", "3. MaskTemplate",
             "4. Passivation(k=0.5)", "5. Passivation(k=1.0)", "6. Passivation(k=2.0)"]
imageList = [img, imgGauss, imgMask, imgPas1, imgPas2, imgPas3]
for i in range(6):
    plt.subplot(2, 3, i   1), plt.title(titleList[i]), plt.axis('off')
    plt.imshow(imageList[i], 'gray', vmin=0, vmax=255)
plt.tight_layout()
plt.show()

图像锐化

图像锐化主要增强图像的灰度跳变部分,主要的图像梯度算子有:Roberts、Prewitt、Sobel、Laplacian、Scharr。

相关理论在我之前的博文【计算机视觉】基础图像知识点整理中整理过,这里不再赘述。

Laplacian算子

OpenCV提供了拉普拉斯算子cv.Laplacian来实现

cv.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta, borderType]]]]) → dst

参数说明:

  • src:输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • ddepth:输出图片的数据深度:
  • dst:输出图像,大小和类型与 src 相同
  • ksize:计算二阶导数滤波器的孔径大小,必须为正奇数,可选项
  • scale:缩放比例因子,可选项,默认值为 1
  • delta:输出图像的偏移量,可选项,默认值为 0
  • borderType:边界扩充的类型,注意不支持对侧填充(BORDER_WRAP)

示例代码:

代码语言:javascript复制
"""
Laplacian算子
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

# 使用 cv2.Laplacian 实现 Laplace 卷积算子
imgLaplace2 = cv2.Laplacian(img, -1, ksize=3)
imgRecovery = cv2.add(img, imgLaplace2)  # 恢复原图像

# 二值化边缘图再卷积
ret, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_TRIANGLE)
imgLaplace3 = cv2.Laplacian(binary, cv2.CV_64F)
imgLaplace3 = cv2.convertScaleAbs(imgLaplace3)

plt.figure(figsize=(9, 6))
plt.subplot(131), plt.axis('off'), plt.title("Original")
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
plt.subplot(132), plt.axis('off'), plt.title("cv.Laplacian")
plt.imshow(imgLaplace2, cmap='gray', vmin=0, vmax=255)
plt.subplot(133), plt.axis('off'), plt.title("thresh-Laplacian")
plt.imshow(imgLaplace3, cmap='gray', vmin=0, vmax=255)
plt.tight_layout()
plt.show()

注:图二是直接用Laplacian进行卷积,图三是先进行阈值化处理再进行卷积,这样提出的边缘会更明显。

Sobel算子

OpenCV提供了函数cv.Sobel实现Sobel梯度算子

cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta, borderType]]]]) → dst

参数说明:

  • src:输入图像,灰度图像,不适用彩色图像
  • dst:输出图像,大小和类型与 src 相同
  • ddepth:输出图片的数据深度,由输入图像的深度进行选择
  • dx:x 轴方向导数的阶数,1 或 2
  • dy:y 轴方向导数的阶数,1 或 2
  • ksize:Sobel 卷积核的大小,可选的取值为:1/3/5/7,ksize=-1 时使用 Scharr 算子运算
  • scale:缩放比例因子,可选项,默认值为 1
  • delta:输出图像的偏移量,可选项,默认值为 0
  • borderType:边界扩充的类型,注意不支持对侧填充(BORDER_WRAP)

为了处理微分运算导致的数据异常(超出 0,255),OpenCV 提供了cv.convertScaleAbs进行饱和运算:dst=saturate(src∗α beta)。

cv.convertScaleAbs(src[, alpha, beta]) → dst

参数说明:

  • src:输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:输出图像,大小和类型与 src 相同
  • alpha:调节系数,可选项,默认值为 1
  • beta:亮度调节,可选项,默认值为 0
代码语言:javascript复制
"""
Sobel算子
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

# 使用 cv2.Sobel 实现 Sobel 算子
SobelX = cv2.Sobel(img, cv2.CV_16S, 1, 0)  # 计算 x 轴方向
SobelY = cv2.Sobel(img, cv2.CV_16S, 0, 1)  # 计算 y 轴方向
absX = cv2.convertScaleAbs(SobelX)  # 转回 uint8
absY = cv2.convertScaleAbs(SobelY)  # 转回 uint8
SobelXY = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)  # 用绝对值近似平方根

plt.figure(figsize=(10, 6))
plt.subplot(141), plt.axis('off'), plt.title("Original")
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
plt.subplot(142), plt.axis('off'), plt.title("SobelX")
plt.imshow(SobelX, cmap='gray', vmin=0, vmax=255)
plt.subplot(143), plt.axis('off'), plt.title("SobelY")
plt.imshow(SobelY, cmap='gray', vmin=0, vmax=255)
plt.subplot(144), plt.axis('off'), plt.title("SobelXY")
plt.imshow(SobelXY, cmap='gray')
plt.tight_layout()
plt.show()

Scharr算子

OpenCV提供了函数cv.Scharr实现Scharr算子。

cv.Scharr(src, ddepth, dx, dy[, dst[, scale[, delta, borderType]]]) → dst

参数说明:

  • src:输入图像
  • dst:输出图像,大小和类型与 src 相同
  • ddepth:输出图片的数据深度,由输入图像的深度进行选择
  • dx:x 轴方向导数的阶数
  • dy:y 轴方向导数的阶数
  • scale:缩放比例因子,可选项,默认值为 1
  • delta:输出图像的偏移量,可选项,默认值为 0
  • borderType:边界扩充的类型,注意不支持对侧填充(BORDER_WRAP)

示例程序

代码语言:javascript复制
"""
Scharr算子
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/lena.jpg", flags=0)

# 使用 cv2.Scharr 实现 Scharr 算子
ScharrX = cv2.Scharr(img, cv2.CV_16S, 1, 0)  # 计算 x 轴方向
ScharrY = cv2.Scharr(img, cv2.CV_16S, 0, 1)  # 计算 y 轴方向
absX = cv2.convertScaleAbs(ScharrX)  # 转回 uint8
absY = cv2.convertScaleAbs(ScharrY)  # 转回 uint8
ScharrXY = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)  # 用绝对值近似平方根

plt.figure(figsize=(10, 6))
plt.subplot(141), plt.axis('off'), plt.title("Original")
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
plt.subplot(142), plt.axis('off'), plt.title("ScharrX")
plt.imshow(ScharrX, cmap='gray', vmin=0, vmax=255)
plt.subplot(143), plt.axis('off'), plt.title("ScharrY")
plt.imshow(ScharrY, cmap='gray', vmin=0, vmax=255)
plt.subplot(144), plt.axis('off'), plt.title("ScharrXY")
plt.imshow(ScharrXY, cmap='gray')
plt.tight_layout()
plt.show()

0 人点赞