天天看點

圖像處理:使用Numpy和OpenCV實作傅裡葉和逆傅裡葉變換

作者丨暴風雨中的白楊 

編輯丨3D視覺開發者社群

 content

1.什麼是傅裡葉變換及其理論基礎

2.Numpy實作傅裡葉和逆傅裡葉變換以及高通濾波示例

3.OpenCV實作傅裡葉和逆傅裡葉變換以及低通濾波示例

圖像處理:使用Numpy和OpenCV實作傅裡葉和逆傅裡葉變換

導讀

文章從實際出發,講述了什麼是傅裡葉變換,它的理論基礎以及Numpy和OpenCV實作傅裡葉和逆傅裡葉變換,并最終用高通濾波和低通濾波的示例。

1. 什麼是傅裡葉變換及其基礎理論

1.1傅裡葉變換

圖像處理一般分為直接對圖像内的像素進行處理的空間域處理和頻率域處理。

空間域處理主要劃分為灰階變換和空間濾波兩種形式。

  • 灰階變換是對圖像内的單個像素進行處理,比如調節對比度和處理門檻值等。
  • 空間濾波涉及圖像品質的改變,例如圖像平滑處理。空間域處理的計算簡單友善,運算速度更快。

頻率域處理是先将圖像變換到頻率域,然後在頻率域對圖像進行處理,最後再通過反變換将圖像從頻率域變換到空間域。

1.2基礎理論

時間差,在傅裡葉變換裡就是相位。相位表述的是與時間差相關的資訊。

在圖像處理過程中,傅裡葉變換就是将圖像分解為正弦分量和餘弦分量兩部分,即将圖像從空間域轉換到頻域。

數字圖像經過傅裡葉變換後,得到的頻域值是複數。是以,顯示傅裡葉變換的結果需要使用實數圖像(real image)加虛數圖像(complex image),或者幅度圖像(magnitude image)加相位圖像(phase image)的形式。因為幅度圖像包含了原圖像中我們所需要的大部分資訊,是以在圖像處理過程中,通常僅使用幅度圖像。

如果希望先在頻域内對圖像進行處理,再通過逆傅裡葉變換得到修改後的空域圖像,就必須同時保留幅度圖像和相位圖像。對圖像進行傅裡葉變換後,會得到圖像中的低頻和高頻資訊。低頻資訊對應圖像内變化緩慢的灰階分量。高頻資訊對應圖像内變化越來越快的灰階分量,是由灰階的尖銳過渡造成的。

傅裡葉變換的目的,就是為了将圖像從空域轉換到頻域,并在頻域内實作對圖像内特定對象的處理,然後再對經過處理的頻域圖像進行逆傅裡葉變換得到空域圖像。

2. Numpy實作傅裡葉和逆傅裡葉變換

2.1Numpy實作傅裡葉變換

Numpy子產品中的fft2()函數可以實作圖像的傅裡葉變換。

Numpy提供的實作傅裡葉變換的函數是numpy.fft.fft2(),它的文法格式是:

​傳回值 = numpy.fft.fft2(原始圖像)​

參數“原始圖像”的類型是灰階圖像,函數的傳回值是一個複數數組(complex ndarray)。經過該函數的處理,就能得到圖像的頻譜資訊。此時,圖像頻譜中的零頻率分量位于頻譜圖像(頻域圖像)的左上角。

為了便于觀察,通常會使用numpy.fft.fftshift()函數将零頻率成分移動到頻域圖像的中心位置。

函數numpy.fft.fftshift()的文法格式是:

​傳回值=numpy.fft.fftshift(原始頻譜)​

為了顯示為圖像,需要将它們的值調整到[0, 255]的灰階空間内,使用的公式為:

​像素新值=20*np.log(np.abs(頻譜值))​

用Numpy實作傅裡葉變換,觀察得到的頻譜圖像。

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


img = cv2.imread('./img/hand1.png',0) 
f = np.fft.fft2(img) 
fshift = np.fft.fftshift(f) 
magnitude_spectrum = 20*np.log(np.abs(fshift)) 
plt.subplot(121) 
plt.imshow(img, cmap = 'gray') 
plt.title('original') 
plt.axis('off') 
plt.subplot(122) 
plt.imshow(magnitude_spectrum, cmap = 'gray') 
plt.title('result') 
plt.axis('off') 
plt.show()      

2.2 實作逆傅裡葉變換

注意: 如果在傅裡葉變換過程中使用了numpy.fft.fftshift()函數移動零頻率分量,那麼在逆傅裡葉變換過程中,需要先使用numpy.fft.ifftshift()函數将零頻率分量移到原來的位置,再進行逆傅裡葉變換

函數numpy.fft.ifftshift()是numpy.fft.fftshift()的逆函數,其文法格式為:

​調整後的頻譜 = numpy.fft.ifftshift(原始頻譜)​

numpy.fft.ifft2()函數可以實作逆傅裡葉變換,傳回空域複數數組。

它是numpy.fft.fft2()的逆函數,該函數的文法格式為:

