OpenCV 图像直方图

OpenCV 图像直方图 教程(中文)重点讲解 OpenCV 中用于计算和可视化图像直方图的核心功能,主要基于 imgproc 模块的 calcHist 函数。直方图用于分析图像的灰度或颜色分布,广泛应用于图像增强、分割和特征提取。本教程涵盖灰度直方图、彩色直方图、均衡化及直方图比较,提供清晰的 Python 代码示例、解释和注意事项,适合初学者快速上手。假设你已安装 OpenCV(opencv-python)和 Matplotlib(用于可视化)。


一、图像直方图概述

  • 直方图:统计图像中像素值的分布,显示每个灰度值(或颜色通道值)的像素数量。
  • 应用场景
  • 图像分析:了解亮度或颜色分布。
  • 图像增强:通过直方图均衡化改善对比度。
  • 图像比较:通过直方图距离判断图像相似性。
  • 关键函数
  • cv2.calcHist:计算直方图。
  • cv2.equalizeHist:直方图均衡化,增强对比度。
  • cv2.compareHist:比较两个直方图的相似性。
  • 输入要求
  • 灰度图像(单通道,uint8)或彩色图像(BGR,uint8)。
  • 直方图均衡化仅适用于灰度图像。

二、核心直方图功能与代码示例

以下按功能分类,逐一讲解并提供 Python 示例代码。

2.1 计算和可视化灰度直方图

cv2.calcHist 计算图像的灰度值分布,Matplotlib 用于可视化。

示例:灰度直方图

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

# 读取图像
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)  # 灰度模式
if img is None:
    print("错误:无法加载图像")
    exit()

# 计算直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
# 参数解释:
# [img]:输入图像
# [0]:通道(灰度为 0)
# None:无掩码
# [256]:直方图 bin 数
# [0, 256]:像素值范围

# 可视化直方图
plt.figure(figsize=(8, 5))
plt.plot(hist, color='black')
plt.title('灰度直方图')
plt.xlabel('像素值')
plt.ylabel('像素数量')
plt.grid(True)
plt.show()

