OpenCV中的图像处理6

傅里叶变换

傅立叶变换用于分析各种滤波器的频率特性。对于图像,使用2D离散傅里叶变换(DFT)查找频域。一种称为快速傅立叶变换(FFT)的快速算法用于DFT的计算。
对于正弦信号 x ( t ) = A s i n ( 2 π f t ) x(t) = Asin(2\pi ft) x(t)=Asin(2πft),可以说
f是信号的频率,如果采用其频域,则可以看到
f的尖峰。如果对信号进行采样以形成离散信号,将获得相同的频域,但是在 [ − π , π ] [-\pi , \pi] [π,π] [ 0 , 2 π ] [0, 2\pi] [0,2π]范围内(对于N点DFT为[0, N])是周期性的。可以将图像视为在两个方向上采样的信号。因此,在X和Y方向都进行傅立叶变换,可以得到图像的频率表示。
对于正弦信号,如果幅度在短时间内变化很快,则可以说它是高频信号。如果变化缓慢,则为低频信号。可以将相同的想法扩展到图像。图像中的振幅在哪里急剧变化?在边缘点或噪声。因此,可以说边缘和噪声是图像中的高频内容。如果幅度没有太大变化,则它是低频分量。

Numpy中的傅里叶变换

Numpy具有FFT软件包执行此操作。np.fft.fft2()提供了频率转换,它将是一个复杂的数组。它的第一个参数是输入图像,即灰度图像。第二个参数是可选的,它决定输出数组的大小若它大于输入图像的大小,则在计算FFT之前用零填充输入图像;若小于输入图像,则将裁切输入图像;若未传递任何参数,则输出数组的大小将于输入的大小相同。一旦获得结果,零频率分量(DC分量)将位于左上角;若要使其居中,则需在两个方向上将结果都移动 N 2 \dfrac{N}{2} 2N;只需通过函数np.fft.fftshift()即可完成(它更容易分析);找到频率变换后,就可以找到幅度谱。
若在结果中可以看到更多白色区域,表明低频内容更多。现在发现了频率变换,可以在频域中进行一些操作,如高通滤波和重建图像,即找到逆DFT;只需用尺寸为60x60的矩形窗口遮罩即可消除低频;然后使用np.fft.ifftshift()应用反向移位,以使DC分量再次出现在左上角,然后使用函数np.ifft2()找到逆FFT,结果将是一个复数,可以采用其绝对值。
npfourier_trans.py

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

