OpenCV 4 Python 实战:计算摄影与机器学习应用
本章内容基于 OpenCV 4 的 Python 接口,深入探讨计算摄影与机器学习在视觉任务中的实际应用。我们将重点处理数码摄影中的动态范围问题、全景拼接算法,以及利用统计特性进行目标跟踪和分类识别。
计算摄影基础
理解 8 位图像的限制
典型的 JPEG 图像将每个像素编码为 24 位(RGB 各 8 位),数值范围在 0-255 之间。这限制了相机的动态范围。大多数 LCD 显示器的对比度比约为 1000:1,而人眼可达 15000:1。这意味着在拍摄高对比场景(如逆光)时,简单的 8 位格式往往无法同时保留亮部和暗部的细节。
数码相机传感器通常使用拜耳滤波器阵列来捕捉颜色信息,每个像素点只记录一种颜色的强度。通过插值算法还原出完整的 RGB 图像。然而,传感器的原始数据(RAW)通常包含 12 到 14 位的精度,远优于 8 位 JPEG。
读取 RAW 图像
为了充分利用传感器的动态范围,我们可以使用 rawpy 库读取 RAW 文件(如 CR2)。以下函数展示了如何加载 RAW 或常规 JPEG 图像,并统一转换为 OpenCV 支持的 BGR 格式:
import rawpy
import cv2
from pathlib import Path
def load_image(path, bps=16):
if path.suffix.upper() == '.CR2':
with rawpy.imread(str(path)) as raw:
data = raw.postprocess(no_auto_bright=True, gamma=(1, 1), output_bps=bps)
return cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
else:
return cv2.imread(str(path))
对于 14 位灰度图的处理,可以进一步转换数据类型以保留更多精度:
def load_14bit_gray(path):
img = load_image(path, bps=16)
return (cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) / 4).astype(np.uint16)
伽玛校正
由于人眼对亮度的感知是非线性的,直接显示线性记录的 RAW 数据往往显得过暗。伽玛校正通过非线性变换调整像素值,使图像更符合人眼的视觉特性。
公式如下: $$O = 255 \times (I / 2^{bps})^\gamma$$
其中 $\gamma < 1$ 用于提亮图像(伽玛压缩),$\gamma > 1$ 用于压暗以突出暗部细节(伽玛扩展)。
实现代码如下:

