import cv2 #opencv读取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
1 直方图
1.1 cv2.calcHist(images,channels,mask,histSize,ranges)
- images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]
- channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。
- mask: 掩模图像。统整幅图像的直方图就把它为 None。但是如 果你想统图像某一分的直方图的你就制作一个掩模图像并 使用它。
- histSize:BIN 的数目。也应用中括号括来
- ranges: 像素值范围常为 [0256]
img = cv2.imread('cat.jpg',0) #0表示灰度图
# 计算图像的直方图
# [img]:输入图像,是一个图像列表。这里我们使用的是单通道的灰度图像。
# [0]:表示计算第0个通道的直方图,即灰度图像的唯一通道。
# None:没有使用掩码,即对整个图像进行计算。
# [256]:表示直方图的分组数(bins),将灰度级分为256个区间,对应0到255的像素值。
# [0, 256]:像素值范围为0到256,表示灰度值的范围。
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
hist.shape
# 使用 Matplotlib 生成图像的直方图
# img.ravel():将图像的多维数组展平为一维数组,适用于计算直方图的像素值。
# 256:直方图的分组数(bins),将像素值划分为 256 个区间,对应 0 到 255 的灰度值。
plt.hist(img.ravel(), 256)
plt.show()
img = cv2.imread('cat.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
# 计算图像的颜色通道直方图并绘制
# [img]:输入图像,是一个包含多个颜色通道的图像(如 BGR 图像)。
# [i]:颜色通道的索引,i 表示当前处理的通道(0=蓝色,1=绿色,2=红色)。
# None:不使用掩码,对整个图像进行处理。
# [256]:将像素值分为 256 个区间(bins),对应灰度值 0 到 255。
# [0, 256]:像素值范围为 0 到 256。
histr = cv2.calcHist([img], [i], None, [256], [0, 256])
# 使用 Matplotlib 绘制颜色通道的直方图
# histr:当前通道的直方图数据。
# color=col:指定绘制的颜色,'col' 是当前通道的颜色('b' = 蓝色,'g' = 绿色,'r' = 红色)。
plt.plot(histr, color=col)
# 设置 X 轴的范围,确保显示 0 到 255 的像素值范围。
plt.xlim([0, 256])
1.2 mask操作
# 创建mast
mask = np.zeros(img.shape[:2], np.uint8)
print (mask.shape)
mask[100:300, 100:400] = 255
cv_show(mask,'mask')
img = cv2.imread('cat.jpg', 0)
cv_show(img,'img')
# masked_img = cv2.bitwise_and(img, img, mask=mask)#与操作
# 使用按位与操作结合掩码进行图像处理
# img:输入图像,这是需要应用掩码的原始图像。
# cv2.bitwise_and():OpenCV 中的按位与操作,对图像的每个像素进行按位与运算。
# mask=mask:指定掩码,掩码是一个与图像大小相同的二值图像,只有掩码中为白色(255)的部分会保留,其他部分会被忽略(黑色部分对应的像素值被屏蔽)。
# 最终结果是图像中的某些部分根据掩码进行保留,其余部分被屏蔽。
masked_img = cv2.bitwise_and(img, img, mask=mask)
cv_show(masked_img,'masked_img')
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
# 计算带掩码的图像直方图
# [img]:输入图像,计算直方图的目标图像。
# [0]:计算第 0 个通道的直方图(灰度图或图像的第一个颜色通道)。
# mask:用于限定计算区域的掩码,只对掩码中为白色(255)的区域进行直方图计算。
# [256]:直方图的分组数(bins),将像素值分为 256 个区间(灰度值 0 到 255)。
# [0, 256]:像素值的范围,表示从 0 到 255 的灰度值。
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
# 使用 Matplotlib 创建子图并可视化图像和直方图
# plt.subplot(221):创建 2x2 网格的第一个子图,用于显示原始图像。
plt.subplot(221), plt.imshow(img, 'gray') # 显示原始图像,使用灰度颜色映射
# plt.subplot(222):创建 2x2 网格的第二个子图,用于显示掩码。
plt.subplot(222), plt.imshow(mask, 'gray') # 显示掩码图像,使用灰度颜色映射
# plt.subplot(223):创建 2x2 网格的第三个子图,用于显示应用了掩码后的图像。
plt.subplot(223), plt.imshow(masked_img, 'gray') # 显示带掩码的图像,使用灰度颜色映射
# plt.subplot(224):创建 2x2 网格的第四个子图,用于显示完整图像和掩码区域的直方图。
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) # 绘制完整图像和掩码区域的直方图
# plt.xlim([0, 256]):设置 X 轴范围,确保显示从 0 到 255 的像素值。
plt.xlim([0, 256])
# plt.show():显示所有子图。
plt.show()
1.3 直方图均衡化
img = cv2.imread('clahe.jpg',0) #0表示灰度图 #clahe
# img 是一个图像数组。ravel() 将图像数组展平为一维数组。
# 256 是直方图的 bin 数量,通常用来表示图像的灰度级别范围(0-255)。
plt.hist(img.ravel(), 256)
plt.show()
# img 是一个灰度图像数组。cv2.equalizeHist() 对图像进行直方图均衡化。
# 直方图均衡化用于提高图像对比度,使得图像的像素值分布更加均匀。
equ = cv2.equalizeHist(img)
# 展平均衡化后的图像数组为一维数组,以便绘制直方图。
# 256 是直方图的 bin 数量,表示图像灰度级别的范围(0-255)。
plt.hist(equ.ravel(), 256)
plt.show()
res = np.hstack((img,equ))
cv_show(res,'res')
1.4 自适应直方图均衡化
# 创建 CLAHE 对象,用于进行对比度限制自适应直方图均衡化。
# clipLimit:指定对比度限制阈值,通常用于防止噪声放大的过度增强。
# tileGridSize:指定局部均衡化的区域大小,通常为 (8, 8) 或其他合适的大小。
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
# 对图像进行 CLAHE 处理,生成对比度增强后的图像。
res_clahe = clahe.apply(img)
# 将原始图像 (img)、均衡化图像 (equ) 和 CLAHE 处理后的图像 (res_clahe) 横向拼接在一起。
# np.hstack() 函数用于将多个数组沿水平方向(列方向)拼接。
res = np.hstack((img, equ, res_clahe))
# res 现在包含了三幅图像并排显示,便于比较原始图像、直方图均衡化结果和 CLAHE 处理结果。
cv_show(res,'res')
2 傅里叶变换
我们生活在时间的世界中,早上7:00起来吃早饭,8:00去挤地铁,9:00开始上班。。。以时间为参照就是时域分析。
但是在频域中一切都是静止的!
傅里叶变换
傅里叶变换的作用
-
高频:变化剧烈的灰度分量,例如边界
-
低频:变化缓慢的灰度分量,例如一片大海
滤波
-
低通滤波器:只保留低频,会使得图像模糊
-
高通滤波器:只保留高频,会使得图像细节增强
-
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
# 读取图像,第二个参数 0 表示以灰度模式加载图像
img = cv2.imread('lena.jpg', 0)
# 将图像转换为 32 位浮点数格式,以便执行傅里叶变换
img_float32 = np.float32(img)
# 对图像进行离散傅里叶变换 (DFT),flags=cv2.DFT_COMPLEX_OUTPUT 确保输出是复数,包含实部和虚部
dft = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT)
# 使用 np.fft.fftshift 将零频率分量移到频谱图中心
dft_shift = np.fft.fftshift(dft)
# 计算幅度谱,使用 cv2.magnitude 计算二维向量的模
# dft_shift[:,:,0] 是实部,dft_shift[:,:,1] 是虚部
# 取对数缩放值,以便能更好地显示幅度谱(否则值可能太小)
magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1]))
# 使用 Matplotlib 显示图像
# 第一个子图,显示原始输入图像,使用灰度颜色映射
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image') # 设置标题为 "Input Image"
plt.xticks([]), plt.yticks([]) # 隐藏 x 和 y 轴刻度
# 第二个子图,显示傅里叶变换后的幅度谱,使用灰度颜色映射
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum') # 设置标题为 "Magnitude Spectrum"
plt.xticks([]), plt.yticks([]) # 隐藏 x 和 y 轴刻度
# 显示图像
plt.show()
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 读取图像,第二个参数 0 表示以灰度模式加载图像
img = cv2.imread('lena.jpg', 0)
# 将图像转换为 32 位浮点数格式,以便执行傅里叶变换
img_float32 = np.float32(img)
# 对图像进行离散傅里叶变换 (DFT),flags=cv2.DFT_COMPLEX_OUTPUT 确保输出是复数,包含实部和虚部
dft = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT)
# 使用 np.fft.fftshift 将零频率分量移到频谱图中心
dft_shift = np.fft.fftshift(dft)
# 获取图像的行和列
rows, cols = img.shape
# 确定图像的中心位置
crow, ccol = int(rows/2), int(cols/2)
# 创建一个掩码,尺寸与图像相同,2 表示包含实部和虚部的二维数组
# 掩码初始化为全 0
mask = np.zeros((rows, cols, 2), np.uint8)
# 在中心区域设置为 1,形成低通滤波器(30x30 的矩形区域)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# 将掩码应用到频谱上,实现低通滤波
fshift = dft_shift * mask
# 进行逆 FFT 移位,将频率分量还原到原始位置
f_ishift = np.fft.ifftshift(fshift)
# 对频谱应用逆离散傅里叶变换 (IDFT),返回到空间域
img_back = cv2.idft(f_ishift)
# 计算返回图像的幅值,得到最终的结果
img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
# 使用 Matplotlib 显示图像
# 第一个子图,显示原始输入图像,使用灰度颜色映射
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image') # 设置标题为 "Input Image"
plt.xticks([]), plt.yticks([]) # 隐藏 x 和 y 轴刻度
# 第二个子图,显示低通滤波后的图像
plt.subplot(122), plt.imshow(img_back, cmap='gray')
plt.title('Result') # 设置标题为 "Result"
plt.xticks([]), plt.yticks([]) # 隐藏 x 和 y 轴刻度
# 显示图像
plt.show()
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 读取图像,0 表示灰度模式
img = cv2.imread('lena.jpg', 0)
# 将图像转换为 32 位浮点数格式,以便执行傅里叶变换
img_float32 = np.float32(img)
# 对图像执行离散傅里叶变换,flags=cv2.DFT_COMPLEX_OUTPUT 输出复数图像
dft = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT)
# 使用 np.fft.fftshift 将零频率分量移到频谱图的中心位置
dft_shift = np.fft.fftshift(dft)
# 获取图像的行数和列数
rows, cols = img.shape
# 确定图像的中心位置,用于创建高通滤波器
crow, ccol = int(rows / 2), int(cols / 2)
# 创建高通滤波掩码,初始化为全 1(保留所有频率)
mask = np.ones((rows, cols, 2), np.uint8)
# 将中心区域设置为 0,形成一个高通滤波器
# 该区域对应于低频信息,设置为 0 将低频滤除
mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 0
# 将掩码应用到傅里叶变换后的频谱上,实现高通滤波
fshift = dft_shift * mask
# 进行逆傅里叶变换之前,使用 np.fft.ifftshift 将频率分量移回原始位置
f_ishift = np.fft.ifftshift(fshift)
# 对频谱应用逆离散傅里叶变换,将频域数据转换回空间域
img_back = cv2.idft(f_ishift)
# 计算返回图像的幅值,得到最终的结果
img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])
# 使用 Matplotlib 显示图像
# 第一个子图显示原始图像
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image') # 标题为 "Input Image"
plt.xticks([]), plt.yticks([]) # 隐藏 x 和 y 轴刻度
# 第二个子图显示高通滤波后的图像
plt.subplot(122), plt.imshow(img_back, cmap='gray')
plt.title('Result') # 标题为 "Result"
plt.xticks([]), plt.yticks([]) # 隐藏 x 和 y 轴刻度
# 显示图像
plt.show()
3 图像的基本变换
图像的大小与缩放
resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
- src: 要缩放的图片
- dsize: 缩放之后的图片大小, 元组和列表表示均可.
- dst: 可选参数, 缩放之后的输出图片
- fx, fy: x轴和y轴的缩放比, 即宽度和高度的缩放比.
- interpolation: 插值算法, 主要有以下几种:
- INTER_NEAREST, 邻近插值, 速度快, 效果差.
- INTER_LINEAR, 双线性插值, 使用原图中的4个点进行插值. 默认.
- INTER_CUBIC, 三次插值, 原图中的16个点.
- INTER_AREA, 区域插值, 效果最好, 计算时间最长.