当前位置: 代码迷 >> 综合 >> OpenCV-Python 笔记(六)傅里叶变换、模板匹配
  详细解决方案

OpenCV-Python 笔记(六)傅里叶变换、模板匹配

热度:81   发布时间:2024-02-08 16:40:59.0

傅里叶变换

理论分析

傅里叶变换广泛运用于信号分析领域,它分析滤波器的频率。在图像分析中国,也可以使用2D傅里叶变换DFT和快速傅里叶变换FFT,它们已经在信号分析方面展现出了非常优秀的特性。对于信号来说,频率高的地方我们叫做高频信号,也就是变化快,频率低的地方叫做低频信号,也就是变化不大的地方。所以在图片中,边缘和噪点变化快,属于高频。根据这个特性,可以获得图像的边缘。

Numpy中的傅里叶变换

img=cv.imread("text.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)
plt.subplot(131),plt.imshow(img,"gray"),plt.title("Original")
plt.subplot(132),plt.imshow(magnitude_spectrum,"gray"),plt.title("Magnitude Spectrum")
plt.subplot(133),plt.imshow(img_back,"gray"),plt.title("After HPT")
plt.show()

在这里插入图片描述

OpenCV中的傅里叶变换

这里做逆变换,删掉边缘。

img=cv.imread("text.jpg",0)
dft=cv.dft(np.float32(img),flags=cv.DFT_COMPLEX_OUTPUT)
dft_shift=np.fft.fftshift(dft)
magnitude_spectrum=2*np.log(cv.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
rows,cols=img.shape
crow,ccol=rows//2,cols//2
mask=np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30,ccol-30:ccol+30]=1
fshirt=dft_shift*mask
f_ishift=np.fft.ifftshift(fshirt)
img_back=cv.idft(f_ishift)
img_back=cv.magnitude(img_back[:,:,0],img_back[:,:,1])
plt.subplot(131),plt.imshow(img,"gray"),plt.title("Original")
plt.subplot(132),plt.imshow(magnitude_spectrum,"gray"),plt.title("magnitude spectrum")
plt.subplot(133),plt.imshow(img_back,"gray"),plt.title("result")
plt.show()

就是让图片显得更模糊
在这里插入图片描述

DFT的性能优化

当数组为2的幂次方时,DFT计算速度最快,对于2,3,5乘积的数也表现不错,所以我们需要重新制定输入数组的大小,让它拥有更好的性能,但是在OpenCV中很难将一个数组扩展为2的幂次方的数组,可以利用Numpy对数组进行扩展,用0填充。
利用OpenCV中的cv.getOptimalDFTSize()可以检查到比较合适的数值。

img=cv.imread("gh1.jpg",0)
rows,cols=img.shape
print(rows,cols)
nrows=cv.getOptimalDFTSize(rows)
ncols=cv.getOptimalDFTSize(cols)
print(nrows,ncols)

输出结果:124 190 125 192
所以我们需要将124扩展为125,将190扩展为192。
这个和卷积神经网络中的SAME方法差不多,我们使用Numpy的padding方法,填充数组。(np.pad默认填充数字是0)

img=cv.imread("gh1.jpg",0)
rows,cols=img.shape
print(rows,cols)
nrows=cv.getOptimalDFTSize(rows)
ncols=cv.getOptimalDFTSize(cols)
print(nrows,ncols)
nimg=np.pad(img,((0,nrows-rows),(0,ncols-cols)))
print(nimg.shape)

结果:
在这里插入图片描述
看看它们之间的性能

a=time.clock()
for i in range(1000):dft1= cv.dft(np.float32(img),flags=cv.DFT_COMPLEX_OUTPUT)
b=time.clock()
print(b-a)
c=time.clock()
for i in range(1000):dft1= cv.dft(np.float32(nimg),flags=cv.DFT_COMPLEX_OUTPUT)
d=time.clock()
print(d-c)

结果:在这里插入图片描述
快了一些,大概有1/3。

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

#创建均值滤波器
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, sobel_x, sobel_y, scharr]
filter_name = ['mean_filter', 'gaussian','laplacian', 'sobel_x','sobel_y', 'scharr_x']
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_name[i]), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
从上面可以看出各种滤波器允许通过的区域状态。所以均值滤波器和高斯滤波器是低通滤波器LPF,其它的是高通滤波器HPF。

模板匹配

理论

模板匹配,就是在一张图片上,查找一个目标,这个目标是我们的另一个输入图片,算法会为我们匹配这个输入在图片上的位置,通过边缘就算概率,当概率大于一定的值的时候,匹配成功。它是在图片上进行滑动匹配,像卷积网络中过滤器在输入数据上做滑动操作一样。

如果输入图像的大小为(W,H) ,而模板图像的大小为(w,h) ,则输出图像的大小将为(W-w + 1, H-h + 1) 。得到结果后,可以使用cv.minMaxLoc()函数查找最大/最小值在哪。将其作为矩形的左上角,并以(w,h) 作为矩形的宽度和高度。该矩形就是模板的区域。

所以,原图会先变小,再扩展到原来的形状。

OpenCV中的模板匹配

OpenCV提供六种方法来实现这个方法。

img=cv.imread("gh.jpg",0)
img2=img.copy()
template=cv.imread("gh2.jpg",0)
w,h=template.shape[::-1]
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)#如果是这两种,需要最小值if method in [cv.TM_SQDIFF,cv.TM_SQDIFF_NORMED]:top_left=min_locelse:top_left=max_locbottom_right=(top_left[0]+w,top_left[1]+h)cv.rectangle(img,top_left,bottom_right,255,2)plt.subplot(121),plt.imshow(res,"gray"),plt.title("Matching Result "+str(meth)),plt.xticks([]),plt.yticks([])plt.subplot(122),plt.imshow(img,"gray"),plt.title("Detected Point "+str(meth)),plt.xticks([]),plt.yticks([])plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
除了使用cv.TM_CCORR时出现偏差,其余的方法都找到了对应的位置所以cv.TM_CCORR的结果并不理想。

多对象匹配

使用多对象匹配,可以在一张图片中,找到所有匹配的对象。

img_rgb=cv.imread("mario1.jpg",0)
template=cv.imread("mario2.jpg",0)
w,h=template.shape[::-1]
res=cv.matchTemplate(img_rgb,template,cv.TM_CCOEFF_NORMED)
#加入threshold阈值,可以调整包含对象的概率
threshold=0.8
loc=np.where(res>=threshold)
for pt in zip(*loc[::-1]):cv.rectangle(img_rgb,pt,(pt[0]+w,pt[1]+h),(0,0,255),2)
plt.subplot(),plt.imshow(img_rgb),plt.title("Result")
plt.show()

可以看见,在多对象匹配的情景中,有部分区域因为阈值的影响,并不能很好的匹配,但是它确实匹配到了最适合的所有对象。
在这里插入图片描述
当阈值为0.4时匹配到所有。
在这里插入图片描述
但是第三个金币的置信度不高,通过调整阈值,把它囊括进来。