OpenCV直方图基本概念

1,247 阅读3分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战

前言

直方图是一种强大的技术,可以用于更好地理解图像内容。例如,许多相机在拍照时会实时显示正在捕获的场景的直方图,以调整相机拍摄的一些参数(例如曝光时间、亮度或对比度等)。在本文中,我们将学习直方图的相关概念。

直方图简介

图像直方图是一种反映图像色调分布的直方图,其绘制每个色调值的像素数。每个色调值的像素数也称为频率( frequency )。因此,强度值在 [0, K-1] 范围内的灰度图像的直方图将恰好包含 K 个矩形。例如,在 8 位灰度图像的情况下,K = 256 (28=2562^8 = 256),因此,强度值在 [0, 255] 范围内。直方图的每个矩形的高度定义为:

h(i)=number of pixels with intensity i,(i[0,255])h(i) = \text{number of pixels with intensity }i,(i\in[0, 255])

例如,h(80)h(80) 即为= 强度为 80 的像素数。

为了更好的理解直方图,我们构建一个由 7 个不同的灰度级方块组成的图形。灰度值分别为: 30 、 60 、 90 、 120 、 150 、 180 和 210 。

def build_sample_image():
    # 定义不同的灰度值: 60, 90, 120, ..., 210
    tones = np.arange(start=60, stop=240, step=30)
    # 使用灰度值30初始化第一个60x60方块
    result = np.ones((60, 60, 3), dtype="uint8") * 30
    # 连接构建的所有灰度方块
    for tone in tones:
        img = np.ones((60, 60, 3), dtype="uint8") * tone
        result = np.concatenate((result, img), axis=1)

    return result

def build_sample_image_2():
    # 翻转构建的灰度图像
    img = np.fliplr(build_sample_image())
    return img

接下来,构建直方图并进行可视化:

def show_img_with_matplotlib(color_img, title, pos):
    img_RGB = color_img[:, :, ::-1]

    ax = plt.subplot(2, 2, pos)
    plt.imshow(img_RGB)
    plt.title(title)
    plt.axis('off')

def show_hist_with_matplotlib_gray(hist, title, pos, color):
    ax = plt.subplot(2, 2, pos)
    plt.title(title)
    plt.xlabel("bins")
    plt.ylabel("number of pixels")
    plt.xlim([0, 256])
    plt.plot(hist, color=color)

plt.figure(figsize=(14, 10))
plt.suptitle("Grayscale histograms introduction", fontsize=14, fontweight='bold')

# 构建图像并转换为灰度图像
image = build_sample_image()
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image_2 = build_sample_image_2()
gray_image_2 = cv2.cvtColor(image_2, cv2.COLOR_BGR2GRAY)

# 构建直方图
hist = cv2.calcHist([gray_image], [0], None, [256], [0, 256])
hist_2 = cv2.calcHist([gray_image_2], [0], None, [256], [0, 256])

# 绘制灰度图像及直方图
show_img_with_matplotlib(cv2.cvtColor(gray_image, cv2.COLOR_GRAY2BGR), "image with 60x60 regions of different tones of gray", 1)
show_hist_with_matplotlib_gray(hist, "grayscale histogram", 2, 'm')
show_img_with_matplotlib(cv2.cvtColor(gray_image_2, cv2.COLOR_GRAY2BGR), "image with 60x60 regions of different tones of gray", 3)
show_hist_with_matplotlib_gray(hist_2, "grayscale histogram", 4, 'm')

plt.show()

直方图(下图中右侧图形)显示图像中每个色调值出现的次数(频率),由于每个方块区域的大小为 60 x 60 = 3600 像素,因此上述所有灰度值的频率都为 3,600,其他值则为 0:

直方图简介

如上图所示,直方图仅显示统计信息,而不显示像素的位置。也可以加载真实照片,并绘制直方图,只需要修改 build_sample_image() 函数:

def build_sample_image():
    image = cv2.imread('example.png')
    return image

直方图简介

直方图相关术语

上例中我们已经看到了构建直方图的简单示例,以下是一些与直方图相关的术语:

术语介绍
bins直方图显示了每个色调值(范围从 0 到 255)的像素数,每一个色调值都称为一个 bin。可以根据需要选择 bin 的数量,常见的值包括 8 、 16 、 32 、 64 、 128 、 256,OpenCV 使用 histSize 来表示 bins
range要测量的色调值的范围,通常为 [0,255] ,以对应所有色调值