​傳回值=numpy.fft.ifft2(頻域資料)​

函數numpy.fft.ifft2()的傳回值仍舊是一個複數數組(complex ndarray)。

逆傅裡葉變換得到的空域資訊是一個複數數組,需要将該資訊調整至[0, 255]灰階空間内,使用的公式為:

​iimg = np.abs(逆傅裡葉變換結果)​

在Numpy内實作傅裡葉變換、逆傅裡葉變換,觀察逆傅裡葉變換的結果圖像。

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


img = cv2.imread('./img/hand1.png',0) 
f = np.fft.fft2(img) 
fshift = np.fft.fftshift(f)


ishift = np.fft.ifftshift(fshift) 
iimg = np.fft.ifft2(ishift) 


iimg = np.abs(iimg) 
 
plt.subplot(121), plt.imshow(img, cmap = 'gray') 
plt.title('original'), plt.axis('off') 
plt.subplot(122), plt.imshow(iimg, cmap = 'gray') 
plt.title('iimg'), plt.axis('off') 
plt.show()      

2.3高通濾波示例

一幅圖像内,同時存在着高頻信号和低頻信号。

低頻信号對應圖像内變化緩慢的灰階分量。例如,在一幅大草原的圖像中,低頻信号對應着顔色趨于一緻的廣袤草原。高頻信号對應圖像内變化越來越快的灰階分量,是由灰階的尖銳過渡造成的。如果在上面的大草原圖像中還有一頭獅子,那麼高頻信号就對應着獅子的邊緣等資訊。

濾波器能夠允許一定頻率的分量通過或者拒絕其通過,按照其作用方式可以劃分為低通濾波器和高通濾波器。

允許低頻信号通過的濾波器稱為低通濾波器。低通濾波器使高頻信号衰減而對低頻信号放行,會使圖像變模糊。允許高頻信号通過的濾波器稱為高通濾波器。高通濾波器使低頻信号衰減而讓高頻信号通過,将增強圖像中尖銳的細節,但是會導緻圖像的對比度降低。

傅裡葉變換可以将圖像的高頻信号和低頻信号分離。通過對圖像的頻域處理,可以實作圖像增強、圖像去噪、邊緣檢測、特征提取、壓縮和加密等操作。

在Numpy内對圖像進行傅裡葉變換,得到其頻域圖像。然後,在頻域内将低頻分量的值處理為0,實作高通濾波。最後,對圖像進行逆傅裡葉變換,得到恢複的原始圖像。

import cv2 
import numpy as np 
import matplotlib.pyplot as plt 
img = cv2.imread('./img/hand1.png',0) 
f = np.fft.fft2(img) 
fshift = np.fft.fftshift(f) 
rows, cols = img.shape 
crow, ccol = int(rows/2) , int(cols/2) 
fshift[crow-30:crow+30, ccol-30:ccol+30] = 0 
ishift = np.fft.ifftshift(fshift) 
iimg = np.fft.ifft2(ishift) 
iimg = np.abs(iimg) 
plt.subplot(121), plt.imshow(img, cmap = 'gray') 
plt.title('original'), plt.axis('off') 
plt.subplot(122), plt.imshow(iimg, cmap = 'gray') 
plt.title('iimg'), plt.axis('off') 
plt.show()      
圖像處理:使用Numpy和OpenCV實作傅裡葉和逆傅裡葉變換

3.OpenCV實作傅裡葉和逆傅裡葉變換及低通濾波示例 

3.1 OpenCV實作傅裡葉變換

OpenCV提供了函數cv2.dft()和cv2.idft()來實作傅裡葉變換和逆傅裡葉變換

函數cv2.dft()的文法格式為:

​傳回結果=cv2.dft(原始圖像,轉換辨別)​

在使用該函數時,需要注意參數的使用規範:

對于參數“原始圖像”,要首先使用np.float32()函數将圖像轉換成np.float32格式。“轉換辨別”的值通常為“cv2.DFT_COMPLEX_OUTPUT”,用來輸出一個複數陣列。

函數cv2.dft()傳回的結果與使用Numpy進行傅裡葉變換得到的結果是一緻的,但是它傳回的值是雙通道的,第1個通道是結果的實數部分,第2個通道是結果的虛數部分。

經過函數cv2.dft()的變換後,得到了原始圖像的頻譜資訊。此時,零頻率分量并不在中心位置,為了處理友善需要将其移至中心位置,可以用函數numpy.fft.fftshift()實作。

例如,如下語句将頻譜圖像dft中的零頻率分量移到頻譜中心,得到了零頻率分量位于中心的頻譜圖像dftshift。

​dftShift = np.fft.fftshift(dft)​

經過上述處理後,頻譜圖像還隻是一個由實部和虛部構成的值。要将其顯示出來,還要做進一步的處理才行。

函數cv2.magnitude()可以計算頻譜資訊的幅度。該函數的文法格式為:

​傳回值=cv2.magnitude(參數1,參數2)​

參數1:浮點型x坐标值,也就是實部。

參數2:浮點型y坐标值,也就是虛部,它必須和參數1具有相同的size