# 显示图像
cv2.imshow('灰度图像', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

说明

  • calcHist 返回一个形状为 (256, 1) 的数组,表示 0-255 灰度值的像素计数。
  • Matplotlib 的 plot 绘制直方图曲线。
  • 灰度直方图反映图像的亮度分布。

2.2 计算和可视化彩色直方图

为彩色图像的每个通道(B、G、R)分别计算直方图。

示例:彩色直方图

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

# 读取图像
img = cv2.imread('lena.jpg')
if img is None:
    print("错误:无法加载图像")
    exit()

# 分离通道
channels = cv2.split(img)  # B, G, R
colors = ('b', 'g', 'r')

# 计算并绘制直方图
plt.figure(figsize=(8, 5))
for i, (ch, color) in enumerate(zip(channels, colors)):
    hist = cv2.calcHist([ch], [0], None, [256], [0, 256])
    plt.plot(hist, color=color, label=f'通道 {color.upper()}')
plt.title('彩色直方图')
plt.xlabel('像素值')
plt.ylabel('像素数量')
plt.legend()
plt.grid(True)
plt.show()

# 显示图像
cv2.imshow('原始图像', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

说明

  • 彩色图像的每个通道(B、G、R)单独计算直方图。
  • 不同颜色通道的分布反映图像的颜色特性。

2.3 直方图均衡化 (cv2.equalizeHist)

直方图均衡化通过重新分配灰度值增强图像对比度,适合低对比度图像。

示例:直方图均衡化

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

# 读取灰度图像
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
if img is None:
    print("错误:无法加载图像")
    exit()

# 直方图均衡化
equalized = cv2.equalizeHist(img)

# 计算直方图
hist_orig = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_eq = cv2.calcHist([equalized], [0], None, [256], [0, 256])

# 可视化直方图
plt.figure(figsize=(12, 5))
plt.subplot(121)
plt.plot(hist_orig, color='black')
plt.title('原始直方图')
plt.xlabel('像素值')
plt.ylabel('像素数量')
plt.grid(True)

plt.subplot(122)
plt.plot(hist_eq, color='black')
plt.title('均衡化后直方图')
plt.xlabel('像素值')
plt.ylabel('像素数量')
plt.grid(True)
plt.tight_layout()
plt.show()

# 显示图像
cv2.imshow('原始灰度图像', img)
cv2.imshow('均衡化图像', equalized)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 保存结果
cv2.imwrite('equalized.jpg', equalized)

说明

  • equalizeHist:将灰度值重新映射,使直方图更均匀,增强对比度。
  • 仅适用于灰度图像(单通道)。
  • 均衡化后直方图分布更宽,图像对比度更强。

2.4 直方图比较 (cv2.compareHist)

比较两张图像的直方图,判断相似性。

示例:直方图比较

import cv2
import numpy as np

# 读取两张图像
img1 = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('lena_modified.jpg', cv2.IMREAD_GRAYSCALE)  # 替换为相似或不同图像
if img1 is None or img2 is None:
    print("错误:无法加载图像")
    exit()

# 计算直方图
hist1 = cv2.calcHist([img1], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])

# 归一化直方图
cv2.normalize(hist1, hist1, 0, 1, cv2.NORM_MINMAX)
cv2.normalize(hist2, hist2, 0, 1, cv2.NORM_MINMAX)

# 比较直方图
methods = [
    ('相关系数', cv2.HISTCMP_CORREL),  # 越大越相似
    ('卡方', cv2.HISTCMP_CHISQR),      # 越小越相似
    ('巴氏距离', cv2.HISTCMP_BHATTACHARYYA)  # 越小越相似
]

for name, method in methods:
    score = cv2.compareHist(hist1, hist2, method)
    print(f"{name}: {score:.4f}")

# 显示图像
cv2.imshow('图像1', img1)
cv2.imshow('图像2', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出示例(假设 img2 是修改后的图像):

相关系数: 0.9500
卡方: 1.2345
巴氏距离: 0.1234

说明

  • compareHist 提供多种比较方法:
  • HISTCMP_CORREL:相关系数,接近 1 表示相似。
  • HISTCMP_CHISQR:卡方距离,接近 0 表示相似。
  • HISTCMP_BHATTACHARYYA:巴氏距离,接近 0 表示相似。
  • 归一化直方图确保比较结果一致。

三、综合示例:直方图处理流水线

结合直方图计算、均衡化和比较:

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

def histogram_pipeline(image_path1, image_path2):
    """直方图处理流水线"""
    # 读取图像
    img1 = cv2.imread(image_path1, cv2.IMREAD_GRAYSCALE)
    img2 = cv2.imread(image_path2, cv2.IMREAD_GRAYSCALE)
    if img1 is None or img2 is None:
        print("错误:无法加载图像")
        return

    # 直方图均衡化
    img1_eq = cv2.equalizeHist(img1)

    # 计算直方图
    hist1 = cv2.calcHist([img1], [0], None, [256], [0, 256])
    hist1_eq = cv2.calcHist([img1_eq], [0], None, [256], [0, 256])
    hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])

    # 比较直方图
    score = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
    print(f"直方图相关系数: {score:.4f}")

    # 可视化直方图
    plt.figure(figsize=(12, 4))
    plt.subplot(131)
    plt.plot(hist1, color='black')
    plt.title('图像1 直方图')
    plt.xlabel('像素值')
    plt.ylabel('像素数量')
    plt.grid(True)

    plt.subplot(132)
    plt.plot(hist1_eq, color='black')
    plt.title('图像1 均衡化直方图')
    plt.xlabel('像素值')
    plt.ylabel('像素数量')
    plt.grid(True)

    plt.subplot(133)
    plt.plot(hist2, color='black')
    plt.title('图像2 直方图')
    plt.xlabel('像素值')
    plt.ylabel('像素数量')
    plt.grid(True)
    plt.tight_layout()
    plt.show()

    # 显示图像
    cv2.imshow('图像1', img1)
    cv2.imshow('图像1 均衡化', img1_eq)
    cv2.imshow('图像2', img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('img1_eq.jpg', img1_eq)

# 使用示例
histogram_pipeline('lena.jpg', 'lena_modified.jpg')  # 替换为你的图像路径

四、应用示例:直方图与阈值处理结合

直方图分析可辅助阈值处理:

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

# 读取灰度图像
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
if img is None:
    print("错误:无法加载图像")
    exit()

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

# 直方图均衡化
img_eq = cv2.equalizeHist(img)

# 二值化(Otsu 阈值)
_, binary = cv2.threshold(img_eq, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 显示结果
cv2.imshow('原始灰度图像', img)
cv2.imshow('均衡化图像', img_eq)
cv2.imshow('二值化图像', binary)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 可视化直方图
plt.figure(figsize=(8, 5))
plt.plot(hist, color='black')
plt.title('原始直方图')
plt.xlabel('像素值')
plt.ylabel('像素数量')
plt.grid(True)
plt.show()

# 保存结果
cv2.imwrite('binary.jpg', binary)

说明

  • 均衡化增强对比度后,Otsu 阈值更有效。
  • 直方图分析可帮助选择合适的阈值。

五、注意事项

  1. 输入图像
  • 灰度图像用于单通道直方图,彩色图像需分离通道。
  • 数据类型通常为 uint8
  1. 参数调整
  • bins(直方图分组数)通常设为 256(完整灰度范围)。
  • 掩码(mask)可限制直方图计算区域。
  1. 性能优化
  • 使用 ROI 减少计算量。
  • 彩色直方图计算时,逐通道处理效率更高。
  1. 错误处理
  • 检查 imread 返回值,防止图像加载失败。
  • 确保通道数和直方图参数匹配。
  1. 可视化
  • Matplotlib 适合直方图可视化,注意 BGR 到 RGB 的转换。

六、资源

  • 官方文档:https://docs.opencv.org/master/d1/db7/tutorial_py_histogram_begins.html
  • imgproc 模块:https://docs.opencv.org/master/d7/d1d/tutorial_how_to_use_IPP.html
  • 社区:在 X 平台搜索 #opencv 获取最新讨论。

如果你需要更深入的直方图处理示例(如多维直方图、CLAHE 均衡化)或 C++ 实现代码,请告诉我,我可以提供详细的解决方案或针对特定任务的优化!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注