img = cv.imread('./OpenCV/fruit.jpg', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20 * np.log(np.abs(fshift))
rows, cols = img.shape
crow, ccol = rows // 2, cols // 2
fshift[crow - 30:crow + 31, ccol - 30:ccol + 31] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.real(img_back)
images = [img, magnitude_spectrum, img_back, img_back]
titles = ['Input Image', 'Magnitude Spectrum', 'Image After HPF', 'Result in JET']
for i in range(4):
    plt.subplot(2, 2, i + 1)
    if i == 3:
        plt.imshow(images[i])
    else:
        plt.imshow(images[i], cmap='gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
结果表明高通滤波是边缘检测操作,这也表明大多数图像数据都存在于频谱的低频区域。在结果的最后一张JET颜色的图像,会看到一些伪像,显示出一些波纹状结构,称为振铃效应,这是由用于遮罩的矩形窗口引起的,此掩码转换为正弦形状,从而导致此问题;因此,不选择矩形窗口用于过滤,更好的选择是高斯窗口。

OpenCV中的傅里叶变换

OpenCV提供了函数cv.dft()cv.idft(),它返回与使用numpy相同的结果,但是有两个通道;第一个通道是结果的实部,第二个通道是结果的虚部;输入图像首先要转换为np.float32。还可以使用函数cv.cartToPolar(),此函数在单个镜头中同时返回幅值和相位。
之后做DFT的逆变换,在使用numpy处理时,创建了一个HDF,这次将看到如何删除图像中的高频内容,即将LPF应用到图像中;它实际上模糊了图像,为此,首先创建一个高值(1)在低频部分,即过滤低频内容,0在高频区。
cvfourier_trans.py

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

img = cv.imread('./OpenCV/fruit.jpg', 0)
dft = cv.dft(np.float32(img), flags=cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20 * np.log(cv.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))
rows, cols = img.shape
crow, ccol = rows // 2, cols // 2
# 首先创建一个掩码,中心正方形为1,其余全为零
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 1
# 应用掩码和逆DFT
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv.idft(f_ishift)
img_back = cv.magnitude(img_back[:, :, 0], img_back[:, :, 1])
images = [img, magnitude_spectrum, img_back, img_back]
titles = ['Input Image', 'Magnitude Spectrum','Mask IDFT Magnitude']
for i in range(3):
    plt.subplot(1, 3, i + 1)
    plt.imshow(images[i], cmap='gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
通常,OpenCV函数cv.dft()cv.idft()比numpy函数更快,但是numpy函数更容易使用。

DFT的性能优化

对于某些数组尺寸,DFT的计算性能较好;当数组大小为2的幂时,速度最快,对于大小为2、3和5的乘积的数组,也可以非常有效地进行处理;为提高性能,可以在找到DFT之前将数组的大小修改为任何最佳大小(通过填充零);对于OpenCV,必须手动填充零;对于numpy,指定FFT计算的新大小,将自动填充零。如何找到最优的大小呢?OpenCV提供了函数cv.getOptimalDFTSize(),它同时适用于cv.dft()np.fft.fft2()

为什么拉普拉斯算子是高通滤波器?

对于更大的FFT只需要拉普拉斯变换。
filters.py

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

# 没有缩放参数的简单均值滤波器
mean_filter = np.ones((3, 3))
# 创建高斯滤波器
x = cv.getGaussianKernel(5, 10)
gaussian = x * x.T
# 不同的边缘检测滤波器
# x方向上的scharr
scharr = np.array([[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]])
# x方向的sobel
sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
# y方向上的sobel
sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
# 拉普拉斯变换
laplacian = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
filters = [mean_filter, gaussian, laplacian, scharr, sobel_x, sobel_y]
filter_names = ['mean_filter', 'gaussian', 'laplacccian', 'scharr', 'sobel_x', 'sobel_y']
fft_filters = [np.fft.fft2(x) for x in filters]
fft_shift = [np.fft.fftshift(y) for y in fft_filters]
mag_spectrum = [np.log(np.abs(z) + 1) for z in fft_shift]
for i in range(6):
    plt.subplot(2, 3, i + 1)
    plt.imshow(mag_spectrum[i], cmap='gray')
    plt.title(filter_names[i])
    plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
从以上图像中,可以看到每种内核阻止的频率区域以及它允许经过的区域;从这些信息中,可以说出为什么每个内核都是HDF或LPF。

模板匹配

模板匹配是一种用于在较大图像中搜索和查找模板图像位置的方法。OpenCV带有一个函数cv.matchTemplate(), 它只是将模板图​​像滑动到输入图像上(就像在2D卷积中一样),然后在模板图像下比较模板和输入图像的拼图;OpenCV中实现了几种比较方法。它返回一个灰度图像,其中每个像素表示该像素的邻域与模板匹配的程度。
如果输入图像的大小为(W x H),而模板图像的大小为(w x h),则输出图像的大小将为(W - w + 1, H - h + 1);得到结果后,可以使用函数cv.minMaxLoc()查找最大/最小值在哪,将其作为矩形的左上角,并以(w, h)作为矩形的宽度和高度,该矩形是模板的区域。若使用函数cv.TM_SQDIFF()作为比较方法,则最小值提供最佳匹配。

OpenCV中的模板匹配

作为示例,将在西游记师徒几人的图像中寻找猪八戒,所以用猪八戒头部图像创建了一个模板,将尝试所以比较方法,以便看到各方法的结果。
match_template.py

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

img = cv.imread('./OpenCV/journey.jpg', 0)
img2 = img.copy()
template = cv.imread('./OpenCV/pig.png', 0)
w, h = template.shape[::-1]
# 6种比较方法
methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR',
             'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED']
for meth in methods:
    img = img2.copy()
    method = eval(meth)
    # 应用模板匹配
    res = cv.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
    # 如果方法是cv.TM_SQDIFF或cv.TM_SQDIFF_NORMED,则取最小值
    if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv.rectangle(img, top_left, bottom_right, (255, 0, 0), 2)
    images = [res, img]
    titles = ['Matching Result', 'Detected Point']
    for i in range(2):
        plt.subplot(1, 2, i + 1)
        plt.imshow(images[i], cmap='gray'), plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)
    plt.show()
  • cv.TM_CCOEFF 在这里插入图片描述
  • cv.TM_CCOEFF_NORMED 在这里插入图片描述
  • cv.TM_CCORR 在这里插入图片描述
  • cv.TM_CCORR_NORMED 在这里插入图片描述
  • cv.TM_SQDIFF 在这里插入图片描述
  • cv.TM_SQDIFF_NORMED 在这里插入图片描述
    可以看到,一些方法的结果并不理想

多对象的模板匹配

若要搜索具有多次出现的对象,则函数cv.minMaxLoc()不会提供所有位置,在这种情况下,可以使用阈值化。
multi_match.py

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

img_rgb = cv.imread('./OpenCV/mario.png')
img = img_rgb.copy()
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
coin = cv.imread('./OpenCV/coin.png', 0)
w, h = coin.shape[::-1]
res = cv.matchTemplate(img_gray, coin, cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
    cv.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 1)
images = [img_rgb, img]
titles = ['Original', 'After Match']
for i in range(2):
    plt.subplot(1, 2, i + 1)
    plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
可以看到,找到了其中的硬币。
学习来源:OpenCV-Python中文文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值