函數cv2.magnitude()的傳回值是參數1和參數2的平方和的平方根,公式為:

圖像處理:使用Numpy和OpenCV實作傅裡葉和逆傅裡葉變換

得到頻譜資訊的幅度後,通常還要對幅度值做進一步的轉換,以便将頻譜資訊以圖像的形式展示出來。簡單來說,就是需要将幅度值映射到灰階圖像的灰階空間[0, 255]内,使其以灰階圖像的形式顯示出來。

這裡使用的公式為:

result = 20*np.log(cv2.magnitude(實部,虛部))      
import numpy as np 
import cv2 
img = cv2.imread('./img/hand1.png',0) 
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT) 
print(dft) 
dftShift = np.fft.fftshift(dft) 
print(dftShift) 
result = 20*np.log(cv2.magnitude(dftShift[:, :,0], dftShift[:, :,1])) #兩個參數,需要拆分通道
print(result)      

用OpenCV函數對圖像進行傅裡葉變換,并展示其頻譜資訊。

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


img = cv2.imread('./img/hand1.png',0) 
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT) 
dftShift = np.fft.fftshift(dft) 
result = 20*np.log(cv2.magnitude(dftShift[:, :,0], dftShift[:, :,1])) 
plt.subplot(121), plt.imshow(img, cmap = 'gray') 
plt.title('original'), plt.axis('off') 
plt.subplot(122), plt.imshow(result, cmap = 'gray') 
plt.title('result'), plt.axis('off') 
plt.show()      

3.2實作逆傅裡葉變換

在OpenCV中,使用函數cv2.idft()實作逆傅裡葉變換,該函數是傅裡葉變換函數cv2.dft()的逆函數。其文法格式為:

​傳回結果=cv2.idft(原始資料)​

對圖像進行傅裡葉變換後,通常會将零頻率分量移至頻譜圖像的中心位置。如果使用函數numpy.fft.fftshift()移動了零頻率分量,那麼在進行逆傅裡葉變換前,要使用函數numpy.fft.ifftshift()将零頻率分量恢複到原來位置。

注意: 在進行逆傅裡葉變換後,得到的值仍舊是複數,需要使用函數cv2.magnitude()計算其幅度。

用OpenCV函數對圖像進行傅裡葉變換、逆傅裡葉變換,并展示原始圖像及經過逆傅裡葉變換後得到的圖像。

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


img = cv2.imread('./img/hand1.png',0) 
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT) 
dftShift = np.fft.fftshift(dft)


ishift = np.fft.ifftshift(dftShift) 
iImg = cv2.idft(ishift) 
iImg= cv2.magnitude(iImg[:, :,0], iImg[:, :,1]) # 計算幅度
plt.subplot(121), plt.imshow(img, cmap = 'gray') 
plt.title('original'), plt.axis('off') 
plt.subplot(122), plt.imshow(iImg, cmap = 'gray') 
plt.title('inverse'), plt.axis('off') 
plt.show()      

3.3低通濾波示例

在一幅圖像内,低頻信号對應圖像内變化緩慢的灰階分量。圖像進行低通濾波後會變模糊

圖像處理:使用Numpy和OpenCV實作傅裡葉和逆傅裡葉變換

實作的中間步驟

rows, cols = img.shape 
crow, ccol = int(rows/2) , int(cols/2) 
mask = np.zeros((rows, cols,2), np.uint8) # 二維的原因,有實部和虛部 
mask[crow-30:crow+30, ccol-30:ccol+30,:] = 1      

然後,将其與頻譜圖像進行運算,實作低通濾波。這裡采用的運算形式是:

fShift = dftShift*mask      

使用函數cv2.dft()對圖像進行傅裡葉變換,得到其頻譜圖像。然後,在頻域内将其高頻分量的值處理為0,實作低通濾波。最後,對圖像進行逆傅裡葉變換,得到恢複的原始圖像。

import numpy as np 
import cv2 
import matplotlib.pyplot as plt 
img = cv2.imread('./img/hand1.png',0) 
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT) 
dftShift = np.fft.fftshift(dft)


rows, cols = img.shape 
crow, ccol = int(rows/2) , int(cols/2) 
mask = np.zeros((rows, cols,2), np.uint8) 
#兩個通道,與頻域圖像比對 
mask[crow-30:crow+30, ccol-30:ccol+30,:] = 1 
fShift = dftShift*mask 
ishift = np.fft.ifftshift(fShift) 
iImg = cv2.idft(ishift) 
iImg= cv2.magnitude(iImg[:, :,0], iImg[:, :,1])


plt.subplot(121), plt.imshow(img, cmap = 'gray') 
plt.title('original'), plt.axis('off') 
plt.subplot(122), plt.imshow(iImg, cmap = 'gray') 
plt.title('inverse'), plt.axis('off') 
plt.show()      
圖像處理:使用Numpy和OpenCV實作傅裡葉和逆傅裡葉變換

經過低通濾波後,圖像的邊緣資訊被削弱了。

繼續閱讀