荐 Machine Learning——CV系列(一)——Python+OpenCV核心操作(2)——图像滤波

2022-08-09,,,,

文章目录

  • 三、图像滤波
    • 3.1 滤波的概念
    • 3.2 卷积操作
    • 3.3 时域滤波
      • 3.3.0 时域滤波总结
      • 3.3.1 平滑算子(低通)
        • 3.3.1.1 均值滤波
        • 3.3.1.2 高斯滤波
        • 3.3.1.3 中值滤波
        • 3.3.1.4 双边滤波
      • 3.3.2 锐化算子(高通)
        • 3.3.2.1 Laplacian滤波
        • 3.3.2.2 USM锐化
    • 3.4 频域滤波
      • 3.4.1 傅里叶变换
        • 3.4.1.0 傅里叶变换的步骤
        • 3.4.1.1 numpy傅里叶变换
        • 3.4.1.2 opencv傅里叶变换
    • 3.5 梯度算子(高通)
      • 3.5.1 Sobel 算子
      • 3.5.2 Scharr 滤波器
      • 3.5.3 Laplacian算子(拉普拉斯算子)

三、图像滤波

3.1 滤波的概念

滤波按照空间可分为 时域滤波频域滤波
时域分析:与时间、空间有关的,图像、语音的某个位置的某个点
频域分析:分析的不是一个点,而是两个点之间的变化,类似梯度

3.2 卷积操作

src = cv2.imread(r"1.jpg")
kernel = np.array([[1, 1, 0], [1, 0, -1], [0, -1, -1]], np.float32)  # 定义一个核
dst = cv2.filter2D(src, -1, kernel=kernel)# 定义一个核
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)
r'''
和深度学习不同,OpenCV的卷积核是自己设置的,而深度学习可以自行学习到。
cv2.filter2D(src, ddepth, kernel)是常用的三个参数
	src:指输入的图像
	ddepth:
		表示目标图像的所需深度,它包含有关图像中存储的数据类型的信息,
		unsigned char(CV_8U),signed char(CV_8S),unsigned short(CV_16U)等,
		当ddepth=-1时,表示输出图像与原图像有相同的深度。
	kernel:就是自己设置的核
'''

3.3 时域滤波

3.3.0 时域滤波总结

均值滤波、高斯滤波、中值滤波、双边滤波 这四个滤波器都是低通滤波器
拉普拉斯滤波则是高通滤波器

低通的主要作用是通过模糊图片,达到去除噪点的功能。不同的滤波器对应不同的噪点,比如高斯滤波就对应高斯噪点、双边滤波就对应椒盐噪点等。

高通的主要作用是寻找边缘轮廓(保留梯度大的地方)。

3.3.1 平滑算子(低通)

3.3.1.1 均值滤波

均值滤波字面上理解就是平均值,其卷积核(3x3为例)如下:

import cv2
src = cv2.imread(r"1.jpg")
dst = cv2.blur(src,(3,3))
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

3.3.1.2 高斯滤波

高斯滤波对应高斯噪点

高斯滤波的卷积核类似一个二维的高斯函数,其卷积核(3x3为例)如下:

import cv2
src = cv2.imread(r"1.jpg")
dst = cv2.GaussianBlur(src, (3, 3), 1)
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

r'''
result = cv2.GaussianBlur(img, (5, 5), 0) # ksize是tuple,和下面中值滤波作对比...
(5, 5)是ksize高斯内核大小,ksize.width和ksize.height可以不同,但它们都必须为正数和奇数,也可以为零,然后根据sigma计算得出。
0==>sigmaX:X方向上的高斯核标准偏差。
sigmaY:Y方向上的高斯核标准差。(上面没写)
	如果sigmaY为零,则将其设置为等于sigmaX;
	如果两个sigmas为零,则分别从ksize.width和ksize.height计算得出。
	为了完全控制结果,建议指定所有ksize,sigmaX和sigmaY。
'''

3.3.1.3 中值滤波

处理椒盐噪声比较合适

中值滤波将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。

import cv2
src = cv2.imread(r"5.jpg")
dst = cv2.medianBlur(src,3)
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

3.3.1.4 双边滤波

双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理 。(处理断断续续的线)
可以查看这个链接详细了解双边滤波: 双边滤波算法

import cv2
src = cv2.imread(r"5.jpg")
dst = cv2.bilateralFilter(src,9,75,75)
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

r'''
result = cv2.bilateralFilter(img,9,75,75)
src:输入图像
d:过滤时周围每个像素领域的直径
sigmaColor:在color space中过滤sigma。参数越大,临近像素将会在越远的地方mix。
sigmaSpace:在coordinate space中过滤sigma。参数越大,那些颜色足够相近的的颜色的影响越大。
'''

3.3.2 锐化算子(高通)

3.3.2.1 Laplacian滤波

周围能量降低,中间能量变高,轮廓更加突出

高通滤波需要自己定义一个滤波器,其卷积核(3x3为例)如下:

import cv2
src = cv2.imread(r"1.jpg")
Laplace = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) #定义一个滤波核
dst = cv2.filter2D(src, -1, kernel=Laplace)
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

dst = cv2.Laplacian(src,-1,1)

3.3.2.2 USM锐化

原图先进行高斯滤波,得到的结果再和原图进行加权叠加。

import cv2
src = cv2.imread(r"1.jpg")
dst = cv2.GaussianBlur(src,ksize=(5,5),sigmaX=0)# 先高斯,作用是模糊去噪
dst = cv2.addWeighted(src, 3, dst, -2, 0)# 后加权
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

3.4 频域滤波

3.4.1 傅里叶变换

傅里叶变换:时域转频域,在频域操作,再转回时域。
核心观点:所有的波形,都可以由一系列的简单但是频率不同的正弦曲线叠加得到。

频域是描述信号在频率方面特性时用到的一种坐标系。频域图显示了在一个频率范围内每个给定频带内的信号量。[横坐标是频率,纵坐标是对应的幅值]。说到时域转频域就不能不说傅里叶变换(FT)了。傅里叶变换和逆变换公式如下:

虚线上是连续FT,下面是离散FT。这里不做复杂推导,只求实用!在Python中,cv2和Numpy两个包都提供了FT方法。

频率:像素与像素之间的差异。
一张图理解时域与频域:

理解:任何一个信号都可以分解为无数个频率的正弦波的叠加。(图像相当于二维信号,也是如此)

3.4.1.0 傅里叶变换的步骤

1.傅里叶变换

图像两边为高频信号

2.把中点移动到中间去

3.得到幅值,x20是为了放大,不然太暗

4.得到高和宽

5.分析需求
去掉低频信号,留下高频信号 --> 边缘
去掉高频信号,留下低频信号 --> 模糊

6.numpy傅里叶逆变换

中点返回、逆变换、绝对值

6.opencv傅里叶逆变换

创建全0矩阵,中间填原来的值

3.4.1.1 numpy傅里叶变换

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('9.jpg', 0)

f = np.fft.fft2(img) #傅里叶变换
fshift = np.fft.fftshift(f) #把中点移动到中间去

magnitude_spectrum = 20 * np.log(np.abs(fshift)) #计算每个频率的成分多少

plt.figure(figsize=(10, 10))
plt.subplot(221), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(222), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])

#去掉低频信号,留下高频信号
rows, cols = img.shape
crow, ccol = rows // 2, cols // 2
fshift[crow - 30:crow + 30, ccol - 30:ccol + 30] = 0


#傅里叶逆变换
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)

plt.subplot(223), plt.imshow(img_back, cmap='gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(224), plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])

plt.show()

直流信号:频率为0的信号。图上的中间白点是直流信号;越往两边走,越是高频信号;越量的地方,成分越多

3.4.1.2 opencv傅里叶变换

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('9.jpg', 0)

dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

plt.figure(figsize=(10, 10))
plt.subplot(221), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(222), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])

