3. 神经网络自动曝光控制

任务说明

本案例需要构建一个轻量级回归模型,输入为场景亮度特征(平均亮度、亮度标准差、暗部比例),输出为最佳曝光参数(曝光时间、模拟增益)。模型训练完成后,通过模拟曝光调整流程,验证模型预测的曝光参数能否将图像亮度拉至目标区间。

实现方案

使用PyTorch搭建三层全连接回归网络,生成模拟场景亮度数据作为训练集。曝光参数预测采用归一化输出,通过模拟曝光函数(线性增益模型)将预测参数作用于输入图像,计算调整后亮度与目标亮度的误差作为评估指标。

完整代码

import torch
import torch.nn as nn
import numpy as np

# ---------- 1. 数据生成 ----------
def generate_scene_data(num_samples=2000):
    """生成模拟场景亮度特征: [平均亮度, 亮度标准差, 暗部比例]"""
    np.random.seed(42)
    avg_lum = np.random.uniform(10, 200, num_samples)      # 平均亮度 10~200
    std_lum = np.random.uniform(5, 60, num_samples)        # 亮度标准差
    dark_ratio = np.random.uniform(0.05, 0.8, num_samples) # 暗部像素比例
    
    # 模拟最佳曝光参数: 曝光时间(ms) 与 增益(线性)
    # 目标亮度约为120, 曝光参数与当前亮度成反比关系
    target_lum = 120.0
    exp_time = np.clip(target_lum / avg_lum * 16.0, 1.0, 100.0)  # 基准16ms
    gain = np.clip(target_lum / (avg_lum * exp_time / 16.0), 1.0, 8.0)
    
    features = np.stack([avg_lum, std_lum, dark_ratio], axis=1)
    labels = np.stack([exp_time, gain], axis=1)
    return features.astype(np.float32), labels.astype(np.float32)

# ---------- 2. 回归模型 ----------
class ExposureRegressor(nn.Module):
    def __init__(self, input_dim=3, hidden_dim=64):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, 2)  # 输出: [曝光时间, 增益]
        )
    
    def forward(self, x):
        return self.net(x)

# ---------- 3. 模拟曝光调整 ----------
def simulate_exposure(image, exp_time_ms, gain):
    """模拟曝光调整: 线性亮度缩放"""
    base_time = 16.0  # 基准曝光时间
    scale = (exp_time_ms / base_time) * gain
    adjusted = np.clip(image * scale, 0, 255)
    return adjusted

def evaluate_exposure(image, exp_time_ms, gain, target_lum=120.0):
    """评估曝光效果: 返回调整后平均亮度与目标亮度误差"""
    adjusted = simulate_exposure(image, exp_time_ms, gain)
    actual_lum = np.mean(adjusted)
    return abs(actual_lum - target_lum), adjusted

# ---------- 4. 训练流程 ----------
def train_model():
    features, labels = generate_scene_data()
    X = torch.tensor(features)
    y = torch.tensor(labels)
    
    model = ExposureRegressor()
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    loss_fn = nn.MSELoss()
    
    dataset = torch.utils.data.TensorDataset(X, y)
    loader = torch.utils.data.DataLoader(dataset, batch_size=64, shuffle=True)
    
    for epoch in range(100):
        total_loss = 0
        for batch_x, batch_y in loader:
            optimizer.zero_grad()
            pred = model(batch_x)
            loss = loss_fn(pred, batch_y)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        if (epoch+1) % 20 == 0:
            print(f"Epoch {epoch+1}, Loss: {total_loss/len(loader):.4f}")
    return model

# ---------- 5. 验证与模拟 ----------
def verify_exposure_control(model):
    """使用测试场景验证曝光控制效果"""
    # 构造3个典型场景: 暗场景, 正常场景, 亮场景
    test_scenes = [
        np.full((64, 64), 30.0),    # 暗场景: 平均亮度30
        np.full((64, 64), 100.0),   # 正常场景: 平均亮度100
        np.full((64, 64), 180.0),   # 亮场景: 平均亮度180
    ]
    
    print("\n=== 曝光控制验证 ===")
    for i, scene in enumerate(test_scenes):
        avg = np.mean(scene)
        std = np.std(scene)
        dark_ratio = np.mean(scene < 50)
        feat = torch.tensor([[avg, std, dark_ratio]])
        
        with torch.no_grad():
            pred = model(feat).numpy()[0]
        
        exp_time, gain = pred
        error, adjusted = evaluate_exposure(scene, exp_time, gain)
        
        print(f"场景{i+1}: 原始亮度={avg:.1f}, "
              f"预测曝光时间={exp_time:.2f}ms, 增益={gain:.2f}, "
              f"调整后亮度={np.mean(adjusted):.1f}, 目标误差={error:.2f}")

if __name__ == "__main__":
    model = train_model()
    verify_exposure_control(model)

运行与验证

运行方式:直接执行 python exposure_control.py(需已安装 torch 与 numpy)。

验证方法:程序会输出训练损失下降过程,并在验证阶段打印三个典型场景的预测曝光参数与调整后亮度误差。

通过标准:训练损失收敛至0.01以下;三个测试场景调整后亮度与目标亮度120的误差均小于15(即调整后亮度在105~135范围内),说明模型能根据场景亮度预测合理曝光参数并完成模拟调整。