3、直方图分析:什么是直方图?直方图如何反映曝光问题?实战:绘制并解读一张欠曝/过曝图像的直方图。

好,咱们进入第三讲。前两章我们聊了暗部提亮和高光压缩的基本概念,也看了几张对比图。但说实话,光靠眼睛看,你很难量化一张图到底欠曝了多少、高光到底有没有被裁切。

这时候,就需要一个非常客观的工具——直方图。

我个人习惯,拿到一张RAW格式的照片,第一件事不是看画面好不好看,而是先看直方图。它就像相机的“体检报告”,哪里有问题,一目了然。

3.1 什么是直方图?

直方图,说白了就是一张统计图。它统计的是图像中每个亮度级别上,有多少个像素。

你想想看,一张8位深度的图像,亮度范围是0到255。0代表纯黑,255代表纯白。直方图的横轴就是这256个亮度级别,从左到右,从黑到白。纵轴呢,就是每个亮度级别上像素的数量。

所以,直方图能告诉你三件事:

  • 曝光分布:像素主要集中在暗部、中间调,还是亮部?
  • 动态范围:图像有没有从纯黑到纯白的完整过渡?
  • 裁切风险:有没有像素被“挤”到最左边(纯黑)或最右边(纯白),导致细节丢失?

核心概念:直方图不是“好看”的工具,而是“诊断”的工具。它不关心画面美不美,只关心数据是否健康。

3.2 直方图如何反映曝光问题?

我在项目中遇到过很多次,摄影师觉得画面“看着还行”,但直方图一拉出来,问题全暴露了。常见的曝光问题,直方图会给出非常明确的信号。

3.2.1 欠曝(曝光不足)

欠曝的直方图,像素会大量堆积在横轴的左侧(暗部区域),右侧(亮部区域)几乎是空的。

举个例子,你拍了一张夜景,画面很暗。直方图看起来就像一座“山”靠在左边墙上。这时候,暗部细节可能已经丢失了,因为很多像素被压缩到了0附近。

曝光状态 直方图特征 典型问题
欠曝 像素集中在左侧,右侧空白 暗部细节丢失,噪点增多
过曝 像素集中在右侧,左侧空白 高光细节丢失,出现死白
正常曝光 像素分布均匀,从暗到亮都有 细节丰富,动态范围利用充分
高对比度 两端高中间低,呈“U”形 暗部和亮部细节可能同时丢失

3.2.2 过曝(曝光过度)

过曝的直方图正好相反。像素会大量堆积在右侧,左侧几乎没东西。这时候,高光区域(比如天空、人脸高光)会变成一片死白,完全没有任何纹理。

嗯,这里要注意:过曝比欠曝更难救。欠曝的暗部噪点,你还能用降噪算法压一压。但过曝的死白区域,像素值就是255,没有任何信息可以恢复。所以,我建议你拍摄时,宁可欠曝一档,也别过曝。

3.2.3 低对比度(灰蒙蒙)

还有一种情况,直方图看起来像一座“山”立在中间,左右两端都是空的。这说明画面既没有纯黑,也没有纯白,整体对比度很低,看起来灰蒙蒙的。

这种图,后期需要做对比度拉伸,把直方图的两端“拉开”,让暗部更暗、亮部更亮。

个人经验:我处理过一张雾天拍的风景照,直方图就是典型的“中间山”。通过调整色阶,把黑场和白场分别拉到直方图的两端,画面立刻通透了很多。但要注意,拉得太狠会产生断层,这个后面会讲。

3.3 实战:绘制并解读一张欠曝/过曝图像的直方图

光说不练假把式。咱们用Python来实战一下。我会生成一张模拟的欠曝图像,然后绘制它的直方图,并解读问题。

3.3.1 生成模拟图像

为了演示,我手动创建一个8x8像素的“图像”,模拟欠曝场景:大部分像素值在0-50之间,只有少数几个像素在100以上。

import numpy as np
import matplotlib.pyplot as plt

