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)
此方法速度很慢,并不是特别适合处理一般图像。