OpenCV直方图均衡化
-
- [0. 前言](#0. 前言)
- [1. 直方图均衡化算法](#1. 直方图均衡化算法)
- [2. 全局直方图均衡化](#2. 全局直方图均衡化)
-
- [2.1 使用最小-最大归一化缩放 CDF](#2.1 使用最小-最大归一化缩放 CDF)
- [2.2 将输入 RGB 图像转换为 LAB 空间](#2.2 将输入 RGB 图像转换为 LAB 空间)
- [3. 自适应直方图均衡化](#3. 自适应直方图均衡化)
-
- [3.1 算法原理](#3.1 算法原理)
- [3.2 使用 OpenCV 执行自适应直方图均衡化](#3.2 使用 OpenCV 执行自适应直方图均衡化)
- [4. 直方图均衡化结果](#4. 直方图均衡化结果)
- 小结
- 系列链接
0. 前言
对比度拉伸/直方图均衡化使用单调非线性映射重新分配输入图像中的像素强度值,以使输出图像具有均匀的强度分布(平坦直方图),从而增强图像的对比度。可以使用以下公式描述直方图均衡化的转换函数:
s k = T ( r k ) = ∑ j = 0 k P r ( r j ) = ∑ j = 0 k n j N s_k=T(r_k)=\sum_{j=0}^kP_r(r_j)=\frac {\sum_{j=0}^kn_j} N sk=T(rk)=j=0∑kPr(rj)=N∑j=0knj
其中, 0 ≤ r k ≤ 1 0≤r_k≤1 0≤rk≤1, k = 0 , 1 , 2 , . . . , 255 k=0,1,2,...,255 k=0,1,2,...,255, N N N 表示图像中像素总数, n j n_j nj 表示灰度强度 j j j 的频率。
1. 直方图均衡化算法
算法的实现步骤如下:
- 首先,计算灰度图像的所有像素的概率质量函数 (
Probability Mass Function
,PMF
),假设像素值为正整数,且在区间[0,255]
之内 - 根据计算的
PMF
计算累积分配函数 (Cumulative Distributive Function
,CDF
),每个像素将被分配一个单浮点值 C D F ( p ) CDF(p) CDF(p),该值表示图像中像素至少为值 p p p 的概率,因此, 0 ≤ C D F ( p ) ≤ 1 0≤CDF(p)≤1 0≤CDF(p)≤1,CDF
是像素值的递增函数 - 将每个像素 p p p 的 C D F ( p ) CDF(p) CDF(p) 从
[0-1]
缩放到[0-255]
,并用缩放后的CDF
给出的新灰度值重新分配像素值
直方图均衡化的实现有以下两种方式:一种是整个图像上的全局操作,第二个是局部(自适应)操作,通过将图像分为若干块,并在每个图像块上执行直方图均衡化。
在本节中,我们将学习如何实现以上算法,然后使用函数实现全局和局部(自适应)直方图均衡化,并在 RGB
和 LAB
颜色空间中增强彩色图像的对比度。
2. 全局直方图均衡化
(1) 首先,导入所需的库和函数:
python
import numpy as np
import matplotlib.pylab as plt
import cv2
def plot_image(image, title=None, sz=10):
plt.imshow(image)
plt.title(title, size=sz)
plt.axis('off')
(2) 定义函数 plot_hist()
,该函数将灰度图像作为参数并计算直方图,然后计算其像素值的 CDF
,使用指定的颜色绘制直方图和 CDF
。我们将使用此函数可视化直方图或彩色图像的每个颜色通道:
python
def plot_hist(img, col='r'):
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()
plt.plot(cdf_normalized, color = col)
plt.hist(img.flatten(),256,[0,256], color = col, alpha = 0.1)
plt.xlim([0,256])
plt.title('CDF and histogram of the color channels', size=10)
#plt.legend(('cdf','histogram'), loc = 'upper left')
return bins, cdf
(3) 通过使用上述函数,定义函数 plot_img_hist()
来绘制 RGB
彩色图像及其每个颜色通道的强度直方图:
python
def plot_img_hist(img, title):
plt.figure(figsize=(20,10))
plt.subplot(121), plot_image(img, title)
plt.subplot(122), plot_hist(img[...,0], 'r'), plot_hist(img[...,1], 'g'), plot_hist(img[...,2], 'b')
plt.show()
(4) 读取输入彩色图像并使用 cvtcolor()
函数将其从 BGR
模式转换为 RGB
模式。由于 OpenCV
以 BGR
格式存储图像,要使用 matplotlib.pyplot
模块的 imshow()
函数以正确的颜色显示图像,我们需要图像处于 RGB
模式:
python
img = cv2.imread('2.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
通过使用 NumPy
的 histogram()
和 cumsum()
函数计算 CDF
,实现直方图均衡化算法。
2.1 使用最小-最大归一化缩放 CDF
(1) 重新指定像素值以拉伸图像的对比度:
python
img2 = img.copy()
for i in range(3):
hist,bins = np.histogram(img[...,i].flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_m = np.ma.masked_equal(cdf,0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
#cdf_m = 255 * cdf / cdf[-1] # normalize
cdf = np.ma.filled(cdf_m,0).astype('uint8')
img2[...,i] = cdf[img[...,i]]
# use linear interpolation of cdf to find new pixel values
#img2[...,i] = np.reshape(np.interp(img[...,i].flatten(),bins[:-1],cdf), img[...,i].shape)
可以在输入图像上应用 OpenCV
的 EqualizeHist()
函数获得相同的输出结果。
(2) 接下来,我们在 LAB
空间中对同一图像应用对比度增强。在 LAB
空间中,只有 L
通道对应于强度,其余通道代表像素的颜色,因此,仅在 L
通道上执行均衡就可以完成均衡化操作。
2.2 将输入 RGB 图像转换为 LAB 空间
在 LAB
空间中执行均衡化,只需使用 OpenCV
的 equalizeHist()
函数执行直方图均衡,并且仅需要重新分配 L
通道中的像素强度值,而无需对彩色通道进行处理。将输出图像从 LAB
色彩空间转换回 RGB
空间:
python
img_lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)
equ = img_lab.copy()
equ[...,0] = cv2.equalizeHist(equ[...,0])
equ = np.clip(cv2.cvtColor(equ, cv2.COLOR_LAB2RGB), 0, 255)
3. 自适应直方图均衡化
我们已经学习了如何实现全局直方图均衡化,接下来,我们继续实现局部(自适应)直方图均衡化,它通常可以避免局部图像特征的过曝或曝光不足。
3.1 算法原理
由于亮度过高,如果使用全局均衡化算法,因为其直方图不限于特定区域,我们可能会丢失某些像素的大部分信息。为了解决此问题,需要使用自适应直方图均衡。我们可以将图像分割为多个图像块(默认情况下的图像块的大小为 8 x 8
)。
然后,对每个图像块执行直方图均衡化。因此,当图像块中不含噪声时,直方图将限制于一个小的区域内。而如果含有噪音,直方图区域将被放大。为避免这种情况,应用对比度限制。如果任一直方图条柱 (bin
) 高于指定的对比度限制(在 OpenCV
中默认为 40
),则在应用直方图均衡之前,需要将这些像素剪切并分布到其他直方图条柱中。在均衡化后,为了去除图像块边界中的伪影,应用双线性插值。
3.2 使用 OpenCV 执行自适应直方图均衡化
使用 OpenCV
中的函数 createCLAH()
创建一个对比度受限的自适应直方图均衡对象,然后使用该函数均衡 LAB
色彩空间的亮度通道。
python
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl = img_lab.copy()
cl[...,0] = clahe.apply(cl[...,0])
cl = np.clip(cv2.cvtColor(cl, cv2.COLOR_LAB2RGB), 0, 255)
4. 直方图均衡化结果
(1) 绘制低对比度输入图像及其颜色通道的直方图:
python
plot_img_hist(img, 'Original Image')
(2) 接下来,通过在每个 RGB
通道上应用直方图均衡化,然后将它们组合起来输出 RGB
图像并绘制输出图像:
python
plot_img_hist(img2, 'Hist. Equalized')
(3) 接下来,通过在 LAB
色彩空间中的亮度通道上应用直方图均衡化,然后转换回 RGB
颜色空间,绘制输出图像:
python
plot_img_hist(equ, 'Hist. Equalized (LAB space)')
(4) 最后,通过在 LAB
色彩空间中的亮度通道上应用自适应直方图均衡化,然后转换回 RGB
颜色空间,绘制输出图像:
python
plot_img_hist(cl, 'Adaptive Hist. Equalized (LAB space)')
从以上结果图像可以看出,从对比度增强和保留精细的局部特征方面来讲,自适应技术的表现最好。
小结
直方图均衡化 (Histogram Equalization
) 是一种增强图像对比度的方法,其通过将一副图像的直方图分布变成近似均匀分布,从而达到增强图像的对比度的目的。直方图均衡化是一种非常经典的算法,可以用于对图像进行预处理,本节中,介绍了直方图均衡化的基本概念,并且实现两种(全局和局部)直方图均衡化算法。
系列链接
Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲/逆扭曲
Python图像处理【6】通过哈希查找重复和类似的图像
Python图像处理【7】采样、卷积与离散傅里叶变换
Python图像处理【8】使用低通滤波器模糊图像
Python图像处理【9】使用高通滤波器执行边缘检测
Python图像处理【10】基于离散余弦变换的图像压缩
Python图像处理【11】利用反卷积执行图像去模糊
Python图像处理【12】基于小波变换执行图像去噪
Python图像处理【13】使用PIL执行图像降噪
Python图像处理【14】基于非线性滤波器的图像去噪
Python图像处理【15】基于非锐化掩码锐化图像