目录
图像基本操作
阈值与平滑处理
图像阈值
图像平滑处理
图像形态学操作
图像梯度计算
Sobel 算子
Canny 边缘检测
图像金字塔与轮廓检测
图像轮廓
接口定义
轮廓绘制
轮廓特征与相似
模板匹配
傅里叶变换
傅里叶变换的作用
滤波
图像基本操作
- 读取图像: 使用cv2.imread()函数可以读取图像文件,并将其存储为一个NumPy数组。
import cv2
image = cv2.imread('image.jpg')
#图像的显示,也可以创建多个窗口
cv2.imshow('image',img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()
- 读取视频:cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1。
# 读取视频
vc = cv2.VideoCapture('./img/test.mp4')
# 检查是否打开正确
if vc.isOpened():
oepn, frame = vc.read()
else:
open = False
while open:
ret, frame = vc.read()
if frame is None:
break
if ret == True:
# 彩色图转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('result', gray)
if cv2.waitKey(100) & 0xFF == 27:
break
vc.release()
cv2.destroyAllWindows()
- 剪裁图像:使用数组切片操作可以裁剪图像的一部分
# 截取部分图像数据
img=cv2.imread('./img/cat.jpg')
# [起始x坐标:宽度,起始y坐标:高度]
cat=img[0:200,0:200]
cv2.imshow('cat', cat)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 边界填充:cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
top_size,bottom_size,left_size,right_size = (50,50,50,50)
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0)
- 图像融合:cv2.addWeighted(图像1, 0.4(权重), 图像2, 0.6(权重), 0(偏置项))
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
# 图像变换
img_dog = cv2.resize(img_dog, (500, 414))
img_dog.shape
# 图像融合
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0)
plt.imshow(res)
- 调整图像:使用cv2.resize()函数可以调整图像的大小
- 图像保存:使用cv2.imwrite()函数可以将图像保存到指定的文件路径
# 读取灰度图
img1=cv2.imread('./img/cat.jpg',cv2.IMREAD_GRAYSCALE)
print(img1)
print(img1.shape) # h,w
# 图像保存
cv2.imwrite('./img/mycat1.png',img1)
- 显示图像: 使用cv2.imshow()函数可以在窗口中显示图像
阈值与平滑处理
图像阈值
图像阈值是一种图像处理术,用于将图像中的像素值分为两个或多个不同的类别。阈值可以用来分割图像、提取感兴趣的目标或者进行图像增强等操作。
在图像阈值处理中,首先需要选择一个阈值,然后将图像中的像素值与该阈值进行比较。如果像素值大于阈值,则将其归为一类;如果像素值小于等于阈值,则将其归为另一类。这样就可以将图像中的像素分为不同的区域或者进行二值化处理。
常见的图像阈值处理方法包括全局阈值法、自适应阈值法和多阈值法等。全局阈值法是指在整个图像上使用一个固定的阈值进行处理;自适应阈值法是指根据图像局部的特性来选择不同的阈值;多阈值法是指使用多个阈值将图像分成多个不同的类别。
图像阈值处理可以应用于很多领域,如图像分割、目标检测、字符识别等。通过调整阈值的选择和处理方法,可以实现对图像的不同特征进行提取和分析。
图像平滑处理
图像平滑处理是一种常用的图像处理技术用于减少图像中的噪声和细节,使图像变得更加平滑和模糊。以下是几种常见的图像平滑处理方法:
1. 均值滤波:将图像中每个像素的值替换为其周围像素的平均值。这种方法可以有效地去除高频噪声,但可能会导致图像细节的模糊。
2. 高斯滤波:使用高斯函数对图像进行卷积操作,以减少噪声。高斯滤波器在中心像素周围的像素上施加较大的权重,而在边缘像素周围施加较小的权重。这种方法可以平滑图像并保留边缘信息。
3. 中值滤波:将图像中每个像素的值替换为其周围像素值的中值。中值滤波器对于去除椒盐噪声等椒盐噪声非常有效,但可能会导致图像细节的丢失。
4. 双边滤波:结合了空间域和灰度值域的信息,对图像进行平滑处理。双边滤波器考虑了像素之间的空间距离和像素值之间的差异,以保留边缘信息的同时减少噪声。
5. 维纳滤波:基于信号和噪声的统计特性,通过最小均方误差准则对图像进行滤波。维纳滤波器可以根据图像的噪声特性进行自适应调整,以实现更好的平滑效果。
img = cv2.imread('lenaNoise.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 均值滤波
# 简单的平均卷积操作
blur = cv2.blur(img, (3, 3))
cv2.imshow('blur', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 方框滤波
# 基本和均值一样,可以选择归一化
box = cv2.boxFilter(img,-1,(3,3), normalize=True)
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 方框滤波
# 基本和均值一样,可以选择归一化,容易越界
box = cv2.boxFilter(img,-1,(3,3), normalize=False)
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img, (5, 5), 1)
cv2.imshow('aussian', aussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 中值滤波
# 相当于用中值代替
median = cv2.medianBlur(img, 5) # 中值滤波
cv2.imshow('median', median)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 展示所有的
res = np.hstack((blur,aussian,median))
#print (res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
图像形态学操作
图像形态学是一种基于数学理论的图像处理方法,主要用于图像的形状分析和特征提取。它通过结构元素与图像进行卷积运算,从而改变图像的形状和结构。
常见的图像形态学操作包括腐蚀、膨胀、开运算、闭运算、击中击不中变换等。
1. 腐蚀(Erosion):腐蚀操作可以使图像中的物体边界向内部收缩。它通过将结构元素与图像进行逐像素的比较,只有当结构元素完全包含在图像中时,该像素才保留,否则被置为背景值。
pie = cv2.imread('pie.png')
cv2.imshow('pie', pie)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((30,30),np.uint8)
erosion_1 = cv2.erode(pie,kernel,iterations = 1)
erosion_2 = cv2.erode(pie,kernel,iterations = 2)
erosion_3 = cv2.erode(pie,kernel,iterations = 3)
res = np.hstack((erosion_1,erosion_2,erosion_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
2. 膨胀(Dilation):膨胀操作可以使图像中的物体边界向外部扩张。它也是通过将结构元素与图像进行逐像素的比较,只要结构元素与图像中的任意一个像素相交,该像素就被保留。
pie = cv2.imread('pie.png')
kernel = np.ones((30,30),np.uint8)
dilate_1 = cv2.dilate(pie,kernel,iterations = 1)
dilate_2 = cv2.dilate(pie,kernel,iterations = 2)
dilate_3 = cv2.dilate(pie,kernel,iterations = 3)
res = np.hstack((dilate_1,dilate_2,dilate_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 开运算(Opening):开运算是先进行腐蚀操作,再进行膨胀操作。它可以消除小的噪点,并保持物体的整体形状。
# 开:先腐蚀,再膨胀
img = cv2.imread('dige.png')
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 闭运算(Closing):闭运算是先进行膨胀操作,再进行腐蚀操作。它可以填充物体内部的小孔,并保持物体的整体形状。
# 闭:先膨胀,再腐蚀
img = cv2.imread('dige.png')
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()
5. 击中击不中变换(Hit-or-Miss Transform):击中击不中变换是一种用于检测特定形状的图像操作。它通过定义两个结构元素,分别表示要击中的形状和要排除的形状,从而实现对特定形状的检测。
6. 梯度计算:梯度计算基于形态学操作,通过对图像进行膨胀和腐蚀操作,然后计算两幅图像之间的差异来获取边缘信息。
具体而言,图像形态学梯度计算的步骤如下:
- 首先,选择一个结构元素(也称为核或模板),它定义了形态学操作的形状和大小。
- 对原始图像进行膨胀操作,膨胀操作将结构元素与图像进行卷积,得到一个膨胀后的图像。
- 对原始图像进行腐蚀操作,腐蚀操作同样将结构元素与图像进行卷积,得到一个腐蚀后的图像。
- 将膨胀后的图像减去腐蚀后的图像,得到梯度图像。
梯度图像中的亮点表示边缘或轮廓的位置,亮点的强度表示边缘或轮廓的强度。通过调整结构元素的形状和大小,可以控制梯度计算的敏感度和精度。
# 梯度=膨胀-腐蚀
pie = cv2.imread('pie.png')
kernel = np.ones((7,7),np.uint8)
dilate = cv2.dilate(pie,kernel,iterations = 5)
erosion = cv2.erode(pie,kernel,iterations = 5)
res = np.hstack((dilate,erosion))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()
7. 礼帽与黑帽:
图像形态学中的礼帽(top hat)和黑帽(black hat)是两种常用的形态学操作,用于图像的亮区域和暗区域的提取。
礼帽操作是通过对原始图像进行开运算(opening)后再与原始图像相减得到的结果。开运算是先进行腐蚀操作,再进行膨胀操作,用于平滑图像并保留较大的亮区域。因此,礼帽操作可以提取出原始图像中较小的、明亮的细节部分。
黑帽操作则是通过对原始图像进行闭运算(closing)后再与原始图像相减得到的结果。闭运算是先进行膨胀操作,再进行腐蚀操作,用于平滑图像并保留较大的暗区域。因此,黑帽操作可以提取出原始图像中较小的、暗的细节部分。
这两种形态学操作可以用于图像增强、噪声去除、边缘检测等应用。礼帽操作可以突出图像中的细节部分,而黑帽操作可以突出图像中的暗部分。
#礼帽 = 原始输入-开运算结果
#黑帽 = 闭运算-原始输入
#礼帽
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()
#黑帽
img = cv2.imread('dige.png')
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()
这些图像形态学操作在图像处理中有广泛的应用,例如边缘检测、形状分析、图像重建等。
图像梯度计算
Sobel 算子
Sobel算子是一种常用的图像梯度算子,用于检测图像中的边缘。它是一种离散型的差分算子,通过计算图像中每个像素点的梯度来确定边缘的位置和方向。
Sobel算子分为水平和垂直两个方向的算子,分别用于计算图像在水平和垂直方向上的梯度。水平方向的Sobel算子通常表示为:
```
-1 0 1
-2 0 2
-1 0 1
```
垂直方向的Sobel算子通常表示为:
```
-1 -2 -1
0 0 0
1 2 1
```
对于图像中的每个像素点,分别将其与周围像素点进行加权求和,得到水平和垂直方向上的梯度值。通过计算这两个方向上的梯度值,可以得到每个像素点的梯度幅值和梯度方向,从而实现边缘检测。
使用Sobel算子进行边缘检测时,通常需要先将图像转换为灰度图像,然后对灰度图像应用Sobel算子。最后,可以根据梯度幅值进行阈值处理,将边缘提取出来。
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
- ddepth:图像的深度
- dx和dy分别表示水平和竖直方向
- ksize是Sobel算子的大小
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show(img,'img')
# 计算dx
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
# 将负值转换为正值
sobelx = cv2.convertScaleAbs(sobelx)
# 计算dy
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
分别计算x和y,再求和
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
"""
不建议直接计算
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')
"""
#不同算子的差异
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')
Canny 边缘检测
Canny边缘检测是一种经典的图像处理算法,用于检测图像中的边缘。它由John F. Canny在1986年提出,并被广泛应用于计算机视觉和图像处理领域。
Canny边缘检测算法的主要步骤包括:
1.噪声抑制:使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。
2.计算梯度:使用Sobel算子计算图像中每个像素点的梯度幅值和方向。梯度幅值表示像素点的灰度变化程度,而梯度方向表示变化最快的方向。
3.非极大值抑制:在梯度方向上进行非极大值抑制,即对每个像素点,只保留沿着梯度方向上幅值最大的像素点,以细化边缘。
4.双阈值检测:根据设定的高阈值和低阈值,对非极大值抑制后的图像进行阈值分割。高于高阈值的像素点被认为是强边缘,低于低阈值的像素点被认为是弱边缘,介于两者之间的像素点根据其与强边缘的连通性进行判断。
5.边缘连接:通过连接强边缘像素点和与之相连的弱边缘像素点,形成完整的边缘。
Canny边缘检测算法具有以下优点:
- 对噪声具有较好的抑制能力。
- 检测到的边缘具有良好的连续性。
- 可以通过调整阈值来控制检测到的边缘数量。
img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)
# minvel maxval
v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
img=cv2.imread("car.png",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,120,250)
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
图像金字塔与轮廓检测
图像金字塔是一种用于图像处理和计算机视觉的技术,它可以通过对原始图像进行多次缩放和降采样来构建一系列不同分辨率的图像。每个分辨率的图像被称为金字塔的一层,而金字塔的结构类似于由大到小的金字塔形状。
图像金字塔有两种类型:高斯金字塔和拉普拉斯金字塔。高斯金字塔通过对原始图像进行重复的平滑和下采样操作来生成不同分辨率的图像。而拉普拉斯金字塔则是通过从高斯金字塔中恢复原始图像并与下一层高斯图像相减得到的。
轮廓检测是一种用于检测图像中物体边界的技术。它可以通过分析图像中的亮度、颜色或纹理等特征来提取出物体的轮廓信息。常用的轮廓检测算法包括Canny边缘检测、Sobel算子、Laplacian算子等。
图像轮廓
图像轮廓是指图像中物体的边界线或者轮廓线。它是由物体与背景之间的灰度或颜色差异形成的。图像轮廓可以用于物体检测、形状分析、目标跟踪等计算机视觉任务中。
在图像处理中,常用的方法来提取图像轮廓包括边缘检测和阈值分割。边缘检测算法可以通过检测图像中灰度或颜色的变化来找到物体的边界。常见的边缘检测算法有Sobel算子、Canny算子等。阈值分割则是将图像根据灰度或颜色的阈值进行分割,得到物体的二值图像,然后通过连通区域分析等方法找到物体的轮廓。
图像轮廓可以表示为一系列的点或者线段,也可以通过多边形或曲线来近似表示。常见的表示方法有边界框、最小外接矩形、最小外接圆等。
图像轮廓在计算机视觉和图像处理领域有广泛的应用。例如,可以通过轮廓提取来进行物体识别和分类,可以通过轮廓匹配来进行目标跟踪和姿态估计,还可以通过轮廓分析来进行形状分析和测量等。
接口定义
cv2.findContours(img,mode,method)
mode:轮廓检索模式
- RETR_EXTERNAL :只检索最外面的轮廓;
- RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
- RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;
method:轮廓逼近方法
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
- CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
import cv2
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 输入图像
img = cv2.imread('image.jpg')
# 将图像变为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 为了更高的准确率,使用二值图像
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# # 显示图像
# cv_show('thresh',thresh)
# # 图像轮廓
# # binary二值类结果 contours轮廓点 hierarchy层级信息
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# print(contours.shape)
# #传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# # 注意需要copy,要不原图会变。。。
draw_img = img.copy()
# # 绘制轮廓图 图像 轮廓点 全部点 G B R 线条宽度
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
cv_show('res',res)
轮廓特征与相似
# 轮廓特征
# 获取轮廓点
cnt = contours[0]
#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)
# 轮廓近似
img = cv2.imread('contours2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
epsilon = 0.15*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
# 边界矩阵
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('轮廓面积与边界矩形比',extent)
# 外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')
模板匹配
模板匹配是一种在图像处理和计算机视觉中常用的技术,用于在一幅图像中寻找与给定模板最相似的部分。它可以用于目标检测、物体识别、图像对齐等应用。
模板匹配的基本思想是将一个固定大小的模板图像与待匹配图像进行比较,通过计算它们之间的相似度来确定最佳匹配位置。常用的相似度度量方法包括平方差匹配、相关性匹配和归一化互相关匹配。
在平方差匹配中,模板图像与待匹配图像的对应像素值之间的差异被计算,并求和得到一个匹配度量值。最小匹配度量值对应的位置即为最佳匹配位置。
在相关性匹配中,模板图像与待匹配图像的对应像素值之间的相关性被计算,并求和得到一个匹配度量值。最大匹配度量值对应的位置即为最佳匹配位置。
在归一化互相关匹配中,模板图像与待匹配图像的对应像素值之间的归一化互相关性被计算,并求和得到一个匹配度量值。最大匹配度量值对应的位置即为最佳匹配位置。
模板匹配的实现可以使用各种图像处理库或者计算机视觉库,如OpenCV。在实际应用中,还可以通过使用多尺度模板匹配、旋转不变模板匹配等技术来提高匹配的准确性和鲁棒性。
# 模板匹配
img = cv2.imread('lena.jpg', 0)
template = cv2.imread('face.jpg', 0)
h, w = template.shape[:2]
img.shape
template.shape
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
res.shape
#最小差异 最大差异 最小距离坐标 最大距离坐标
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
for meth in methods:
img2 = img.copy()
# 匹配方法的真值
method = eval(meth)
print (method)
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 如果是平方差匹配TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 画矩形
cv2.rectangle(img2, top_left, bottom_right, 255, 2)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隐藏坐标轴
plt.subplot(122), plt.imshow(img2, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
傅里叶变换
傅里叶变换是一种数学变换,它将一个函数从时域(时间域)转换到频域(频率域)。通过傅里叶变换,我们可以将一个信号分解成一系列不同频率的正弦和余弦函数的叠加。这种变换在信号处理、图像处理、通信等领域中广泛应用。
傅里叶变换的数学表达式为:
F(ω) = ∫[−∞,+∞] f(t) * e^(-jωt) dt
其中,F(ω)表示频域中的复数函数,f(t)表示时域中的函数,e^(-jωt)是一个复指数函数,ω是角频率。
傅里叶变换有两种形式:连续傅里叶变换(CTFT)和离散傅里叶变换(DFT)。连续傅里叶变换适用于连续信号,而离散傅里叶变换适用于离散信号。
在实际应用中,傅里叶变换可以用来分析信号的频谱特性,提取信号中的频率信息,滤波、压缩、编码等。同时,傅里叶变换也有逆变换,可以将频域信号转换回时域信号。
傅里叶变换的作用
-
高频:变化剧烈的灰度分量,例如边界
-
低频:变化缓慢的灰度分量,例如一片大海
滤波
-
低通滤波器:只保留低频,会使得图像模糊
-
高通滤波器:只保留高频,会使得图像细节增强
- opencv中主要就是cv2.dft()和cv2.idft(),输入图像需要先转换成np.float32 格式。
- 得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现。
- cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255)
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 灰度图转换
img = cv2.imread('lena.jpg',0)
# 输入图像需要先转换成np.float32 格式
img_float32 = np.float32(img)
# 先dft
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
# 转换到中心位置
dft_shift = 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
# IDFT
fshift = dft_shift*mask
# 从中心位置还原
f_ishift = np.fft.ifftshift(fshift)
# 后idft
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('Result'), plt.xticks([]), plt.yticks([])
plt.show()
img = cv2.imread('lena.jpg',0)
img_float32 = np.float32(img)
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
rows, cols = img.shape
crow, ccol = int(rows/2) , int(cols/2) # 中心位置
# 高通滤波
mask = np.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0
# IDFT
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('Result'), plt.xticks([]), plt.yticks([])
plt.show()