OpenCV-Python -- Histograms-2:Histogram Equalization

学习目标

  • 这一节,我们要学习直方图均衡化(histogram equalization),然后提高图像的对比度。

理论

考虑一张图像的像素值仅分布在特定的范围,比如较亮的图像的像素值通常都较大。但是,一张好的图像应该分布均匀。所以我们需要通过直方图均衡化使得图像分布更加均匀,见下图:
在这里插入图片描述

可以参考:Histogram Equalization。下面我们使用Numpy计算图像的直方图,实例如下:

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

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

hist,bins = np.histogram(img.flatten(),256,[0,256])

cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()

plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.show()

运行结果如下:
在这里插入图片描述
从上图可以看出,直方图分布在亮的区域。我们需要均匀的分布。那么我们需要一个变换函数,使得像素分布均匀,这正是直方图均衡化。

现在,我们需要寻找最小的直方图值(除了0),然后应用直方图均衡化,但是,这里我们使用的是Numpy中的掩码数组(masked array)的概念。对于掩码数组,所有的操作作用于非掩码的元素

cdf_m = np.ma.masked_equal(cdf,0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
cdf = np.ma.filled(cdf_m,0).astype('uint8')

现在我们可以建立输入与输出像素值的对应关系:

img2 = cdf[img]

然后按照上述的计算直方图的代码重新计算,完整的代码如下:

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

img = cv2.imread('wiki.jpg', 0)
print(img.shape)
# cv2.imshow('img', img)
# cv2.waitKey(0)
hist, bins = np.histogram(img.flatten(), 256, [0, 256])

cdf = hist.cumsum()
# print(hist)
# print(len(cdf))
# print(cdf)
#
# cdf_normalized = cdf * hist.max() / cdf.max()
#
# plt.plot(cdf_normalized, color='b')
# plt.hist(img.flatten(), 256, [0, 256], color='r')
# plt.xlim([0, 256])
# plt.legend(('cdf', 'histogram'), loc='upper left')
# plt.show()


cdf_m = np.ma.masked_equal(cdf, 0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
cdf1 = np.ma.filled(cdf_m, 0).astype('uint8')

img2 = cdf1[img]
cv2.imshow('img2', img2)
cv2.waitKey(0)
hist1, bins1 = np.histogram(img2.flatten(), 256, [0, 256])

cdf2 = hist1.cumsum()

cdf_normalized = cdf2 * hist1.max() / cdf2.max()

plt.plot(cdf_normalized, color='b')
plt.hist(img2.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
plt.show()

运行结果如下:
在这里插入图片描述
另外一个重要的特性:尽管图像是暗的图像,当进行直方图均衡化后,我们可以得到得到与之前同样的效果的图像。那么,我们可以将该调整后的图像作为参考图像,然后使得源图像调整到参考图像的亮度。比如在人脸识别中,我们可以在训练人脸之前,使用直方图均衡化将所有图像调整到相同的光照条件。

掩码矩阵:

>>> import numpy as np
>>> import numpy.ma as ma
>>> x = np.array([1, 2, 3, -1, 5]) 如果我们希望-1被标记为无效 则可以:
>>> mx = ma.masked_array(x, mask=[0, 0, 0, 1, 0])
>>> 当计算平均值时,不会考虑无效
>>> mx.mean()
2.75 

Histograms Equalization in OpenCV

OpenCV中含有相应的函数可以进行直方图均衡化,cv2.equalizeHist(). 输入时灰度图,输出是进行直方图均衡化的图像。

下面是简单的例子:

img = cv2.imread('wiki.jpg', 0)
equ = cv2.equalizeHist(img)
res = np.hstack((img, equ))  # stacking images side-by-side
# cv2.imwrite('res.png', res)
cv2.imshow('res', res)
cv2.waitKey(0)

在这里插入图片描述

当图像的直方图分布于特定的区域内,那么直方图均衡化非常有效。但是,但图像具有较大的强度变化(直方图覆盖区域较大),均衡化就不是那么有效了。

CLAHE (Contrast Limited Adaptive Histogram Equalization)

从上面的直方图均衡化可以看出,它使得全局的对比度得以改善。但是在大多情况下,这并不好。比如,下面的图像以及均衡化处理后的图像:
在这里插入图片描述
从图中可以看出,图像的背景对比度得到增强。但是图中的人脸雕像确变得模糊,由于过亮,我们丢失了大部分的信息。造成这种结果的原因是,该图像的直方图覆盖的区域较大,见下面的直方图:
在这里插入图片描述
为了解决这个问题,我们使用adaptive histogram equalization. 这里,图像被分为很多的小块,opencv中默认的小块大小时8x8。在每一个小块使用直方图均衡化。那么每一个小块的直方图很可能覆盖很小的区域(除非有噪声)。为了克服噪声,采用对比度限制(contrast limiting)。如果任何一个直方图的bin超过设定的限制值(默认40),这些像素被截断,并均匀分布到其它的bin,然后在施加均衡化。最后借助双线性插值移除多余的边界。

下面的代码展示了CLAHE:

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

# plt.hist(img.ravel(), 256, [0, 256])
# plt.show()

# create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)

# cv2.imwrite('clahe_2.jpg',cl1)
cv2.imshow('cl1', cl1)
cv2.waitKey(0)

运行结果如下,尤其是雕像区域:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值