近日发布的小米 17 Ultra 徕卡特别版搭载了独家的 “徕卡一瞬” 影像模式。

1. 不是滤镜,而是风格模型
“徕卡一瞬”并非像普通手机里那样简单叠加滤镜,而是在影像处理管线层面重新构建风格模型,是经过端侧模型训练 + 真实胶片/相机样张建模后的成果。

2. 模拟真实经典徕卡相机的画质风格
“徕卡一瞬”提供两种风格:

  • 徕卡 M9 CCD 模拟模式 — 复刻 Leica M9 相机 CCD 影像的色彩与影调,带来浓郁色彩层次和柔和细节质感,而不仅仅是颜色调整。
  • 徕卡 M3 + MONOPAN 50 胶片黑白模式 — 模拟经典 Leica M3 相机配合 MONOPAN 50 胶片产生的黑白影调效果,具备真实的亮度过渡和颗粒质感,而不是普通黑白滤镜那样简单去色。

3. 深度计算摄影模型实现风格迁移
色彩与影调不是预设滤镜,而是基于真实 Leica M9、M3 样张数据训练的端侧模型,可以理解成“风格迁移模型”,它会把输入的 RAW 图像映射到徕卡风格输出,同时尽量减少算法味、保持自然质感。

作者有一台富士xs20,拍摄了大量使用富士cc或者nc滤镜的照片,同时保留了jpg和raf格式的文件,这些raf和对应目标风格 jpg的训练对数据非常适合用于训练一个“富士一瞬”模型。这与小米“徕卡一瞬”使用 Leica M9/M3 样张训练模型非常相似,是学习一个摄影风格映射,最终期望通过模型处理raf或者raw格式的照片,能够输出富士滤镜风格的jpg格式照片。我们首先基于Pix2Pix项目来进行可行性验证

下面是gpt建议的训练路线:

  • 风格迁移 / 图像-to-图像网络:如使用 GAN(例如 CycleGAN、Pix2Pix)或变体网络(如 StyleGAN、VAE/GAN 组合)
    这些模型可以学习从 RAW 空间到目标风格空间的映射。
  • 条件生成模型:使用条件生成网络conditioning on RAW输入,让模型输出富士 JPG 风格图像。
  • 增强对光线、色彩、胶片颗粒的理解
    富士胶片风格往往还涉及对亮部/暗部色彩的特性映射,可能需要设计自定义损失(比如感知损失、色彩直方图损失等)来保留风格细节。

由于Pix2Pix 是严格的一对一(paired)图像到图像模型,raf和 富士机内 jpg 这种“同名一一对应”的数据,正是 Pix2Pix 的理想使用场景,因此我们使用Pix2Pix开始。

Gpt给出的大概需要的数据量:

Level 0:验证效果

200–300 对 RAW + JPG

可以做到:

  • 模型明显学到富士风格
  • 色彩方向、对比、整体观感接近
  • 局部可能有 artifacts
  • 高光 / 暗部可能不稳定

适合第一次跑通 pipeline

不推荐低于 150 对,Pix2Pix 会过拟合到“模板色”。

Level 1:可对比、可用

800–1500 对(推荐)

可以做到:

  • 和机内 JPG 非常接近
  • 不同场景(室内 / 户外 / 阴天)表现稳定
  • 颗粒、色彩过渡开始“像富士”
  • 用训练集里的 RAW 对比 JPG,差异肉眼可接受

我们首先从最简单的开始,需要注意的是:raw 或者raf文件不能直接用于模型训练,必须进行预处理,使得RAW 转换为统一“摄影输入空间”。

流程:

  1. rawpy 读取 RAW
  2. 不加曲线、不加 gamma
  3. 白平衡(或固定)
  4. 转 linear RGB
  5. 归一化到 [0,1]

否则模型学的是:不同曝光或者不同相机的混乱映射。

整体流程:

Fuji RAW (.RAF / .DNG)

        ↓  rawpy

Linear RGB (float32)

        ↓ resize / crop

256x256 tensor

        ↓ Pix2Pix Generator

Fake Fuji JPG

1.首先准备一个fuji_instant文件夹,下面各创建jpg与raf文件夹放原始数据集。

注意

  • raf 和 jpg 文件名必须完全一致
  • Level 0 不做数据增强

2.然后准备脚本preprocess_level0.py用于预处理数据。

import rawpy
import numpy as np
from PIL import Image
from pathlib import Path
from tqdm import tqdm

RAW_DIR = Path("/mnt/smb_share/fuji_instant/raf")
JPG_DIR = Path("/mnt/smb_share/fuji_instant/jpg")

OUT_ROOT = Path("pytorch-CycleGAN-and-pix2pix/datasets/fuji_level0")
TRAIN_A = OUT_ROOT / "train/A"
TRAIN_B = OUT_ROOT / "train/B"

TRAIN_A.mkdir(parents=True, exist_ok=True)
TRAIN_B.mkdir(parents=True, exist_ok=True)

SIZE = 256

