13. 图像直方图

一、什么是图像直方图

  从统计的角度讲,直方图是图像内灰度值的统计特性与图像灰度值之间的函数,直方图统计图像内各个灰度级出现的次数。从直方图的图像上观察,横坐标 是图像中各像素点的 灰度级纵坐标 是具有该灰度级(像素值)的 像素个数

  在实际处理中,灰度图形直方图的 x 轴区间一般是 [0, 255],对应的是 8 位位图的 256 个灰度级,y 轴对应的是具有相应灰度级的像素点的个数。有时为了方便表示,也会采用归一化直方图。在归一化直方图总,x 轴仍然表示 灰度级,y 轴不在表示灰度级出现的次数,而是灰度级出现的 频率

\[灰度值出现的频率 = 灰度级出现的次数 / 总像素数 \]

在归一化直方图中,各个灰度级出现的频率之和为 1。

  在 OpenCV 的官网上,提出了三个概念:DIMSRINSRANGE

  • DIMS:表示在绘制直方图时,收集的参数的数量。一般情况下,直方图中收集的数据只有一种,就是灰度级。因此,该值为 1
  • RANGE:表示要统计的灰度级范围,一般为 [0, 255]0 对应的是黑色,255 对应的是白色。
  • BINS:参数子集的数目。在处理数据的过程中,有时需要将众多的数据分为若干分组,再进行分析。

  我们可以在终端中使用 pip 安装 OpenCV 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载

pip install opencv-python -i https://mirrors.aliyun.com/pypi/simple

  国内常用的 pip 下载源列表:

二、绘制直方图

  我们可以使用 matplotlib.pyplot 模块中的 hist() 函数绘制直方图。该函数的作用是根据数据源和灰度值分组绘制直方图。我们可以们可以在终端中使用 pip 安装 matplotlib 模块。

pip install matplotlib -i https://mirrors.aliyun.com/pypi/simple

  其函数定义如下:

matplotlib.pyplot.hist(
    x: ArrayLike | Sequence[ArrayLike],
    bins: int | Sequence[float] | str | None = None,
    range: tuple[float, float] | None = None,
    density: bool = False,
    weights: ArrayLike | None = None,
    cumulative: bool | float = False,
    bottom: ArrayLike | float | None = None,
    histtype: Literal["bar", "barstacked", "step", "stepfilled"] = "bar",
    align: Literal["left", "mid", "right"] = "mid",
    orientation: Literal["vertical", "horizontal"] = "vertical",
    rwidth: float | None = None,
    log: bool = False,
    color: ColorType | Sequence[ColorType] | None = None,
    label: str | Sequence[str] | None = None,
    stacked: bool = False,
    *,
    data=None,
    **kwargs,
) -> tuple[
    np.ndarray | list[np.ndarray],
    np.ndarray,
    BarContainer | Polygon | list[BarContainer | Polygon],
]

  其中,参数 X数据源,必须是一维的。图像通常是二维的,需要使用 ravel() 函数将图像处理为一维数据源以后,在作为参数使用。参数 BINSBINS 的具体值,表示灰度级的分组情况。参数 rangex 轴的范围

import sys
import cv2
import matplotlib.pyplot as plt

if __name__ == '__main__':
    image = cv2.imread("assets/images/1.jpg")
    if image is None:
        print("加载图片失败")
        sys.exit(0)

    plt.hist(image.ravel(), 256, (0, 255))
    plt.show()

  在 OpenCV 中,我们可以使用 cv2.calcHist() 函数 计算图像的统计直方图,该函数能统计各个灰度级的像素点个数。

