直方图是进行图像处理过程中的一种非常重要的工具。它是从图像内部灰度级的角度对图像进行表述。
直方图统计的是图像内各个灰度级出现的次数。
使用函数:matplotlib.pyplot.hist(X, BINS)
X 和 BINS 的参数如下:
X:数据源,必须是一维的。对于通常的二维图像来说,需要使用ravel()函数将图像处理为一维数据源。
BINS:表示灰度级的分组情况。
函数ravel()的作用是将二维数组降维成一维数组,举例如下:
假设存在一图像img,其灰度值分别为:
这里我们使用ravel()函数对img处理:
change = img.ravel()
可得到change为:
代码示例如下:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\Wxr\Desktop\lena1.jpg')
cv2.imshow('img', img)
grayscale = plt.hist(img.ravel(), 256)
plt.show(grayscale)
cv2.waitKey(0)
运行上述代码块后,得到如下图结果,其中左边是读入图像,右边是对应的直方图。
接下来我们考虑将灰度级划分为16个子集,即BINS值为16,代码块如下:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\Wxr\Desktop\lena1.jpg')
cv2.imshow('img', img)
grayscale = plt.hist(img.ravel(), 16)
plt.show(grayscale)
cv2.waitKey(0)
运行上述程序,得到直方图结果如下,可以看出,整个灰度级被划分为16个子集。
使用函数:hist = cv2.calcHist(image, channels, mask, histSize, ranges, accumulate)
该函数能统计各个灰度级的像素点个数。再利用plot()函数,便可将cv2.calcHist()的统计结果绘制成直方图。
函数中返回值及各参数含义如下:
hist:返回的统计直方图,是一个一维数组,数组内元素是各灰度级的像素个数。
image:读入图像,该参数需用“[ ]”括起来。
channels:通道编号,如果读入的图像是单通道灰度图像,则此参数值为[0];对于读入的彩色图像,其值可为[0]、[1]、[2]分别对应通道B、G、R。该参数需用“[ ]”括起来。
mask:掩模图像,当统计图像某一区域的直方图时,需要设置该值;当统计整幅图的直方图时,此值设为None。
histSize:BINS值,该参数需用“[ ]”括起来。
ranges:像素值范围,例如,8位灰度图的像素值范围是[0,255]。
accumulate:累计标识,默认为False。如果被设置为True,则直方图在开始计算时不会被清零,计算的是多个直方图的累积结果,用于对一组图像计算直方图。该参数允许从多个对象中计算单个直方图,或者实时更新直方图。该参数是可选的,一般情况下不需要设置。
代码示例如下:
import cv2
import numpy as np
img = cv2.imread(r'C:\Users\Wxr\Desktop\lena1.jpg')
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
print(type(hist))
print(hist.shape)
print(hist.size)
print(hist)
运行上述程序,得到结果如下:
从上图可以看出,函数cv2.calcHist()的返回值的数据类型为“ndarray”;该数据的shape为(256,1),说明其有256行1列;该数据的size为256,说明有256个元素,分别对应256个灰度级在图像内出现的次数;运行结果的下半部分是hist内的部分数据。
代码示例如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\Wxr\Desktop\lena1.jpg')
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
cv2.imshow('img', img)
plt.plot(hist, color='r')
plt.show()
2.1节我们介绍了cv2.calcHist()函数,其中参数mask用于标识是否使用掩模图像,当使用掩模图像获取直方图时,仅获取mask指定区域的直方图。
我们构造掩模图像时,通常先构造一个像素值都是0的二维数组,再将数组中指定区域的像素值设定为255,就得到了掩模图像。
代码示例如下:
import cv2
import numpy as np
mask = np.zeros([600, 600], np.uint8)
mask[200:400, 200:400] = 255
cv2.imshow('mask', mask)
cv2.waitKey()
运行上述代码,得到结果如下图所示:
该代码首先生成了一个600×600大小的黑色快,接着令行200-400、列200-400的区域为白色,由此得到掩模图像。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread(r'C:\Users\Wxr\Desktop\lena1.jpg', cv2.IMREAD_GRAYSCALE)
mask = np.zeros(img.shape, np.uint8)
mask[200:400, 200:400] = 255
histimg = cv2.calcHist([img], [0], None, [256], [0, 255])
histmaskimg = cv2.calcHist([img], [0], mask, [256], [0, 255])
image = cv2.add(img, np.zeros(np.shape(img), dtype=np.uint8), mask=mask)
plt.subplot(1, 2, 1)
plt.plot(histimg)
plt.subplot(1, 2, 2)
plt.plot(histmaskimg)
plt.show()
cv2.imshow("img", img)
cv2.imshow("image", image)
cv2.waitKey()
运行上述代码,得到结果如下图所示:
上图中,左边表示的是原图直方图,右图表示的是掩模处理后得到的图像直方图。
HE的中心思想是把原始图像的灰度直方图从比较集中给的某个灰度区间变成全部灰度范围内的均匀分布。
接下来我们用数学公式来描述HE过程,首先我们假设一幅图像中像素总数为N,图像的灰度级数是L,灰度空间是[0,L-1],我们用 n k n_{k} nk表示第k级灰度(第k个灰度级,像素值为k)在图像内的像素点个数,呢吗该图像中灰度级为 r k r_{k} rk(第k个灰度级)出现的概率为:
P ( r k ) = n k N ( k = 0 , 1 , … , L − 1 ) P(r_{k})=\frac{n_{k}}{N} (k=0,1,…,L-1) P(rk)=Nnk(k=0,1,…,L−1)
根据灰度级概率,对其进行均衡化处理的计算公式为:
s k = T ( r k ) = ( L − 1 ) ∑ j = 0 k P r ( r j ) = ( L − 1 ) ∑ j = 0 k n j / N s_{k}=T(r_{k})=(L-1) \sum_{j=0}^{k}P_{r}(r_{j})=(L-1) \sum_{j=0}^{k}n_{j}/N sk=T(rk)=(L−1)j=0∑kPr(rj)=(L−1)j=0∑knj/N
式中, ∑ j = 0 k P r ( r j ) \sum_{j=0}^{k}P_{r}(r_{j}) ∑j=0kPr(rj)表示累计概率,将该值与灰度级的最大值L-1相乘即得到均衡化后的新灰度级(像素值)。
我们使用下方函数对图像进行HE操作
dst = cv2.equalizeHist(src)
式中,src是8位单通道原始图像,dst是直方图均衡化处理后的结果。
示例代码如下:
import cv2
import matplotlib.pyplot as plt
image = cv2.imread(r'C:\Users\Wxr\Desktop\dzd.jpg', cv2.IMREAD_GRAYSCALE)
equ = cv2.equalizeHist(image)
plt.figure("1")
plt.hist(image.ravel(), 256)
plt.figure("2")
plt.hist(equ.ravel(), 256)
plt.show()
cv2.imshow('original', image)
cv2.imshow('result', equ)
cv2.waitKey()
由上图可看出,在HE之前,图像整体比较暗;均衡化之后,图像的亮度变得比较均衡,图像一些细节也得以观察到。
直方图均衡化使得图像色彩更加均衡,外观更清晰,也使图像更便于处理,它被广泛应用于低照度图像的增强,例如医学图像处理、夜间车牌识别和人脸识别等领域。
参考:李立宗:OpenCV轻松入门-面向Python