直方图是分析图像像素分布的核心工具,本文通过自定义函数实现彩色图像 BGR 三通道直方图的手动绘制,新手可直观理解直方图的计算与可视化逻辑。
核心代码实现
python
import cv2 as cv
import numpy as np
# 定义计算并绘制直方图的函数
def calcanddrawhist(src, color):
# 1. 计算直方图:[图像], [通道索引], 掩膜, 直方图条数, 像素值范围
hist = cv.calcHist([src], [0], None, [256], [0.0, 255.0])
# 2. 获取直方图最大值(用于归一化)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(hist)
# 3. 创建256×256的空白画布(用于绘制直方图)
hist_img = np.zeros([256, 256, 3], np.uint8)
hpt = int(0.9 * 256) # 限制直方图高度(预留10%边距)
# 4. 遍历每个像素值,绘制直方图线条
for h in range(256):
# 归一化直方图数值到画布高度范围
intensity = int(hist[h] * hpt / max_val)
# 绘制垂直线:从底部向上,颜色为指定通道色
cv.line(hist_img, (h, 256), (h, 256-intensity), color)
return hist_img
# 主程序
src = cv.imread('.\image\1.bmp')
if src is None:
print('could not load image')
exit()
cv.imshow('src', src)
# 拆分BGR通道
b, g, r = cv.split(src)
# 分别计算并绘制各通道直方图
hist_imgb = calcanddrawhist(b, [255, 0, 0]) # 蓝色通道(B)
hist_imgg = calcanddrawhist(g, [0, 255, 0]) # 绿色通道(G)
hist_imgr = calcanddrawhist(r, [0, 0, 255]) # 红色通道(R)
# 显示各通道直方图
cv.imshow('his_b', hist_imgb)
cv.imshow('his_g', hist_imgg)
cv.imshow('his_r', hist_imgr)
cv.waitKeyEx(0)
cv.destroyAllWindows()
关键知识点解析
1. 直方图绘制核心流程
| 步骤 | 核心 API / 操作 | 作用说明 |
|---|---|---|
| 计算直方图 | cv.calcHist() |
统计每个像素值(0-255)的出现次数 |
| 归一化 | hist[h] * hpt/max_val |
将直方图数值缩放到画布高度范围,避免超出画布 |
| 绘制线条 | cv.line() |
以像素值为 x 轴,出现次数为 y 轴,绘制垂直线条 |
2. 关键参数说明
cv.calcHist([src], [0], None, [256], [0.0, 255.0]):[src]:输入图像(需为列表格式);[0]:通道索引(单通道处理时固定为 0);None:无掩膜(分析整幅图像);[256]:直方图条数(对应 0-255 像素值);[0.0, 255.0]:像素值范围。
hpt = int(0.9 * 256):预留 10% 画布边距,避免直方图顶边溢出。
3. 优化与扩展技巧
-
合并显示 :将三个通道直方图拼接为一张图,更便于对比:
python
运行
pythonhist_merge = cv.hconcat([hist_imgb, hist_imgg, hist_imgr]) cv.imshow('hist_merge', hist_merge) -
灰度图直方图 :无需拆分通道,直接传入灰度图即可:
python
运行
pythongray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) hist_gray = calcanddrawhist(gray, [255, 255, 255]) # 白色线条 -
效率优化:循环绘制可改用 numpy 向量化操作,提升大图像处理速度。
总结
- 直方图绘制核心是
cv.calcHist()统计像素分布,再通过归一化 + 画线实现可视化; - 彩色图像需拆分 BGR 通道分别绘制,通道颜色与线条颜色对应更易区分;
- 归一化步骤是关键,可避免直方图数值超出画布范围导致显示异常。