# 模拟一张欠曝图像(8x8像素)
# 大部分像素值在0-50之间,模拟暗部
under_exposed = np.array([
    [10, 15, 20, 25, 30, 35, 40, 45],
    [12, 18, 22, 28, 32, 38, 42, 48],
    [8,  14, 19, 24, 29, 34, 39, 44],
    [5,  10, 15, 20, 25, 30, 35, 40],
    [3,   8, 12, 18, 22, 28, 32, 38],
    [1,   5, 10, 15, 20, 25, 30, 35],
    [0,   3,  8, 12, 18, 22, 28, 32],
    [0,   1,  5, 10, 15, 20, 25, 30]
], dtype=np.uint8)

# 模拟一张过曝图像(大部分像素值在200-255之间)
over_exposed = np.array([
    [200, 210, 220, 230, 240, 250, 255, 255],
    [205, 215, 225, 235, 245, 255, 255, 255],
    [210, 220, 230, 240, 250, 255, 255, 255],
    [215, 225, 235, 245, 255, 255, 255, 255],
    [220, 230, 240, 250, 255, 255, 255, 255],
    [225, 235, 245, 255, 255, 255, 255, 255],
    [230, 240, 250, 255, 255, 255, 255, 255],
    [235, 245, 255, 255, 255, 255, 255, 255]
], dtype=np.uint8)

print("欠曝图像像素值范围:", under_exposed.min(), "-", under_exposed.max())
print("过曝图像像素值范围:", over_exposed.min(), "-", over_exposed.max())

3.3.2 绘制直方图

接下来,我们用matplotlib绘制这两张图的直方图。

# 绘制欠曝图像的直方图
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.hist(under_exposed.ravel(), bins=256, range=[0, 256], color='blue', alpha=0.7)
plt.title('欠曝图像直方图')
plt.xlabel('像素亮度 (0-255)')
plt.ylabel('像素数量')
plt.xlim([0, 255])

# 绘制过曝图像的直方图
plt.subplot(1, 2, 2)
plt.hist(over_exposed.ravel(), bins=256, range=[0, 256], color='red', alpha=0.7)
plt.title('过曝图像直方图')
plt.xlabel('像素亮度 (0-255)')
plt.ylabel('像素数量')
plt.xlim([0, 255])

plt.tight_layout()
plt.show()

3.3.3 解读结果

运行上面的代码,你会看到两张直方图:

  • 欠曝直方图:所有柱子都挤在0-50之间,右侧一片空白。这说明画面整体很暗,而且暗部细节可能已经丢失(因为很多像素值接近0)。
  • 过曝直方图:所有柱子都挤在200-255之间,左侧一片空白。这说明画面整体很亮,而且高光区域(值255)出现了裁切,细节完全丢失。

避坑指南:我曾经在处理一张星空照片时,发现直方图左侧有一个“尖峰”。一开始没在意,后来放大看,发现暗部全是彩色噪点。那个尖峰就是噪点像素的统计。所以,直方图左侧的尖峰,不一定只是“暗部细节”,也可能是“噪点”。

3.4 知识结构图:直方图分析核心逻辑

下面这张SVG图,帮你梳理直方图分析的核心逻辑。从“输入图像”到“诊断结论”,每一步都对应一个具体的操作。

直方图分析核心逻辑 直方图分析核心逻辑 输入图像 计算直方图(统计像素分布) 分析分布形态(左偏/右偏/居中) 欠曝(左侧堆积) 正常(分布均匀) 过曝(右侧堆积) 诊断结论:是否需要提亮/压缩/降噪 提示:直方图是诊断工具,不是审美工具。先看数据,再谈调色。

3.5 总结与下一步

直方图是后期处理的基础。它能帮你快速判断一张图的曝光状态,避免盲目调色。

记住几个关键点:

  • 欠曝:像素挤在左边,暗部细节可能丢失,噪点增多。
  • 过曝:像素挤在右边,高光细节丢失,无法恢复。
  • 低对比度:像素挤在中间,画面发灰。
  • 高对比度:像素挤在两边,暗部和亮部细节可能同时丢失。