def process_raw(path):
    with rawpy.imread(str(path)) as raw:   # ← 关键修复点
        rgb = raw.postprocess(
            use_camera_wb=True,
            no_auto_bright=True,
            output_bps=16
        )
    rgb = rgb.astype(np.float32) / 65535.0
    img = Image.fromarray((rgb * 255).astype(np.uint8))
    return img.resize((SIZE, SIZE), Image.BICUBIC)

def process_jpg(path):
    img = Image.open(path).convert("RGB")
    return img.resize((SIZE, SIZE), Image.BICUBIC)

for raw_path in tqdm(sorted(RAW_DIR.glob("*"))):
    name = raw_path.stem
    jpg_path = JPG_DIR / f"{name}.jpg"
    if not jpg_path.exists():
        continue

    process_raw(raw_path).save(TRAIN_A / f"{name}.png")
    process_jpg(jpg_path).save(TRAIN_B / f"{name}.png")

print("✅ Level 0 preprocess done")

python3 reprocess_level0.py

生成的数据路径

3. 准备 Pix2Pix并安装相关的依赖

git clone https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix.git

cd pytorch-CycleGAN-and-pix2pix

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

pip install numpy pillow opencv-python visdom dominate tqdm rawpy imageio

4. 然后开始level0训练

python train.py \

  --dataroot datasets/fuji_level0 \

  --name fuji_pix2pix_level0 \

  --model pix2pix \

  --direction AtoB \

  --netG unet_256 \

  --batch_size 1 \

  --gpu_ids 0 \

  --display_freq 200 \

  --print_freq 200 \

  --save_epoch_freq 10 \

  --n_epochs 100 \

  --n_epochs_decay 50

输出路径中会看到三张图:

文件含义
xxx_real_A.pngRAW 输入
xxx_fake_B.png模型输出
xxx_real_B.png富士机内 JPG
    

结果样张

以下是gpt对训练日志的评估结论:

这是一份正常的 Pix2Pix Level 0 训练日志,已经跑通了 Level 0 的核心流程。

一、结论

当前训练状态:正常、值得继续跑

  • 没有 mode collapse
  • 没有 D/G 崩溃
  • L1 在合理区间波动
  • GAN loss不是一边倒

二、解读第一轮数据:

(epoch: 1, iters: 200)

G_GAN: 2.306

G_L1: 23.450

D_real: 0.069

D_fake: 0.239
  1. G_L1(重要)
  • 初期:20–50 都是正常
  • 现在:7~26 之间波动

非常健康,模型正在快速学习「RAW → 富士 JPG」的主色调 & 对比

  • G_GAN
  • 正常范围0.5 ~ 3.0
  • 现在:0.6 ~ 2.6

GAN loss 本来就会抖
这不是 regression 问题。

  • D_real / D_fake(判别器状态)

现在:

  • 有时 D_real ≈ 0.06(D 很自信)
  • 有时 D_real > 1(D 被骗了)
  • D_fake 在 0~1 来回

是 Pix2Pix 的理想状态
说明:

  • 判别器和生成器在拉锯
  • 没有一方完全碾压

作者通过肉眼观察,epoch 20色彩接近,但是有马赛克状,不是很清晰,epoch 150有这个问题。这不是训练失败,也不是你数据问题,而是 Pix2Pix 在 RAW→JPG 任务里的结构性缺陷。

一、马赛克感

有这些特征:

  • 画面块状纹理
  • 局部像被「8×8 / 16×16」切块
  • 边缘不锐利
  • 纹理像“涂抹+拼块”

这不是分辨率低而是Pix2Pix(U-Net + PatchGAN)在摄影任务里的已知问题。因为PatchGAN 是“块级判别”,判别器只看 70×70 patch,只关心局部相似性,不关心全局连续性。模型会学会了“每一块都像富士”,但不关心块与块之间的连续性(整体效果)。同时RAW 到 JPG 是连续 tone mapping 问题,而 Pix2Pix 更擅长:

  • 语义翻译(马↔斑马)
  • 边缘明显的任务

但摄影里需要的是:

  • 平滑的亮度过渡
  • 连续色彩曲线
  • 高频纹理的自然保留

Pix2Pix 不擅长这个。

  • 256×256 + resize 本身会放大问题
  • resize → bicubic
  • GAN 学到的是 resize 后的“假纹理”
  • 最终看起来像涂抹 + 马赛克

训练进行到epoch 150也有相同的问题,这不是数据量不够,是模型表达方式不对。但我们已经确认1.色彩接近富士的滤镜 2.风格方向正确。这说明数据和pipeline是对的,问题在于模型结构。Level 0 到这里就可以结束了。它验证 raf到富士风格jpg是可行的,同时帮我们找到了问题边界。同时我们注意到输出的样张已经出现了裁切区域匹配不上的问题,我们在后续的训练过程中也需要解决这个问题。

注:中途发现Smb挂载nas路径的时候一直不成功,后来发现,smb用户密码长度不能超过64!!!!

下篇:徕卡一瞬?富士一瞬!level1训练

发布于20260118。

船长

发表评论