OpenCV-Python图像处理学习笔记(七)——直方图、图像变换

本篇学习笔记主要涉及直方图、图像变换等内容。

获取更多可以查看本栏目其他文章。

 往期内容:

OpenCV-Python图像处理学习笔记(一)——认识、安装、环境测试

OpenCV-Python图像处理学习笔记(二)——图像/视频读取保存、分割及边界填充

OpenCV-Python图像处理学习笔记(三)——数值运算、图像阈值、图像平滑(滤波)

OpenCV-Python图像处理学习笔记(四)——形态学操作、图像梯度
OpenCV-Python图像处理学习笔记(五)——Canny 边缘检测、图像金字塔、轮廓检测(一)

OpenCV-Python图像处理学习笔记(六)——轮廓检测(二)、模板匹配
OpenCV-Python图像处理学习笔记(七)——直方图、图像变换

目录

1  直方图

1.1  直方图的计算和绘制

 1.2  直方图均衡化

2  图像变换

2.1  傅里叶变换

 2.2  傅里叶逆变换

3  小结


导入必要Python包

import cv2
import numpy as np
import matplotlib.pyplot as plt

1  直方图

通过直方图可以对整幅图像的灰度分布有一个整体的了解。直方图的 x 轴是灰度值(0 到 255),y 轴是图片中具有同一个灰度值的 点的数目。 直方图其实就是对图像的另一种解释,通过直方图可以对图像的对比度,亮度,灰度分布等有一个直观的认识。

1.1  直方图的计算和绘制

函数 cv2.calcHist 可以统计一幅图像的直方图,具体参数如下:

cv2.calcHist(images, channels, mask, histSize, ranges)
  • images:原图像(图像格式为 uint8 或 float32)。当传入函数时应该 用中括号 [] 括起来。
  • channels:同样需要用中括号括起来,它会告诉函数我们要统计那幅图 像的直方图。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像 的话,传入的参数可以是 [0],[1],[2] 它们分别对应着通道 B,G,R。
  • mask:掩模图像。要统计整幅图像的直方图就把它设为 None。但是如 果你想统计图像某一部分的直方图的话,你就需要制作一个掩模图像,并使用它。
  • histSize:BIN 的数目。也应该用中括号括起来。
  • ranges:像素值范围,通常为 [0,256]
img = cv2.imread('.jpg', 0)

#  [img],[0],None,[256],[0,256],只有 mask 没有中括号
hist = cv2.calcHist([img], [0], None, [256], [0,256])

使用numpy中np.histogram()同样也可以计算图像的直方图,另一个函数 np.bincount(),它的运 行速度是 np.histgram 的十倍。所以对于一维直方图,最好使用这个函数。使用 np.bincount 时需要设置 minlength=256。但OpenCV 的函数要比 np.histgram() 快 40 倍。所以坚持使用 OpenCV 函数。

具体使用如下:

# img.ravel() 将图像转成一维数组,这里没有中括号。
hist, bins = np.histogram(img.ravel(), 256, [0, 256])

hist = np.bincount(img.ravel(),minlength=256)

有两种方法来绘制直方图:

使用 Matplotlib 中的绘图函数和使用 OpenCV 绘图函数,一般情况下使用Matplotlib,其中有直方图绘制函数:matplotlib.pyplot.hist() 它可以直接统计并绘制直方图。

img = cv2.imread('.jpg', 0)
plt.hist(img.ravel(), 256, [0, 256]);
plt.show()

 可以只使用 matplotlib 的绘图功能,比较方便的同时绘制多通道(BGR)的直方图。

img = cv2.imread('.jpg')
color = ('b', 'g', 'r')

# 对一个列表或数组既要遍历索引又要遍历元素时
# enumerate 会将数组或列表组成一个索引序列
for i, col in enumerate(color):
    histr = cv2.calcHist([img], [i], None, [256], [0, 256])
    plt.plot(histr, color=col)
    plt.xlim([0, 256])
    plt.show()

 如果需要统计并绘制图像局部的直方图,就需要使用掩膜。将要统计的 部分设置成白色,其余部分为黑色,就构成了一副掩模图像。然后把这个掩模 图像传给函数就可以了。

img = cv2.imread('.jpg', 0)

# 掩模图像
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img, img, mask = mask)
'''
bitwise_and(src1, src2, dst=None, mask=None)
src1、src2:为输入图像或标量,标量可以为单个数值或一个四元组
dst:可选输出变量,如果需要使用非None则要先定义,且其大小与输入变量相同
mask:图像掩膜,可选参数,为8位单通道的灰度图像,用于指定要更改的输出图像数组的元素,即输出图像像素只有mask对应位置元素不为0的部分才输出,否则该位置像素的所有通道分量都设置为0

返回值为结果图像矩阵,如果dst传入了实参,则返回值与dst对应实参相同。
'''

hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])

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()

 1.2  直方图均衡化

