「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」
灰度直方图
我们已经在《直方图基本概念》中介绍了直方图的相关概念,在本文中,我们将学习OpenCV中如何绘制图像的直方图。
OpenCV 中使用 cv2.calcHist() 函数来计算一个或多个数组的直方图。因此,该函数可以应用于单通道图像和多通道图像。
我们首先了解如何计算灰度图像的直方图:
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
其中,函数参数如下:
| 参数 | 解释 |
|---|---|
| images | 以列表形式提供的 uint8 或 float32 类型的源图像,如 [image_gray] |
| channels | 需要计算直方图的通道的索引,以列表形式提供(例如,灰度图像中使用 [0],或多通道图像中 [0] , [1] , [2] 分别计算第一个、第二个或第三个通道的直方图) |
| mask | 蒙版图像,用于计算蒙版定义的图像特定区域的直方图,如果此参数等于 None,则将在没有蒙版的情况下使用完整图像计算直方图 |
| histSize | 它表示作为列表提供的 bin 数量,如 [256] |
| range | 要测量的强度值的范围,如 [0,256] |
不带蒙版的灰度直方图
计算全灰度图像(无蒙版)直方图的代码如下:
image = cv2.imread('example.png')
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
hist = cv2.calcHist([gray_image], [0], None, [256], [0, 256])
show_img_with_matplotlib(cv2.cvtColor(gray_image, cv2.COLOR_GRAY2BGR), "gray", 1)
show_hist_with_matplotlib_gray(hist, "grayscale histogram", 4, 'm')
在上述代码中, hist 是一个形状为 (256, 1) 的数组,每个值( bin )对应于具有相应色调值的像素数。
灰度图像的亮度可以定义为图像所有像素的平均强度,由以下公式给出:
这里, 是图像特定像素的色调值。因此,如果图像的平均色调较高,意味着图像的大多数像素将非常接近白色;相反,如果图像的平均色调较低,意味着图像的大多数像素将非常接近黑色。
我们可以对灰度图像执行图像加法和减法,以便修改图像中每个像素的灰度强度,以观察如何更改图像的亮度以及直方图如何变化:
M = np.ones(gray_image.shape, dtype="uint8") * 30
# 每个灰度值加 30
added_image = cv2.add(gray_image, M)
# 计算结果图像的直方图
hist_added_image = cv2.calcHist([added_image], [0], None, [256], [0, 256])
# 每个灰度值减 30
subtracted_image = cv2.subtract(gray_image, M)
# 计算结果图像的直方图
hist_subtracted_image = cv2.calcHist([subtracted_image], [0], None, [256], [0, 256])
# 可视化
show_img_with_matplotlib(cv2.cvtColor(added_image, cv2.COLOR_GRAY2BGR), "gray lighter", 2)
show_hist_with_matplotlib_gray(hist_added_image, "grayscale histogram", 5, 'm')
show_img_with_matplotlib(cv2.cvtColor(subtracted_image, cv2.COLOR_GRAY2BGR), "gray darker", 3)
show_hist_with_matplotlib_gray(hist_subtracted_image, "grayscale histogram", 6, 'm')
中间的灰度图像对应于原始图像的每个像素加 35 的图像,从而产生更亮的图像,图像的直方图会向右偏移,因为没有强度在 [0-35] 范围内的像素;而右侧的灰度图像对应于原始图像的每个像素都减去 35 的图像,从而导致图像更暗,图像的直方图会向左偏移,因为没有强度在 [220-255] 范围内的像素。
带有蒙版的灰度直方图
如果需要应用蒙版,需要首先创建一个蒙版。
# 加载并修改图像
image = cv2.imread('example.png')
height, width = image.shape[:2]
# 添加了一些具有 0 和 255 灰度强度的黑色和白色小圆圈
for i in range(0, width, 20):
cv2.circle(image, (i, 390), 5, (0, 0, 0), -1)
cv2.circle(image, (i, 10), 5, (255, 255, 255), -1)
# 将图像转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 创建蒙版
mask = np.zeros(gray_image.shape[:2], np.uint8)
mask[30:190, 30:190] = 255
蒙版由与加载图像尺寸相同的黑色图像组成,白色图像对应于要计算直方图的区域。 然后使用所创建的蒙版来计算直方图,调用 cv2.calcHist() 函数并传递创建的蒙版:
hist_mask = cv2.calcHist([gray_image], [0], mask, [256], [0, 256])
# 可视化
show_img_with_matplotlib(cv2.cvtColor(gray_image, cv2.COLOR_GRAY2BGR), "gray", 1)
show_hist_with_matplotlib_gray(hist, "grayscale histogram", 2, 'm')
show_img_with_matplotlib(cv2.cvtColor(masked_img, cv2.COLOR_GRAY2BGR), "masked gray image", 3)
show_hist_with_matplotlib_gray(hist_mask, "grayscale masked histogram", 4, 'm')
如上图所示,我们对图像进行了修改,我们在其中分别添加了一些黑色和白色的小圆圈,这导致直方图在 bins = 0 和 255 中有较大的值,如第一个直方图所示。但是,添加的这些修改不会出现在蒙版图像直方图中,因为应用了蒙版,因此在计算直方图时没有将它们考虑在内。