OpenCV 直方图与傅里叶变换(学习笔记) 电脑版发表于:2024/1/23 11:48 ![](https://img.tnblog.net/arcimg/hb/3c9a034e352c4476b4ec6d8cc07a0263.png) >#OpenCV 直方图与傅里叶变换(学习笔记) [TOC] ## 直方图 tn2>直方图是一种统计图表,用于展示数据的分布情况。 这里将展示图片中直方图,把每一个像素点作为展示。 ![](https://img.tnblog.net/arcimg/hb/c31920c44a014f25bb94f45815a54a35.png) ### 初始化 ```python import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline ``` ```python def cv_show(img,name): cv2.imshow(name,img) cv2.waitKey() cv2.destroyAllWindows() ``` ### 示例代码 tn2>直方图主要调用的方法是`cv2.calcHist(images,channels,mask,histSize,ranges)`。 | 参数 | 描述 | | ------------ | ------------ | |`images`| 原图像图像格式为 uint8 或 ?oat32。当传入函数时应 用中括号 [] 括来例如[img]| |`channels`| 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。 | |`mask`| 掩模图像。统整幅图像的直方图就把它为 None。但是如果你想统图像某一分的直方图的你就制作一个掩模图像并使用它。| |`histSize`|BIN的数目。也应用中括号括来| |`ranges`| 像素值范围常为 [0256] | ```python img = cv2.imread('cat.jpg',0) #0表示灰度图 hist = cv2.calcHist([img],[0],None,[256],[0,256]) hist.shape ``` >(256, 1) ```python plt.hist(img.ravel(),256); plt.show() ``` ![](https://img.tnblog.net/arcimg/hb/3d0adea36dfb44e18570d3e4c7d90689.png) ```python img = cv2.imread('cat.jpg') color = ('b','g','r') for i,col in enumerate(color): histr = cv2.calcHist([img],[i],None,[256],[0,256]) plt.plot(histr,color = col) plt.xlim([0,256]) ``` ![](https://img.tnblog.net/arcimg/hb/49e3a37a99fb4f8eb844d81344078400.png) tn2>通过以上操作我们看到了这张照片特定颜色的分布情况。 但是如果我们想关注具体那一部分的颜色信息我们就需要使用`mask`这个东西。 ### mask操作 ```python # 创建mast mask = np.zeros(img.shape[:2], np.uint8) print (mask.shape) # 维度 mask[100:300, 100:400] = 255 cv_show(mask,'mask') ``` >(414, 500) ![](https://img.tnblog.net/arcimg/hb/14a81d57295e4358a0dcd6079b806a9e.png) tn2>接下来我们通过mask对中间图片白框范围的小猫做一个直方图 ```python img = cv2.imread('cat.jpg', 0) cv_show(img,'img') ``` ![](https://img.tnblog.net/arcimg/hb/e07e5d94850e4f1fa50a861ce0b0ef7a.png) ```python masked_img = cv2.bitwise_and(img, img, mask=mask)#与操作 cv_show(masked_img,'masked_img') ``` ![](https://img.tnblog.net/arcimg/hb/8ba9b57a72214e17bd797be15402462f.png) ```python hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) ``` ```python plt.subplot(221), plt.imshow(img, 'gray') plt.subplot(222), plt.imshow(mask, 'gray') plt.subplot(223), plt.imshow(masked_img, 'gray') plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0, 256]) plt.show() ``` ![](https://img.tnblog.net/arcimg/hb/aab0746063914c8fa8ebcfd67f47f07f.png) ### 直方图均衡化 tn2>直方图均衡化是一种用于改善图片处理的技术,特别是在图像背景和前景都很暗的情况下。 这个方法通常用来增强医学和卫星图像的对比度,但也可以用于任何类型的图像。 ![](https://img.tnblog.net/arcimg/hb/94285dba175d47bdbcf1b989b0d750f5.png) ![](https://img.tnblog.net/arcimg/hb/003413b5e9464743ad40953c4dcae5b0.png) ![](https://img.tnblog.net/arcimg/hb/4e8c3f5dd0524077ab0b4af9c38cd1cc.png) tn2>加载原始图像。 ```python img = cv2.imread('clahe.jpg',0) #0表示灰度图 #clahe plt.hist(img.ravel(),256); plt.show() ``` ![](https://img.tnblog.net/arcimg/hb/9c44588a2820453b9684d06cf523d76d.png) tn2>进行直方图均值化。 ```python equ = cv2.equalizeHist(img) plt.hist(equ.ravel(),256) plt.show() ``` ![](https://img.tnblog.net/arcimg/hb/2bcace75b9644e0b85d2c16f332dda00.png) tn2>我们发现它产生了很多缝隙。 接下来我们查看一下两张图片的差异。 ```python res = np.hstack((img,equ)) cv_show(res,'res') ``` ![](https://img.tnblog.net/arcimg/hb/772c4f991f204d23bdd15ae76e58db60.png) tn2>发现比以前的要亮了很多。 ### 自适应直方图均衡化 tn2>它是直方图均衡化的一种改进方法,它对一小部分一小部分进行均衡化,不像直方图全局均衡化。 ```python clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) res_clahe = clahe.apply(img) res = np.hstack((img,equ,res_clahe)) # 三张图做对比 cv_show(res,'res') ``` ![](https://img.tnblog.net/arcimg/hb/6210f51089d04f648f6f8077b455d03e.png) tn2>发现自适应直方图均衡化比直方图均衡化要写实很多。 ## 傅里叶变换 tn2>简单来讲,傅里叶变换换就是一种把复杂信号分解成简单波形的方法,让我们能更容易地理解和处理这些信号。 更多请参考:https://zhuanlan.zhihu.com/p/19763358 ![](https://img.tnblog.net/arcimg/hb/78d37e56e35047569a67c81095e0ca6f.png) ### 傅里叶变换的作用 tn2>高频:变化剧烈的灰度分量,例如边界。 低频:变化缓慢的灰度分量,例如一片大海。 ### 滤波 tn2>低通滤波器:只保留低频,会使得图像模糊 高通滤波器:只保留高频,会使得图像细节增强 opencv中主要就是`cv2.dft()`和`cv2.idft()`,输入图像需要先转换成`np.float32` 格式。 得到的结果中频率为`0`的部分会在左上角,通常要转换到中心位置,可以通过`shift`变换来实现。 `cv2.dft()`返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255)。 ```python import numpy as np import cv2 from matplotlib import pyplot as plt 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) # 得到灰度图能表示的形式 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() ``` ![](https://img.tnblog.net/arcimg/hb/3e9aabaa897f466eb61031d9f80e9839.png) ```python 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) # 使用cv2.dft进行傅立叶变换,并使用cv2.DFT_COMPLEX_OUTPUT标志返回复数结果 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) # 创建一个低通滤波器掩码,初始时全为0 mask = np.zeros((rows, cols, 2), np.uint8) # 将掩码中心部分设置为1(30x30区域),其余为0 mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 1 # 将傅立叶变换结果与低通滤波器掩码相乘,实现低通滤波 fshift = dft_shift * mask # 使用ifftshift将中心移回原位 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'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_back, cmap='gray') plt.title('Result'), plt.xticks([]), plt.yticks([]) plt.show() # 显示图像 ``` ![](https://img.tnblog.net/arcimg/hb/91b97589bf1946f197183c80d7009121.png) ```python # 导入必要的库 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) # 使用cv2.dft进行傅立叶变换,并使用cv2.DFT_COMPLEX_OUTPUT标志返回复数结果 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) # 中心位置 # 创建一个高通滤波器掩码,初始时全为1 mask = np.ones((rows, cols, 2), np.uint8) # 将掩码中心部分设置为0(30x30区域),其余为1 mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 0 # 将傅立叶变换结果与高通滤波器掩码相乘,实现高通滤波 fshift = dft_shift * mask # 使用ifftshift将中心移回原位 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'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_back, cmap='gray') plt.title('Result'), plt.xticks([]), plt.yticks([]) plt.show() # 显示图像 ``` ![](https://img.tnblog.net/arcimg/hb/5cedc8a76a744985a28e5b5071e07039.png)