如果一幅图片整体很亮,那所有的像素值应该都会很高。但是一副高质量的图像的像素值分布应该很广泛。所以应该把它的直方图做一个横向拉伸,这就是直方图均衡化要做的事情。通常情况下这 种操作会改善图像的对比度。

 OpenCV 中的直方图均衡化函数为 cv2.equalizeHist()。这个函数的输入图片仅仅是一副灰度图像,输出结果是直方图均衡化之后的图像。

img = cv2.imread('.jpg', 0)

# 均衡化直方图
equ = cv2.equalizeHist(img)

res = np.hstack((img, equ))
cv2.imwrite('res.png', res)

均衡化前后前后图像对比如下: 

很明显,第二张图较第一张图对比度较高。由于太亮造成图像丢失了很多信息,造成这种结果的根本原因在于这幅图像的直方图并不是集中在某一个区域。为了解决这个问题,需要使用自适应的直方图均衡化。

  • cv2.createCLAHA (用于生成自适应均衡化图像)
img = cv2.imread('.png', 0)

'''
第一步:使用 cv2.createCLAHE(clipLimit=2.0, titleGridSize=(8, 8)) 实例化均衡直方图函数
第二步:使用 .apply 进行均衡化操作
第三步:进行画图操作
'''

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
'''
clipLimit:颜色对比度的阈值
titleGridSize:进行像素均衡化的网格大小,即在多少网格下进行直方图的均衡化操作
'''

cl1 = clahe.apply(img)
cv2.imwrite('clahe_2.jpg',cl1)

2  图像变换

2.1  傅里叶变换

任何连续测量的时序或信号,都可以表示为不同频率的正弦波信号的无限叠加。也就是说,傅里叶变换是一种特殊的积分变换,它能将满足一定条件的某个函数表示成正弦基函数的线性组合或者积分。

傅里叶变换详细原理内容可参考该作者文章。

傅里叶变换经常被用来分析不同滤波器的频率特性。对于数字图像这种离散的信号,频率大小表示信号变化的剧烈程度或者说是信号变化的快慢。

频率越大,变化越剧烈,频率越小,信号越平缓,对应到图像中,高频信号往往是图像中的边缘信号和噪声信号,而低频信号包含图像变化频繁的图像轮廓及背景等信号。

因此,我们可以做相应的锐化和模糊的处理:提出其中的高频分量做傅里叶逆变换得到的就是锐化的结果(高通滤波器)。 提出其中的低频分量做傅里叶逆变换得到的就是模糊的结果(低通滤波器)。

  • 低通滤波器:只保留低频,会使图像模糊;
  • 高通滤波器:只保留高频,会使图像细节增强。

可以使用 2D 离散傅里叶变换 (DFT) 分析图像的频域特性。实现DFT 的一个快速算法被称为 快速傅里叶变换(FFT)。

OpenCV 中相应的函数是 cv2.dft() cv2.idft()。输出是双通道的。第一个通道是结果的实数部分,第二个通道是结果的虚数部分。输入图像要首先转换成 np.float32 格式。cv2.dft() 得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,通过shift实现。

img = cv2.imread('.jpg', 0)

dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
'''
dst = cv2.dft (src, flags)
参数:
src:输入图像,需要转换格式为np.float32,可以为实数矩阵或者复数矩阵
flags:转换标志
(如DFT_COMPLEX_OUTPUT,对一维或二维实数数组正变换,输出一个同样尺寸的复数矩阵)
(DFT_REAL_OUTPUT,对一维或二维复数数组反变换,通常输出同样尺寸的复矩阵)
返回结果:是双通道的,第一个的结果是虚数部分,第二个通道的结果是实数部分
'''
# 将图像的低频部分移动到图像的中心
dft_shift = np.fft.fftshift(dft)

# 对dft_shift进行转换才可以在图像中展示
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))
'''
cv2.magnitude函数用于求取传入数据的平方根
代码公式表示为:(a1**2+a2**2)**(1/2)
'''

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()

 2.2  傅里叶逆变换

需要使用函数cv2.idft(),分别由低通滤波和高通滤波两种操作。首先我们需要构建一个掩模,与低频区域对应的地方设置为1, 与高频区域对应的地方设置为0。

rows, cols = img.shape

# 低通滤波
# 定义掩模:生成的掩模中间为1周围为0
# 求得图像的中心点位置
crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2)
mask = np.zeros((img.shape[0], img.shape[1], 2), np.uint8)
mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 1

## 高通滤波
## 定义掩模:生成的掩模中间为0周围为1
## 求得图像的中心点位置
# crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2) 
# mask = np.ones((img.shape[0], img.shape[1], 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)

# 使用cv2.magnitude转化为空间域内
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('Magnitude Spectrum')
plt.xticks([])
plt.yticks([])

plt.show()

其结果如下:

高通滤波
低通滤波

3  小结

OpenCV基础内容部分就到此结束,后期有机会再进行同步其他内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊仔阿大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值