rows, cols = img.shape
crow, ccol = rows // 2, cols // 2
# create a mask first, center square is 1, remaining all zeros
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 1
fshift = dft_shift * mask
# apply mask and inverse DFT

f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])

plt.subplot(223), plt.imshow(img_back, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(224), plt.imshow(img_back)
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])

plt.show()

3.5 梯度算子(高通)

原理:梯度简单来说就是求导
OpenCV提供的梯度滤波器Sobel,Scharr和Laplacian

Sobel,Scharr 其实就是求一阶或二阶导数。
Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化。
Laplacian 是求二阶导数。

3.5.1 Sobel 算子

Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好

Gx可以用来得到横向的梯度;Gy可以得到纵向的梯度。总梯度方向是两向量的夹角,大小满足平行四边形法则。

import cv2
src = cv2.imread(r"1.jpg")
dst1 = cv2.Sobel(src,-1,1,0)# 只计算x方向的梯度
dst2 = cv2.Sobel(src,-1,0,1)# 只计算y方向的梯度
dst = cv2.add(dst2,dst1)
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)
"""
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
Sobel参数:
src:输入图像
ddepth:输出图像的深度(可以理解为数据类型),-1表示与原图像相同的深度。
图像深度是指存储每个像素值所用的位数,例如cv2.CV_8U,指的是8位无符号数,取值范围为0~255,

超出范围则会被截断[卡死在区间内的意思]。
-------------------------------------------------
cv2.CV_8U	8位无符号数
cv2.CV_16S	16位无符号数
cv2.CV_16U	16位有符号数
cv2.CV_32F	32位浮点数
cv2.CV_64F	64位浮点数
-------------------------------------------------
dx,dy:当组合为(dx=1,dy=0)时求x方向的一阶导数,
	当组合为(dx=0,dy=1)时求y方向的一阶导数
	[如果同时为1,通常得不到想要的结果]
ksize:(可选参数)Sobel算子的大小,必须是1,3,5或者7,默认为3
"""

x方向Sobel

y方向Sobel

总梯度图像(两个梯度方向相加)

3.5.2 Scharr 滤波器

Scharr 滤波器是对 Sobel 滤波器的改进版本。很明显,Scharr 滤波器把 Sobel 滤波器的参数"增强"了,所以效果会更显著一些。

此外,为了加速计算,可以简化幅值:

特别地,Sx在处理横向的梯度的时候,也在纵向方向上有类似"高斯"的功能;Sy则反过来。所以Scharr 滤波器能有"低通模糊"的功效。

import cv2
src = cv2.imread(r"1.jpg")
dst1 = cv2.Scharr(src,-1,1,0)# 可以对比上文 Sobel 滤波器,注意这里没有ksize参数。
dst2 = cv2.Scharr(src,-1,0,1)# 可以对比上文 Sobel 滤波器,注意这里没有ksize参数。
dst = cv2.add(dst2,dst1)
cv2.imshow("src show", src)
cv2.imshow("dst show", dst)
cv2.waitKey(0)

3.5.3 Laplacian算子(拉普拉斯算子)

拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二阶 Sobel 导数,OpenCV 在计算拉普拉斯算子时直接调用 Sobel 算子。

import cv2
src = cv2.imread(r"2.jpg")
laplacian = cv2.Laplacian(src, -1)
cv2.imshow("src show", src)
cv2.imshow("dst show", laplacian)# 可以对比上文 Sobel 滤波器,这里可以只需要两个参数,并且没有xy分量了
cv2.waitKey(0)

本文地址:https://blog.csdn.net/wa1tzy/article/details/107142364

《荐 Machine Learning——CV系列(一)——Python+OpenCV核心操作(2)——图像滤波.doc》

下载本文的Word格式文档,以方便收藏与打印。