18 直方图均衡化
18.1 什么是直方图
直方图是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的 bin 当中。其中, bin 为直方图中经常用到的一个概念,可以译为 “直条” 或 “组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。
直方图:反映图像像素分布的统计图,横坐标就是图像像素的取值,纵坐标是该像素的个数。也就是对一张图像中不同像素值的像素个数的统计。
增加对比度:黑的更黑,白的更白。
18.2 绘制直方图
就是以像素值为横坐标,像素值的个数为纵坐标绘制一个统计图。
hist=cv2.calcHist(images, channels, mask, histSize, ranges)
images
:输入图像列表,可以是一幅或多幅图像(通常是灰度图像或者彩色图像的各个通道)。
channels
:一个包含整数的列表,指示在每个图像上计算直方图的通道编号。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像的话,传入的参数可以是 [0],[1],[2] 它们分别对应着通道 B,G,R。
mask
(可选):一个与输入图像尺寸相同的二值掩模图像,其中非零元素标记了参与直方图计算的区域,None为全部计算。
histSize
:一个整数列表,也就是直方图的区间个数(BIN 的数目)。用中括号括起来,例如:[256]。
ranges
:每维数据的取值范围,它是一个二维列表,每一维对应一个通道的最小值和最大值,例如对灰度图像可能是[0, 256]
。
返回值hist 是一个长度为255的数组,数组中的每个值表示图像中对应灰度等级的像素计数
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)
获取直方图的最小值、最大值及其对应最小值的位置索引、最大值的位置索引
cv2.line(img, pt1, pt2, color, thickness)
img:原始图像,即要在上面画线的numpy数组(一般为uint8类型)。
pt1 和 pt2:分别为线段的起点和终点坐标,它们都是元组类型,例如 (x1, y1)
和 (x2, y2)
分别代表线段两端的横纵坐标。
color:线段的颜色,通常是一个包含三个元素的元组 (B, G, R)
表示BGR色彩空间的像素值,也可以是灰度图像的一个整数值。
thickness:线段的宽度,默认值是1,如果设置为负数,则线宽会被填充。
import cv2 as cv
import numpy as np
def Draw(img, color):
# 计算直方图,将img放入列表中
hist = cv.calcHist([img], [0], None, [256], [0, 256])
minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(hist)
hist_img = np.zeros((256, 256, 3), dtype=np.uint8)
hpt = int(0.9 * 256)
# 处理maxVal为0的情况
if maxVal == 0:
maxVal = 1 # 避免除以0
for h in range(256):
intensity = int(hist[h].item() / maxVal * hpt)
# 修正坐标和线宽
cv.line(hist_img, (h, 255), (h, 255 - intensity), color, 1)
cv.imshow('Original Image', img)
cv.imshow('Histogram', hist_img)
cv.waitKey(0)
img = cv.imread('images/ikun2.jpeg')
color = (100, 100, 255)
Draw(img, color)
注意OpenCV的
cv.calcHist
函数要求第一个参数是图像列表。原代码直接传入img
会导致错误,应改为[img]
。
18.3 直方图均衡化
一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。
如果一幅图像整体很亮,那所有的像素值的取值个数应该都会很高,所以应该把它的直方图做一个横向拉伸,就可以扩大图像像素值的分布范围,提高图像的对比度
通俗的讲,就是遍历图像的像素统计出灰度值的个数、比例与累计比例,并重新映射到0-255范围(也可以是其他范围)内,其实从观感上就可以发现,下面两幅图中前面那幅图对比度不高,偏灰白。
直方图均衡化作用:
增强对比度
提高图像质量
可以看到均衡化后图片的亮度和对比度效果明显好于原图。
18.3.1 自适应直方图均衡化
自适应直方图均衡化(Adaptive Histogram Equalization, AHE),通过调整图像像素值的分布,使得图像的对比度和亮度得到改善。
具体过程如下所示:
假设有一个3*3的图像,其灰度图的像素值如上图所示,现在我们要对其进行直方图均衡化,首先就是统计其每个像素值的个数、比例以及其累计比例。如下图所示。
接下来我们就要进行计算,就是将要缩放的范围(通常是缩放到0-255,所以就是255-0)乘以累计比例,得到新的像素值,并将新的像素值放到对应的位置上,比如像素值为50的像素点,将其累计比例乘以255,也就是0.33乘以255得到84.15,取整后得到84,并将84放在原图像中像素值为50的地方,像素值为100、210、255的计算过程类似,最终会得到如下图所示的结果,这样就完成了最基本的直方图均衡化的过程。
dst = cv.equalizeHist(imgGray)
imgGray为需要直方图均衡化的灰度图,返回值为处理后的图像
from Draw import draw
img1 = cv.imread('images/qilongzhu.jpg')
gray = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)
dst_gray = draw(gray,(100,100,255))
equalize = cv.equalizeHist(gray)
dst_equalize = draw(equalize,(100,100,255))
cv.imshow('gray', gray)
cv.imshow('gray_hist', dst_gray)
cv.imshow("dst_equalize",dst_equalize)
cv.waitKey(0)
cv.destroyAllWindows()
注意要创建一个叫Draw.py的文件
# 新建 Draw.py 文件并包含以下内容
import cv2 as cv
import numpy as np
def draw(img, color):
hist = cv.calcHist([img], [0], None, [256], [0, 256])
minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(hist)
hist_img = np.zeros((256, 256, 3), dtype=np.uint8)
hpt = int(0.9 * 256)
if maxVal == 0:
maxVal = 1
for h in range(256):
intensity = int(hist[h].item() / maxVal * hpt)
cv.line(hist_img, (h, 255), (h, 255 - intensity), color, 1)
return hist_img # 添加返回语句
![]() | ![]() |
该方法适用于图像的灰度分布不均匀,且灰度分布集中在更窄的范围,图像的细节不够清晰且对比度较低的情况,然而,传统的直方图均衡化方法会引入噪声,并导致图像中出现过度增强的区域。这是因为直方图均衡化方法没有考虑到图像的局部特征和全局对比度的差异。如下图:
18.3.2 对比度受限的自适应直方图均衡化
很明显,因为全局调整亮度和对比度的原因,脸部太亮,大部分细节都丢失了。自适应均衡化就是用来解决这一问题的:它在每一个小区域内(默认8×8)进行直方图均衡化。当然,如果有噪点的话,噪点会被放大,需要对小区域内的对比度进行了限制,所以这个算法全称叫:对比度受限的自适应直方图均衡化(Contrast Limited Adaptive Histogram Equalization, CLAHE)。
其主要步骤为:
-
图像分块(Tiling):
-
图像首先被划分为多个不重叠的小块(tiles)。这样做的目的是因为在全局直方图均衡化中,单一的直方图无法反映图像各个局部区域的差异性。通过局部处理,AHE能够更好地适应图像内部的不同光照和对比度特性。(tiles 的 大小默认是 8x8)
-
-
计算子区域直方图:
-
对于每个小块,独立计算其内部像素的灰度直方图。直方图反映了该区域内像素值的分布情况。
-
-
子区域直方图均衡化:
-
对每个小块的直方图执行直方图均衡化操作。这涉及重新分配像素值,以便在整个小块内更均匀地分布。均衡化过程会增加低频像素的数量,减少高频像素的数量,从而提高整个小块的对比度。
-
-
对比度限制(Contrast Limiting):
-
如果有噪声的话,噪声会被放大。为了防止过大的对比度增强导致噪声放大,出现了限制对比度自适应直方图均衡化(CLAHE)。CLAHE会在直方图均衡化过程中引入一个对比度限制参数。当某一小块的直方图在均衡化后出现极端值时,会对直方图进行平滑处理(使用线性或非线性的钳制函数),确保对比度增强在一个合理的范围内。
-
-
重采样和邻域像素融合:
-
由于小块之间是不重叠的,直接拼接经过均衡化处理的小块会产生明显的边界效应。因此,在CLAHE中通常采用重采样技术来消除这种效应,比如通过双线性插值将相邻小块的均衡化结果进行平滑过渡,使最终图像看起来更为自然和平滑。
-
-
合成输出图像:
-
将所有小块均衡化后的结果整合在一起,得到最终的自适应直方图均衡化后的图像。
-
clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)
clipLimit(可选):对比度限制参数,用于控制直方图均衡化过程中对比度增强的程度。如果设置一个大于1的值(如2.0或4.0),CLAHE会限制对比度增强的最大程度,避免过度放大噪声。如果不设置,OpenCV会使用一个默认值。
tileGridSize(可选):图像分块的大小,通常是一个包含两个整数的元组,如
(8, 8)
,表示将图像划分成8x8的小块进行独立的直方图均衡化处理。分块大小的选择会影响到CLAHE的效果以及处理速度。
创建CLAHE对象后,可以使用 .apply()
方法对图像进行CLAHE处理:
img=clahe.apply(image)
image:要均衡化的图像。 img均衡后的图像
from Draw import draw
img1 = cv.imread('images/qilongzhu.jpg')
gray = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)
dst_gray = draw(gray,(100,100,255))
equalize = cv.equalizeHist(gray)
dst_equalize = draw(equalize,(100,100,255))
#对比度受限制的直方图均匀化
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
img_clahe = clahe.apply(gray)
dst_clahe = draw(img_clahe,(100,255,255))
cv.imshow("dst_clahe",dst_clahe)
cv.imshow("img_clahe",img_clahe)
# cv.imshow('gray', gray)
# cv.imshow('gray_hist', dst_gray)
cv.imshow("dst_equalize",dst_equalize)
cv.waitKey(0)
cv.destroyAllWindows()