作者:Xiou
直方图是图像处理过程中的一种非常重要的分析工具。直方图从图像内部灰度级的角度对图像进行表述,包含十分丰富而重要的信息。从直方图的角度对图像进行处理,可以达到增强图像显示效果的目的。
1.直方图概述
从统计的角度讲,直方图是图像内灰度值的统计特性与图像灰度值之间的函数,直方图统计图像内各个灰度级出现的次数。从直方图的图形上观察,横坐标是图像中各像素点的灰度级,纵坐标是具有该灰度级(像素值)的像素个数。
一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图描述了图像中灰度的分布情况,能够很直观地展示出图像中各个灰度级所占的多少。
上图就是一个灰度图像的灰度直方图,横坐标代表的是像素的灰度值的范围[0,255],越接近0表示图像越暗,越接近255表示图像越亮。纵坐标代表的是每一个灰度值在图像所有像素中出现的次数。曲线越高,表示该像素值在图像中出现的次数越多。
2.绘制直方图
Python的模块matplotlib.pyplot中的hist()函数能够方便地绘制直方图,我们通常采用该函数直接绘制直方图。除此以外,OpenCV中的cv2.calcHist()函数能够计算统计直方图,还可以在此基础上绘制图像的直方图。下面分别讨论这两种方式。
calcHist,该函数能够同时计算多个图像、多个通道、不同灰度范围的灰度直方图。为了更为通用,该函数的参数有些复杂,具体函数声明如下:
calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
其中,参数
images表示图像矩阵数组,这些图像必须有相同大小和相同的深度;
channels表示要计算直方图的通道个数;
mask表示可选的掩码,不使用时可设为空,mask必须是一个8位的数组并且和images的数组大小相同,在进行直方图计算的时候只会统计该掩码不为0的对应像素;
histSize表示直方图每个维度的大小;
hist表示输出的直方图;
ranges表示直方图每个维度要统计的灰度级的范围;
accumulate表示累积标志,默认值为False。
OpenCV中用calHist函数得到直方图数据后就可以将其绘制出来了。
代码实例1:得到某图像的灰度直方图。
测试原图:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.jpg',0)
plt.hist(img.ravel(),256,[0,256]);
plt.show()
输出结果:
代码实例2:统计时使用cv2.calcHist()函数。绘制BGR直方图
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.jpg',1)
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])
plt.show()
输出结果:
代码实例3:直方图+mask
import numpy as np
import cv2
from matplotlib import pyplot as plt
plt.style.use("fivethirtyeight")
# 读取图片, 并转换成灰度图
img = cv2.imread("girl1.jpg", 0)
# 创建mask
mask = np.zeros(img.shape, np.uint8)
mask[280:1000, 420:1500] = 255
# 获取mask后的图像
masked_img = cv2.bitwise_and(img, img, mask=mask)
# 直方图
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
# 图片展示
f, ax = plt.subplots(2, 2, figsize=(12, 9))
ax[0, 0].imshow(img, 'gray')
ax[0, 0].set_title("original image")
ax[0, 1].imshow(mask, 'gray')
ax[0, 1].set_title("mask")
ax[1, 0].imshow(masked_img, 'gray')
ax[1, 0].set_title("masked image")
ax[1, 1].plot(hist_full)
ax[1, 1].plot(hist_mask)
ax[1, 1].set_title("original vs masked hist")
plt.show()
输出结果:
3.直方图均衡化
直方图均衡化是一种常见的增强图像对比度的方法,可以增强局部图像的对比度,在数据较为相似的图像中的作用更加明显。直方图均衡化处理的“中心思想”是:把原始图像的灰度直方图从比较集中的某个灰度区间,变成在全部灰度范围内的均匀分布。
直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改成“均匀”分布。
为什么要直方图均衡化?很多时候,图片看起来的效果不是那么清晰,这时可以对图像进行一些处理来扩大图像像素值显示的范围。例如,有些图像整体像素值偏低,图像中的一些特征不是很清晰,只是隐约看到一些轮廓痕迹,这时可以经过图像直方图均衡化之后,使图像看起来亮一些,也便于后续处理。
直方图均衡化是灰度变换的一个重要应用,它高效且易于实现,广泛应用于图像增强处理中。图像的像素灰度变化是随机的,直方图的图形高低不齐,直方图均衡化就是用一定的算法使直方图大致平和的方法。
直方图均衡化的算法主要包括两个步骤:
(1)计算累计直方图。
(2)对累计直方图进行区间转换在此基础上,再利用人眼视觉达到直方图均衡化的目的。
下面我们实现直方图均衡化来改善图像质量,通常有两种方法:
一种是不通过OpenCV函数;
另外一种是通过OpenCV函数equalizeHist(这种方式不懂原理也可以使用,只要把原图像输入即可得到均衡化的图像输出)。
equalizeHist函数对图像进行直方图均衡化(归一化图像亮度和增强图像对比度),声明如下:
equalizeHist(src[, dst])
代码实例:
import cv2
from matplotlib import pyplot as plt
plt.style.use("fivethirtyeight")
# 读取图片, 并转换成灰度图
img = cv2.imread("girl1.jpg", 0)
# 均衡化
img_equ = cv2.equalizeHist(img)
# 直方图
f, ax = plt.subplots(2, 2, figsize=(16, 16))
ax[0, 0].imshow(img, "gray")
ax[0, 0].set_title("before")
ax[0, 1].imshow(img_equ, "gray")
ax[0, 1].set_title("after")
ax[1, 0].hist(img.ravel(), 256)
ax[1, 1].hist(img_equ.ravel(), 256)
plt.show()
输出结果: