4. 场景特征提取:从像素到语义的桥梁
场景自适应算法要能“看懂”画面,第一步就是提取特征。说白了,就是把一张图片里那些对曝光融合有用的信息,用数学的方式描述出来。
我个人习惯把特征分成四大类:亮度、纹理、颜色、边缘梯度。这四类特征组合起来,基本就能覆盖一个场景的“画像”了。
4.1 亮度统计特征
亮度是最直观的特征。但直接取平均亮度往往不够用,因为场景的动态范围差异太大了。
核心指标:
- 平均亮度:整张图的亮度均值,反映整体明暗倾向。
- 亮度直方图:统计每个亮度级别的像素数量。我常用它来判断场景是否过曝或欠曝。
- 动态范围:最亮区域与最暗区域的比值。这个值越大,说明场景越“硬”,需要多帧融合。
- 亮度标准差:反映亮度的离散程度。标准差大,说明场景明暗对比强烈。
实战经验:我在做HDR融合时,发现单纯用平均亮度做阈值判断,经常把黄昏场景误判为“正常”。后来改用直方图的第5百分位和第95百分位差值作为动态范围指标,准确率提升了不少。
亮度特征提取代码示例:
import cv2
import numpy as np
def extract_luminance_features(img):
# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 平均亮度
mean_lum = np.mean(gray)
# 直方图
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
# 动态范围(取第5和第95百分位)
sorted_pixels = np.sort(gray.flatten())
low = sorted_pixels[int(len(sorted_pixels) * 0.05)]
high = sorted_pixels[int(len(sorted_pixels) * 0.95)]
dynamic_range = high - low
# 标准差
std_lum = np.std(gray)
return {
'mean': mean_lum,
'histogram': hist,
'dynamic_range': dynamic_range,
'std': std_lum
}
4.2 纹理特征
纹理特征描述的是图像局部的细节丰富程度。你想想看,一张白墙和一张布满树叶的照片,纹理复杂度天差地别。
常用方法:
- 局部二值模式(LBP):对每个像素的邻域进行二值编码,统计模式分布。对光照变化不敏感,适合做纹理分类。
- 灰度共生矩阵(GLCM):统计像素对的空间关系,提取对比度、能量、熵等特征。
- 拉普拉斯方差:计算图像的二阶导数,方差越大,纹理越丰富。这个计算简单,我经常用它做快速纹理评估。
避坑指南:我曾经在低光照场景下用LBP提取纹理,结果全是噪声。后来我意识到,纹理特征提取前一定要做预处理——先降噪,再提取。否则你提取的“纹理”其实是噪声。
纹理特征对比表:
| 方法 | 计算速度 | 光照鲁棒性 | 适用场景 |
|---|---|---|---|
| LBP | 快 | 高 | 纹理分类、人脸识别 |
| GLCM | 慢 | 中 | 纹理分析、医学图像 |
| 拉普拉斯方差 | 极快 | 低 | 快速模糊检测、对焦评估 |
4.3 颜色分布特征
颜色分布能告诉我们场景的“调性”。比如,日落场景偏橙红,雪景偏蓝白,森林偏绿。
关键特征:
- 颜色直方图:在RGB或HSV空间统计每个通道的分布。HSV空间更符合人眼感知,我一般用H和S通道。
- 主色调:通过K-means聚类提取图像中的主要颜色。这个在场景分类中很实用。
- 色彩丰富度:计算颜色直方图的熵值。熵越大,颜色越丰富。
- 色偏检测:计算a*b*通道的均值,判断是否存在整体色偏。
注意:颜色特征对光照变化非常敏感。同一场景在日光和灯光下,颜色直方图可能完全不同。所以,在做颜色特征提取时,最好先做白平衡校正,或者使用对光照不敏感的颜色空间(如HSV的H通道)。
4.4 边缘与梯度特征
边缘和梯度是图像中最“硬”的结构信息。它们描述了像素值的变化剧烈程度。
核心算子:
- Sobel算子:计算一阶梯度,提取水平和垂直方向的边缘。
- Canny边缘检测:多阶段算法,能提取出清晰的单像素边缘。
- 梯度幅值直方图:统计每个像素的梯度大小,反映图像的锐度。
- 边缘密度:边缘像素占总像素的比例。边缘密度高,说明场景细节丰富。
梯度特征提取代码:
def extract_gradient_features(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Sobel梯度
grad_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
grad_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
grad_mag = np.sqrt(grad_x**2 + grad_y**2)
# Canny边缘
edges = cv2.Canny(gray, 50, 150)
# 边缘密度
edge_density = np.sum(edges > 0) / edges.size
# 梯度幅值统计
grad_mean = np.mean(grad_mag)
grad_std = np.std(grad_mag)
return {
'gradient_magnitude': grad_mag,
'edges': edges,
'edge_density': edge_density,
'grad_mean': grad_mean,
'grad_std': grad_std
}
4.5 特征融合与场景画像
单一特征往往不够用。我习惯把四类特征组合成一个特征向量,用来描述整个场景。
特征向量示例:
- 亮度:平均亮度、动态范围、标准差
- 纹理:拉普拉斯方差、LBP直方图熵
- 颜色:HSV直方图、主色调、色彩丰富度
- 边缘:边缘密度、梯度均值、梯度标准差
这个特征向量就是场景的“数字指纹”。后续的曝光融合参数,就是根据这个指纹来动态调整的。
我的经验:特征维度不是越多越好。我曾经堆了30多个特征,结果模型过拟合严重。后来我通过主成分分析(PCA)降维到10个核心特征,效果反而更好。记住:特征提取的目标是“够用”,不是“堆砌”。