cv2.calcHist(images: _typing.Sequence[cv2.typing.MatLike], channels: _typing.Sequence[int], mask: cv2.typing.MatLike | None, histSize: _typing.Sequence[int], ranges: _typing.Sequence[float], hist: cv2.typing.MatLike | None = ..., accumulate: bool = ...) -> cv2.typing.MatLike: ...

  其中,参数 images原始图像集合。参数 channels 是指定 通道编号。参数 mask 是 掩模图像,用于指定需要计算统计直方图的区域。当统计整幅图像的直方图,将该值设置为 None。当统计图像某一部分的直方图时,mask 值为非空。参数 histSize对应通道的 BINS 值,参数 range像素值范围。参数 accumulate累计标识,默认为 False。如果被设置为 True,则直方图在开始计算是不会被清零,计算的是多个直方图的累积结果,用于对一组图像计算直方图。

import sys
import cv2
import matplotlib.pyplot as plt

if __name__ == '__main__':
    image = cv2.imread("assets/images/1.jpg")
    if image is None:
        print("加载图片失败")
        sys.exit(0)

    blue_hist = cv2.calcHist([image], [0], None, [256], [0, 255])
    green_hist = cv2.calcHist([image], [1], None, [256], [0, 255])
    red_hist = cv2.calcHist([image], [2], None, [256], [0, 255])

    plt.plot(blue_hist, color='blue', label='blue')
    plt.plot(green_hist, color='green', label='green')
    plt.plot(red_hist, color='red', label='red')
    plt.legend()
    plt.show()

三、使用掩膜的直方图

  在 cv2.calcHiST() 函数中,参数 mask 用于标识是否使用掩模图像。当使用掩模图像获取直方图时,仅获取掩膜参数 mask 指定区域的直方图。为了方便理解,我们可以将掩膜图像看作一块玻璃板,玻璃板上的白色区域是可以透明的,黑色区域是不透明的。掩膜运算就是将该玻璃板覆盖在原始图像上,透过玻璃板显示的部分就是掩膜运算的结果图像。

import sys
import cv2
import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':
    image = cv2.imread("assets/images/1.jpg")
    if image is None:
        print("加载图片失败")
        sys.exit(0)

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    mask = np.zeros(gray.shape, dtype=np.uint8)                                 # 创建一个和灰度图像相同大小的掩码图像
    mask[200: 400, 200: 400] = 255                                              # 设置想要统计直方图的区域

    mask_hist = cv2.calcHist([image], [0], mask, [256], [0, 255])
    gray_hist = cv2.calcHist([image], [0], None, [256], [0, 255])

    plt.plot(mask_hist, label='mask_hist')
    plt.plot(gray_hist, label='gray_hist')
    plt.legend()
    plt.show()

四、直方图均衡化

  如果一幅图像拥有全部可能的灰度值,并且像素值的灰度均匀分布,那么这幅图像就具有高对比度和多变的灰度色调,灰度级丰富且覆盖范围较大。在外观上,这样的图像有更丰富的色彩,不会过暗或者过亮。直方图均衡化的主要目的是将原始图像的灰度级均匀地映射到整个灰度级范围内,得到一个灰度级分布均匀的图像。这种均衡化,即实现了灰度值统计上的概率均衡,也实现了人类视觉系统上的视觉均衡。

  直方图均衡化的算法主要包括两个步骤:

  1. 计算累计直方图。
  2. 对累计直方图进行区间转换。

  在此基础上,再利用人眼视觉达到直方图均衡化的目的。

  在 OpenCV 中,我们可以使用 cv2.equalizeHist() 函数实现直方图均衡化,该函数的定义如下:

cv2.equalizeHist(src: cv2.typing.MatLike, dst: cv2.typing.MatLike | None = ...) -> cv2.typing.MatLike: ...

  其中,参数 src8 位单通道原始图像。参数 dst直方图均衡化处理后的图像

import sys
import cv2
import numpy as np

if __name__ == '__main__':
    src = cv2.imread("assets/images/10.png")
    if src is None:
        print("加载图片失败")
        sys.exit(0)

    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    dst = cv2.equalizeHist(gray)
    cv2.imshow("image", np.hstack((gray, dst)))                                  # 连接两个图像显示

    cv2.waitKey(0)
    cv2.destroyAllWindows()
    sys.exit(0)
posted @ 2026-01-03 23:56  星光映梦  阅读(12)  评论(0)    收藏  举报