3.11傅立葉變換
3.11.1Fourier Transform in OpenCV-cv.dft、cv.magnitude、cv.
idft
這兩個函數是圖像頻域處理(如去噪、邊緣增強、紋理分析)的基礎工具。
1.cv.dft()
?—— 離散傅里葉變換
功能:
將圖像從空間域(像素強度)轉換到頻域(頻率成分),用于分析圖像中的高頻(邊緣、噪聲)和低頻(平滑區域)信息。
dft_output = cv.dft(src, flags=cv.DFT_COMPLEX_OUTPUT)
src: 輸入圖像(需為單通道灰度圖,格式 np.float32)。
flags:
cv.DFT_COMPLEX_OUTPUT(默認):輸出復數形式(實部+虛部)。
cv.DFT_SCALE:歸一化結果(便于可視化)。
cv.DFT_ROWS:對多行單獨執行DFT。
返回值:
復數形式的頻域矩陣(尺寸與輸入相同,類型為 np.complex64 或雙通道 np.float32),是一個雙通道數組(dft[:,:,0]為實部,dft[:,:,1]為虛部)。
cv.magnitude()
?是 OpenCV 中用于計算二維向量幅值的函數,在頻域處理中常用于從傅里葉變換的復數結果(實部 + 虛部)中提取幅度譜(即頻率分量的強度)
magnitude = cv.magnitude(x, y)
參數:
x:輸入矩陣的實部(如 dft[:,:,0])。
y:輸入矩陣的虛部(如 dft[:,:,1])。
返回值:與輸入同尺寸的單通道矩陣,每個元素值為:
?
作用:?
2.?cv.idft()
?—— 逆離散傅里葉變換
功能:
將頻域數據還原回空間域圖像,常用于濾波后的重建。
idft_output = cv.idft(src, flags=cv.DFT_SCALE | cv.DFT_REAL_OUTPUT)
src: 頻域數據(復數或雙通道浮點矩陣)。
flags:
cv.DFT_SCALE:對結果歸一化(抵消DFT的系數)。
cv.DFT_REAL_OUTPUT:輸出實數圖像(單通道)。
返回值:
重建的空間域圖像(雙通道 np.float32)。
關鍵注意事項
輸入格式:
cv.dft()
?的輸入必須是單通道?np.float32
。頻譜可視化:需對DFT結果取對數并歸一化(
cv.magnitude()
?+?cv.normalize()
)。頻移操作:使用?
np.fft.fftshift()
?將低頻分量移到頻譜中心。濾波應用:通過掩碼(
mask
)修改頻域數據(如高通/低通濾波)。
3.demo
通過DFT和IDFT)實現把圖片從顏色空間變換到頻域空間,再通過低通濾波,模糊圖像以去除高頻成分(如邊緣和噪聲)
from operator import eq
from turtle import title
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
from numpy.fft import fftshiftimg = cv.imread("image4.png")
assert img is not None, "file could not be read, check with os.path.exists()"
img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)# 轉到頻域可視化
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)magnitude = 20*np.log(cv.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))# 從頻域轉到顏色空間,帶有低通濾波mask
row,col = img.shape
crow,ccol = row//2,col//2
mask = np.zeros((row,col,2),np.uint8)
mask[crow-30:crow+30,ccol-30:ccol+30] = 1fshift = dft_shift * mask
fshift = np.fft.ifftshift(fshift)
img_back = cv.idft(fshift)
img_back = cv.magnitude(img_back[:,:,0],img_back[:,:,1])titles = ['img','dft','img_back']
images = [img,magnitude,img_back]
for i in range(3):plt.subplot(1,3,i+1)plt.imshow(images[i],cmap='gray')plt.title(titles[i])
plt.show()
?
4.解讀-幅度譜的關鍵特征
中心亮點:代表低頻成分(圖像中的平滑區域,如背景或緩慢變化的顏色)。
外圍亮點:代表高頻成分(圖像中的邊緣、紋理或噪聲)。
對稱性:幅度譜是中心對稱的(因為DFT的結果是共軛對稱的)。
示例分析
如果原始圖像中有明顯的邊緣(如物體的輪廓),幅度譜的外圍會顯示對應的亮點。
如果圖像整體較模糊(低頻主導),幅度譜的中心區域會更亮。
如何驗證結果是否正確?
?
5.最常用的2種濾波操作
必須避開的坑
錯誤1:未轉
float32
直接DFT → 報錯錯誤2:逆變換后未取實部(
.real
)→ 結果異常錯誤3:未中心化頻譜 → 低頻分布在四角
3.11.2DFT的性能優化
3.11.3Why Laplacian is a High Pass Filter?
不做重點介紹
3.12模板匹配Template Matching-cv2.matchTemplate()、cv2.minMaxLoc()
模板匹配(Template Matching)
- 作用:在輸入圖像(大圖)中查找與模板圖像(小圖)最相似的部分。
- 原理:滑動模板窗口,用數學方法計算每個位置的相似度,返回最佳匹配位置。
- 特點:對平移有效,但對旋轉、縮放、光照變化敏感。適合固定圖案的快速定位(如按鈕檢測、LOGO識別)
匹配函數:cv2.matchTemplate()
result = cv2.matchTemplate(image, templ, method, mask=None)
參數:
image:輸入圖像(大圖),必須為單通道灰度圖(若彩色需先轉灰度)。
templ:模板圖像(小圖),尺寸≤大圖且數據類型相同。
method:匹配方法(見下方6種選項)。
mask:可選掩模,與模板同尺寸,僅匹配掩模非零區域。
返回值:
result:浮點型矩陣,尺寸為 (W-w+1, H-h+1)(W/H=大圖寬高,w/h=模板寬高)。
每個像素值表示該位置的匹配程度(具體含義取決于method)。
method介紹如下:
方法名 公式/原理 最佳匹配位置 返回值范圍
cv2.TM_SQDIFF 平方差最小化 最小值點 [0, ∞)
cv2.TM_SQDIFF_NORMED 歸一化平方差 最小值點 [0, 1]
cv2.TM_CCORR 互相關(亮度敏感) 最大值點 [0, ∞)
cv2.TM_CCORR_NORMED 歸一化互相關 最大值點 [0, 1]
cv2.TM_CCOEFF 相關系數(去均值) 最大值點 [-1, 1]
cv2.TM_CCOEFF_NORMED 歸一化相關系數(推薦) 最大值點 [-1, 1]
推薦:TM_CCOEFF_NORMED(抗亮度變化)或 TM_SQDIFF_NORMED(精確差異)。
結果解析函數:cv2.minMaxLoc()
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
作用:找到匹配結果矩陣中的極值位置。
返回值:
min_val, max_val:最小/最大值(根據method決定取哪個)。
min_loc, max_loc:對應極值的坐標(左上角點,格式 (x, y))。
demo:完成單模板的匹配,多模板其實就是加個循環結構畫結果出來
from unittest import result
import cv2 as cv
import numpy as npimg1 = cv.imread('image4.png')
img = cv.cvtColor(img1,cv.COLOR_BGR2GRAY)
template = cv.imread('template.png',cv.IMREAD_GRAYSCALE)
h,w = template.shaperesult = cv.matchTemplate(img,template,cv.TM_CCOEFF_NORMED)# 獲取最佳匹配位置
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
# print(min_val, max_val, min_loc, max_loc)
top_left = max_loc # TM_CCOEFF_NORMED取最大值位置
bottom_right = (top_left[0] + w, top_left[1] + h)# 繪制矩形框
cv.rectangle(img1,top_left,bottom_right,[0,255,0],2)cv.imshow('match',img1)
cv.waitKey(0)
cv.destroyAllWindows()
3.13hough transformation-cv2.HoughLines()、cv2.HoughLinesP()、cv2.HoughCircles()
3.13.1直線霍夫變換
標準霍夫線變換:cv2.HoughLines()
lines = cv2.HoughLines(image, rho, theta, threshold, lines=None, srn=None, stn=None, min_theta=None, max_theta=None)
參數:
image:輸入圖像(必須為二值圖,常用Canny邊緣檢測結果)。
rho:距離分辨率(像素),推薦1。
theta:角度分辨率(弧度),推薦np.pi/180(即1度)。
threshold:投票閾值(低于此值的直線被忽略),值越小檢測越多。
srn, stn:多尺度霍夫變換參數,通常設為0。
min_theta, max_theta:角度范圍限制(默認0~π)。
返回值:
lines:(N, 1, 2)的NumPy數組,每行為[ρ, θ]。
概率霍夫線變換(推薦):cv2.HoughLinesP()
lines = cv2.HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None)
參數:
minLineLength:線段最小長度(小于此值被過濾)。
maxLineGap:允許的同一直線上點的最大間隔。
返回值:
lines:(N, 1, 4)數組,每行為[x1, y1, x2, y2](線段端點坐標)。
對比
HoughLines
:返回無限長的直線參數,需自行計算端點。HoughLinesP
:直接返回線段端點,更易使用。
demo1:使用標準霍夫變換
import cv2 as cv
import numpy as npimg = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)lines = cv.HoughLines(edges,1,np.pi/180,200)
for line in lines:rho,theta = line[0]a = np.cos(theta)b = np.sin(theta)x0 = a*rhoy0 = b*rhox1 = int(x0 + 1000*(-b))y1 = int(y0 + 1000*(a))x2 = int(x0 - 1000*(-b))y2 = int(y0 - 1000*(a))cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)cv.imwrite('houghlines3.jpg',img)
demo2:使用概率霍夫變換
import cv2 as cv
import numpy as npimg = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
lines = cv.HoughLinesP(edges,1,np.pi/180,100,minLineLength=100,maxLineGap=10)
for line in lines:x1,y1,x2,y2 = line[0]cv.line(img,(x1,y1),(x2,y2),(0,255,0),2)cv.imwrite('houghlines5.jpg',img)
3.13.2霍夫圓變換
demo:檢測圓形
import numpy as np
import cv2 as cvimg = cv.imread('opencv-logo-white.png', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
img = cv.medianBlur(img,5)
cimg = cv.cvtColor(img,cv.COLOR_GRAY2BGR)circles = cv.HoughCircles(img,cv.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)circles = np.uint16(np.around(circles))
for i in circles[0,:]:# draw the outer circlecv.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)# draw the center of the circlecv.circle(cimg,(i[0],i[1]),2,(0,0,255),3)cv.imshow('detected circles',cimg)
cv.waitKey(0)
cv.destroyAllWindows()
3.14基于分水嶺算法的圖像分割-cv2.watershed()
這一節簡單介紹原理,目前分割基本都是基于深度學習的。如果需要處理重疊/粘連物體,那么需要這個方法。
markers = cv2.watershed(image, markers)
參數:
image:輸入3通道彩色圖像(BGR格式)。
markers:標記矩陣(與圖像同尺寸,數據類型為int32):
markers[i,j]=0:未確定區域(待分割)
markers[i,j]>0:前景對象的唯一標簽
markers[i,j]=-1:算法輸出的邊界像素
返回值:
修改后的markers矩陣,邊界像素被標記為-1。
3.15GrabCut算法-cv2.grabCut()
mask, bgdModel, fgdModel = cv2.grabCut(image, mask, rect, bgdModel, fgdModel, iterCount, mode
)
參數說明:
參數 類型 說明
image np.uint8 (RGB) 輸入圖像(必須為3通道彩色圖)
mask np.uint8 掩模矩陣,取值:
? 0(GC_BGD): 明確背景
? 1(GC_FGD): 明確前景
? 2(GC_PR_BGD): 可能背景
? 3(GC_PR_FGD): 可能前景
rect tuple 前景的矩形框 (x,y,w,h),僅在 mode=GC_INIT_WITH_RECT 時使用
bgdModel/fgdModel np.float64 算法內部使用的臨時數組,初始化為 np.zeros((1,65), np.float64)
iterCount int 迭代次數(通常5~10次)
mode int GC_INIT_WITH_RECT(矩形初始化)或 GC_INIT_WITH_MASK(掩模初始化)
返回值:
mask:更新后的掩模(需后處理提取最終結果)
bgdModel/fgdModel:可復用的模型參數
關鍵注意事項
輸入圖像:必須是RGB格式(BGR順序),且為
np.uint8
類型。掩模初始化:
矩形模式:
mask
會被自動覆蓋,無需手動設置。掩模模式:需提前用
GC_FGD
/GC_BGD
標記明確區域。
結果優化:
對邊緣粗糙部分,可用
cv2.erode()
或cv2.GaussianBlur()
平滑。
性能權衡:
iterCount=5
:快速但可能欠分割iterCount=10
:速度慢但精度高
demo:使用該方法進行分割
import numpy as np
import cv2# 1. 加載圖像
img = cv2.imread('test.jpg')
mask = np.zeros(img.shape[:2], np.uint8) # 初始化掩模# 2. 定義前景矩形框 (x,y,width,height)
rect = (50, 50, 300, 400) # 需完全包圍目標物體# 3. 執行GrabCut
bgdModel = np.zeros((1,65), np.float64)
fgdModel = np.zeros((1,65), np.float64)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)# 4. 提取前景(類型轉換)
mask = np.where((mask==1)|(mask==3), 255, 0).astype('uint8')# 5. 應用掩模并顯示
result = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
此方法速度很慢,并不是特別適合處理一般圖像。