使用Python,OpenCV计算图像直方图(cv2.calcHist)

Posted 程序媛一枚~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Python,OpenCV计算图像直方图(cv2.calcHist)相关的知识,希望对你有一定的参考价值。

这篇博客将介绍如何使用Python,OpenCV和cv2.calcHist 计算图像直方图。

直方图算机视觉中应用广泛。

  1. 使用灰度直方图进行阈值处理;
  2. 使用直方图进行白平衡;
  3. 使用颜色直方图来跟踪图像中的对象,例如使用 CamShift 算法;
  4. 使用颜色直方图作为特征——包括多维的颜色直方图;
  5. 使用图像梯度的直方图来形成 HOG 和 SIFT 描述符;
  6. 在图像搜索引擎和机器学习中使用的极受欢迎的视觉词袋表示也是直方图!

而且很有可能,我敢肯定这不是您第一次在研究中遇到直方图。

为什么直方图如此有用?
因为直方图捕获了一组数据的频率分布。事实证明,检查这些频率分布是构建简单图像处理技术……以及非常强大的机器学习算法的一种非常好的方式。

1. 效果图

原图BGR图:

颜色不对,因为OpenCV读取图片默认Numpy是BGR通道形式,而matplotlib期待RGB通道的图像,所以先转换在展示~
在这里插入图片描述

原图RGB图 VS 灰度图效果如下:
在这里插入图片描述

灰度图 VS 灰度直方图 VS 归一化灰度直方图效果图如下:

归一化的意义可以保证:同一图像等比例缩放后,归一化直方图也相同。

从直方图中可以看出:落在(0~30)像素范围的像素较多,说明图像中包含有“黑色”像素。
在这里插入图片描述
BGR-扁平化的彩色直方图如下:

在这里插入图片描述
2维3种组合的效果图如下:,从图中可以看到 G and R的峰值更多一些~,查看原始图确实红色和绿色更多一些。
在这里插入图片描述

原图 VS 蒙版区域效果图如下:
在这里插入图片描述
蒙版区域直方图如下,可以看到绿色、红色像素峰值多一些
在这里插入图片描述

2. 原理

2.1 什么是图像直方图?

直方图表示图像中像素强度(无论是彩色还是灰度)的分布。它可以被可视化为一个图形,给出了强度(像素值)分布的高级直觉。

假设一个 RGB 颜色空间,所有像素值将在 0 到 255 的范围内。在绘制直方图时,x 轴充当“bins”。如果用 256 构建一个直方图
bins,然后有效地计算每个像素值出现的次数。

相反,如果只使用 2(等距)bins,然后计算像素在 [0, 128] 或 [128, 255] 范围内的次数。然后将合并到 x 轴值的像素数绘制在 y 轴上。

通过简单地检查图像的直方图,可以大致了解对比度、亮度和强度分布。

2.2 计算直方图

cv2.calcHist(images, channels, mask, histSize, ranges)

  • images 要计算直方图的原始图像
  • channels 通道,[0]:灰度直方图,[0,1,2]BGR彩色直方图
  • mask 要为某个蒙版像素计算直方图,没有可设置为None
  • histSize x轴要分的bins个数[32,32,32],每个通道分为32个区间
  • range 可能的像素范围[0,256] 因为calHist不包含后者

2.3 可视化蒙版区域

  1. 构建和原图相同的黑色像素图
  2. 绘制一个矩形白色
  3. 原图与蒙版图使用按位与来可视化图像的蒙版区域

3. 源码

# 为单通道灰度图计算直方图
# USAGE
# python all_histogram.py

# 导入必要的包
from matplotlib import pyplot as plt
import argparse
import cv2
import numpy as np


def plot_histogram(image, title, mask=None):
    # 拆分图像为BGR通道,初始化每个通道的名称
    chans = cv2.split(image)
    colors = ("b", "g", "r")
    plt.figure()
    plt.title(title)
    plt.xlabel("Bins")
    plt.ylabel("# of Pixels")

    # 遍历图像通道
    for (chan, color) in zip(chans, colors):
        # 为当前通道计算直方图,并展示
        hist = cv2.calcHist([chan], [0], mask, [256], [0, 256])
        plt.plot(hist, color=color)
        plt.xlim([0, 256])


# 构建命令行参数及解析
# --image 输入图像
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=False, default="ml4.jpg",
                help="path to the image")
args = vars(ap.parse_args())

# 加载图像,并转换灰度图
image = cv2.imread(args["image"])
plt.figure()
plt.title("BGR Origin Image")
plt.axis("off")
plt.imshow(image)

plt.figure()
plt.title("RGB Origin Image")
plt.axis("off")
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 计算灰度直方图
hist = cv2.calcHist([image], [0], None, [256], [0, 256])

# 计算图像直方图后,在屏幕上显示灰度图像并绘制未归一化的图像直方图
# Numpy读取图片后以BGR通道形式,matplotlib期待RGB通道的图像,所以先转换在展示~
plt.figure()
plt.title("Gray Origin Image")
plt.axis("off")
plt.imshow(cv2.cvtColor(image, cv2.COLOR_GRAY2RGB))

# 非标准化直方图计算分布的原始频率。考虑计算一个包中不同颜色的 M&M 的数量,最终可得到每种颜色的整数计数,/总数得到比率。
# 归一化比率可以很方便的解决同一个图像只是尺寸等比例缩放但直方图差距很大的问题。
# 绘制直方图
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.plot(hist)
plt.xlim([0, 256])

# 归一化直方图
hist /= hist.sum()

# 展示归一化的直方图
plt.figure()
plt.title("Grayscale Histogram (Normalized)")
plt.xlabel("Bins")
plt.ylabel("% of Pixels")
plt.plot(hist)
plt.xlim([0, 256])
plt.show()

# 分离图像的3通道 初始化通道名
image = cv2.imread(args["image"])
chans = cv2.split(image)
colors = ("b", "g", "r")  # 初始化通道名称
# 初始化matplotlib图
plt.figure()
plt.title("'Flattened' Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")

# 遍历图像通道
for (chan, color) in zip(chans, colors):
    # 为当前通道绘制直方图,并展示
    hist = cv2.calcHist([chan], [0], None, [256], [0, 256])
    # hist /= hist.sum()
    plt.plot(hist, color=color)
    plt.xlim([0, 256])

# 加载图像并绘制直方图
image = cv2.imread("ml0.jpg")
plot_histogram(image, "Histogram for Original Image")

# 为图像构建一个mask,蒙版将是黑色像素忽略,只关注白色像素
mask = np.zeros(image.shape[:2], dtype="uint8")
cv2.rectangle(mask, (260, 290), (490, 520), 255, -1)

# 展示蒙版区域
masked = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("Applying the Mask", masked)

# 为蒙版图像计算直方图,并展示(在直方图计算中只考虑原始图像中属于掩码区域的像素。)
plot_histogram(image, "Histogram for Masked Image", mask=mask)

# 展示结果
plt.show()

参考

以上是关于使用Python,OpenCV计算图像直方图(cv2.calcHist)的主要内容,如果未能解决你的问题,请参考以下文章

Opencv 图像处理:图像通道直方图与色彩空间

OpenCV之图像直方图比较

在python3下使用OpenCV 抓取摄像头图像并实时显示3色直方图

opencv-python图像处理 ---直方图与傅里叶变换逆变换

OpenCV Python equalizeHist 彩色图像

opencv直方图拉伸