下面是整理好的带详细注释 + 教学说明的代码,既可以直接运行,也适合作为学习笔记:
python
运行
# 导入核心库
import cv2 # OpenCV:用于图像读取、直方图计算
import matplotlib.pyplot as plt # Matplotlib:用于绘图展示
# ===================== 一、灰度图直方图(Matplotlib直接绘制) =====================
"""
核心原理:
将灰度图像的二维像素矩阵展平为一维数组,统计每个灰度值(0-255)的像素数量
适合快速查看整体灰度分布,代码更简洁
"""
# 1. 读取灰度图:cv2.IMREAD_GRAYSCALE 指定以灰度模式读取
phone_gray = cv2.imread(filename='phone.png', flags=cv2.IMREAD_GRAYSCALE)
# 检查图片是否读取成功(避坑:路径/文件名错误会导致返回None)
if phone_gray is None:
print("❌ 错误:找不到phone.png,请检查文件路径/名称!")
else:
# 2. 将二维图像矩阵展平为一维数组(比如200x200的图→40000个像素值的一维数组)
pixel_values = phone_gray.ravel()
# 3. 绘制直方图
plt.figure(figsize=(8, 4)) # 设置画布大小
plt.hist(pixel_values, bins=256, range=[0, 256]) # bins=256表示分256个区间(对应0-255灰度)
plt.title('灰度图直方图(Matplotlib版)') # 标题
plt.xlabel('灰度值(0=黑,255=白)') # X轴标签
plt.ylabel('像素数量') # Y轴标签
plt.xlim([0, 256]) # 限定X轴范围(仅显示0-255)
plt.show() # 显示图像
# ===================== 二、灰度图直方图(OpenCV计算+Matplotlib绘图) =====================
"""
核心原理:
先用cv2.calcHist计算直方图数据,再用plt.plot绘制曲线
适合自定义区间(比如合并灰度级),灵活性更高
"""
if phone_gray is not None:
# 1. 计算直方图:cv2.calcHist(图像列表, 通道, 掩码, 区间数, 灰度范围)
# - images=[phone_gray]:传入要计算的图像(需用列表包裹)
# - channels=[0]:灰度图只有1个通道,固定传0;彩色图传0/1/2对应B/G/R
# - mask=None:不使用掩码(计算整张图)
# - histSize=[16]:将0-255灰度级合并为16个区间(每个区间包含16个灰度值)
# - ranges=[0, 256]:灰度值的范围(固定0-256)
phone_hist = cv2.calcHist(images=[phone_gray], channels=[0], mask=None, histSize=[16], ranges=[0, 256])
# 2. 绘制直方图曲线
plt.figure(figsize=(8, 4))
plt.plot(phone_hist, color='black', linewidth=2) # 黑色曲线,线宽2
plt.title('灰度图直方图(OpenCV计算版)')
plt.xlabel('灰度区间(共16个)')
plt.ylabel('像素数量')
plt.show()
# ===================== 三、彩色图三通道直方图(B/G/R分别绘制) =====================
"""
核心原理:
OpenCV读取的彩色图是BGR格式(不是RGB),需分别计算B/G/R三个通道的直方图
适合分析彩色图像的色彩分布(比如偏蓝/偏红)
"""
# 1. 读取彩色图(默认BGR格式)
img_color = cv2.imread('phone.png')
if img_color is None:
print("❌ 错误:找不到phone.png,请检查文件路径/名称!")
else:
# 2. 定义通道对应的颜色(B→蓝色,G→绿色,R→红色)
color = ('blue', 'green', 'red')
# 3. 遍历三个通道,分别计算+绘制直方图
plt.figure(figsize=(8, 4))
for i, col in enumerate(color):
# 计算当前通道的直方图(histSize=256:每个通道分256个区间)
histr = cv2.calcHist(images=[img_color], channels=[i], mask=None, histSize=[256], ranges=[0, 256])
# 绘制曲线(颜色与通道对应,更直观)
plt.plot(histr, color=col, label=f'{col}通道')
# 4. 美化图表
plt.title('彩色图三通道直方图')
plt.xlabel('像素值(0-255)')
plt.ylabel('像素数量')
plt.legend() # 显示图例(区分三个通道)
plt.show()
📝 关键知识点总结
- 两种绘制方式对比 :
- Matplotlib
plt.hist():适合快速查看灰度分布,代码简洁,直接处理像素数组。 - OpenCV
cv2.calcHist():适合自定义区间 / 掩码,灵活性高,先算数据再绘图。
- Matplotlib
- 避坑要点 :
- 图片路径 / 名称必须正确(建议将
phone.png和代码放在同一文件夹); - OpenCV 彩色图是BGR 格式,不是 RGB,绘制时需注意通道对应关系;
- 用
if img is None检查图片读取状态,避免后续代码报错。
- 图片路径 / 名称必须正确(建议将
- 应用场景 :
- 灰度直方图:判断图像亮度(峰值偏左 = 偏暗,偏右 = 偏亮)、对比度;
- 彩色直方图:分析图像色彩偏向,用于图像增强 / 色彩校正。
直接运行这段代码,会依次弹出 3 个直方图窗口,清晰展示不同方式的绘制效果。如果需要调整图片路径、区间数等参数,注释里都标注了修改位置,新手也能轻松上手~