OpenCV中的圖像處理 —— 傅里葉變換+模板匹配(opencv進行圖像處理)
OpenCV中的圖像處理 —— 傅里葉變換+模板匹配
現在也在逐漸深入啦,希望跟大家一起進步越來越強
1. 傅里葉變換
關于傅里葉變換最重要的兩個概念:時域與頻域。以時間作為參照來觀察動態世界的方法我們稱其為時域分析,而頻域是什么呢,它是描述信號在頻率方面特性時用到的一種坐標系,頻域圖顯示了在一個頻率范圍內每個給定頻帶內的信號量。貫穿時域與頻域的方法之一就是大名鼎鼎的傅里葉分析,它可以分為傅里葉級數和傅里葉變換,傅里葉變換也就是我們這一部分要說的東西
傅里葉變換是分析線性系統的一個有力工具。 它告訴我們任何周期函數,都可以看作是不同振幅,不同相位正弦波的疊加。從數學意義上說,傅里葉變換將一個任意的周期函數分解成為無窮個正弦函數的和的形式;從物理效果上看,傅里葉變換實現了將信號從空間域到頻率域的轉換
在計算機視覺中傅立葉變換用于分析各種濾波器的頻率特性,對于圖像,使用2D離散傅里葉變換(DFT)查找頻域(還有一種稱為快速傅立葉變換(FFT)的快速算法)這一段文字是不是不太好理解,因為里面涉及太多比較深奧的東西了,傅里葉變換本身是比較難的一個點,在這里我就不細說了,我們只說說在計算機視覺領域我們是怎么用它的,想要深入了解的同學來看看這篇文章:深入淺出的講解傅里葉變換(真正的通俗易懂)
對于正弦信號,如果幅度在短時間內變化比較快,則可以說它是高頻信號,如果變化緩慢,則為低頻信號,我們可以將相同的想法擴展到圖像,圖像中的振幅在哪里急劇變化?當然是在邊緣點或噪聲,因此,可以說邊緣和噪聲是圖像中的高頻內容
1.1 Numpy實現傅里葉變換
Numpy提供了FFT軟件包來查找傅里葉變換,**np.fft.fft2()為我們提供了頻率轉換,它將是一個復雜的數組,它的第一個參數是輸入圖像(灰度圖像),第二個參數是可選的,它決定輸出數組的大小。如果它大于輸入圖像的大小,則在計算FFT之前用零填充輸入圖像。如果小于輸入圖像,將裁切輸入圖像。如果未傳遞任何參數,則輸出數組的大小將與輸入的大小相同,但是現在獲得的結果它的零頻率分量(DC分量)將位于左上角,為了便于分析我們要把它居中,居中處理關系到np.fft.fftshift()**函數
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread(r'E:\image\test16.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('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray') plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([]) plt.show()
我們可以看到幅度譜的中心有更多白色區域,說明圖像低頻內容更多。找到了幅度譜那我們是不是可以在頻域中進行一些操作呢?例如高通濾波和重建圖像,實質就是找到逆DFT,我們首先要用尺寸為60*60的矩形窗口遮罩抵消低頻信號,然后使用np.fft.ifftshift()應用反向移位,以使DC分量再次出現在左上角。然后使用np.ifft2()函數找到逆FFT,結果同樣是一個復數
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread(r'E:\image\test15.png', 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.abs(img_back) plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(132), plt.imshow(magnitude_spectrum, cmap='gray') plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([]) plt.subplot(133), plt.imshow(img_back) plt.title('Result in JET'), plt.xticks([]), plt.yticks([]) plt.show()
1.2 OpenCV實現傅里葉變換
OpenCV為此提供了cv.dft()和cv.idft()函數。它返回與前一個相同的結果,但是有兩個通道。第一個通道是結果的實部,第二個通道是結果的虛部。輸入圖像首先應轉換為np.float32
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread(r'E:\image\test17.png', 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])) plt.subplot(121), plt.imshow(img, cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray') plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([]) plt.show()
這一塊兒代碼有幾個難懂的地方,沒關系我們來分析一下:第一次看這段代碼會有幾個疑問,cv.dft()函數的參數怎么傳遞?cv.magnitude()函數是怎么用的?
cv.dft()函數的作用是對一維或者二維浮點數數組進行正向或反向離散傅里葉變換,其中包括4個參數,第一個即源圖像,第二個參數是OutputArray類型的dst,函數調用后返回的運算結果存在這里,它的尺寸和類型取決于第三個參數flags轉換標識符,它的默認值為0(參考自:opencv:dft()函數詳解)
cv.magnitude()函數用來計算二維矢量的幅值,其中包括3個參數,第一個是InputArray類型的x,表示矢量的浮點型X坐標值,也就是實部,第二個參數是InputArray類型的y,表示矢量的浮點型Y坐標值,也就是虛部,第三個參數是輸出的幅值
接下來我們需要做OpenCV中DFT的逆變換,上一節用了高通濾波器HPF,這一部分我們會將低通濾波器LPF應用到圖像中
注意:通常,OpenCV函數cv.dft()和cv.idft()比Numpy函數更快,大約快3倍,但是Numpy函數更容易使用
我們把這一部分的代碼放在后面,與DFT的性能優化放在一起更容易理解
1.3 DFT的性能優化
對于某些數組尺寸,DFT的計算性能較好,例如當數組大小為2的冪時,速度最快,對于大小為2、3和5的乘積的數組,也可以非常有效地進行處理,關于代碼的性能問題,我們可以在找到DFT之前將數組的大小修改為任何最佳大小(通過填充零),對于OpenCV,我們必須手動填充零,但是對于Numpy,指定FFT計算的新大小,它將自動為您填充零
關于尋找最優大小,OpenCV為此提供了一個函數:cv.getOptimalDFTSize()
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread(r'E:\image\test17.png', 0) rows, cols = img.shape print(rows, cols) # 計算DFT效率最佳的尺寸 nrows = cv2.getOptimalDFTSize(rows) ncols = cv2.getOptimalDFTSize(cols) print(nrows, ncols) nimg = np.zeros((nrows, ncols)) nimg[:rows, :cols] = img img = nimg # OpenCV計算快速傅里葉變換,輸入圖像應首先轉換為np.float32,然后使用函數cv2.dft()和cv2.idft()。 # 返回結果與Numpy相同,但有兩個通道。第一個通道為有結果的實部,第二個通道為有結果的虛部。 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.subplot(121), plt.imshow(img, cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray') plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([]) plt.show() rows, cols = img.shape crow, ccol = rows // 2, cols // 2 # 首先創建一個mask,中心正方形為1,其他均為0 # 如何刪除圖像中的高頻內容,即我們將LPF應用于圖像。它實際上模糊了圖像。 # 為此首先創建一個在低頻時具有高值的掩碼,即傳遞LF內容,在HF區域為0。 mask = np.zeros((rows, cols, 2), np.uint8) mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 1 # 應用掩碼Mask和求逆DTF fshift = dft_shift * mask f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1]) plt.subplot(121), plt.imshow(img, cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_back, cmap='gray') plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([]) plt.show()
2. 模板匹配
模板匹配是一種用于在較大圖像中搜索和查找模板圖像位置的方法。為此,OpenCV帶有一個函數cv.matchTemplate(), 它只是將模板圖像滑動到輸入圖像上(就像在2D卷積中一樣),然后在模板圖像下比較模板和輸入圖像的拼圖,它返回一個灰度圖像,其中每個像素表示該像素的鄰域與模板匹配的程度。但是在這個方法的內涵中,到底是用什么樣的方法去做做匹配的呢?這個就由函數的其中一個參數來決定了
如果輸入圖像的大小為 (W * H) ,而模板圖像的大小為 (w * h) ,則輸出圖像的大小將為(W-w + 1,H-h + 1) ,在我們得到結果后,可以使用cv.minMaxLoc()函數查找最大/最小值在哪,將其作為矩形的左上角,并以 (w,h) 作為矩形的寬度和高度
匹配方式(比較方法)與cv.matchTemplate()的參數有關,我們先來看看都有哪些參數,并且應用不同的參數會有什么樣不同的效果。該函數第一個參數是源圖像,第二個參數是模板圖像,第三個參數是匹配的結果圖像,第四個參數是用于指定比較的方法
cv::TM_SQDIFF:該方法使用平方差進行匹配,因此最佳的匹配結果在結果為0處,值越大匹配結果越差
cv::TM_SQDIFF_NORMED:該方法使用歸一化的平方差進行匹配,最佳匹配也在結果為0處
cv::TM_CCORR:相關性匹配方法,該方法使用源圖像與模板圖像的卷積結果進行匹配,因此,最佳匹配位置在值最大處,值越小匹配結果越差
cv::TM_CCORR_NORMED:歸一化的相關性匹配方法,與相關性匹配方法類似,最佳匹配位置也是在值最大處
cv::TM_CCOEFF:相關性系數匹配方法,該方法使用源圖像與其均值的差、模板與其均值的差二者之間的相關性進行匹配,最佳匹配結果在值等于1處,最差匹配結果在值等于-1處,值等于0直接表示二者不相關
cv::TM_CCOEFF_NORMED:歸一化的相關性系數匹配方法,正值表示匹配的結果較好,負值則表示匹配的效果較差,也是值越大,匹配效果也好
資料摘自:【OpenCV3】模板匹配——cv::matchTemplate()詳解
2.1 單對象的模板匹配
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread(r'E:\image\test15.png', 0) img2 = img.copy() template = cv.imread(r'E:\image\temple.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) # 如果方法是TM_SQDIFF或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) plt.subplot(121), plt.imshow(res, cmap='gray') plt.title('Matching Result'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img, cmap='gray') plt.title('Detected Point'), plt.xticks([]), plt.yticks([]) plt.suptitle(meth) plt.show()
2.2 多對象的模板匹配
上一節我們匹配了梅大人的面部,但是如果圖像中有很多滿足匹配條件的模板呢?這個時候cv.minMaxLoc()不會為我們提供所有位置,我們會使用閾值化
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img_rgb = cv.imread(r'E:\image\num.png') img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY) template = cv.imread(r'E:\image\temple2.png', 0) w, h = template.shape[::-1] res = cv.matchTemplate(img_gray, template, cv.TM_CCOEFF_NORMED) 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) cv.imshow('res.png', img_rgb) cv.waitKey(0) cv.destroyWindow()
代碼解析:第8行的shape()函數是numpy.core.fromnumeric中的函數,它的功能是查看矩陣或者數組的維數,第11行的locloc是滿足“res >= threshold”的像素點的索引集合,第12行的函數zip()用可迭代的對象作為參數,將對象中對應的元素打包成一個個元組,然后返回由這些元組組成的列表
(注:文章內容參考OpenCV4.1中文官方文檔)
如果文章對您有所幫助,記得一鍵三連支持一下哦
AI OpenCV 圖像處理 機器學習 機器視覺
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。