AI 创作开放麦 · 2026-08

口袋里的暗房

小胶片(Little Film)—— 把 iPhone 对准胶卷负片,取景框里直接看到照片。

负片 正片
负片 ⇌ 正片 · 同一张胶片 · 模型实时转换

John · 机器学习工程师 · 胶片爱好者 · 约 12 分钟

故事从 2024 开始

DIY 胶片冲洗与翻拍流程

朋友送相机时的照片
01 · 朋友送的相机
后来使用的胶片相机
02 · 我的相机
冲洗药水
03 · 冲洗
翻拍装置
05 · 翻拍

朋友送相机 → 开始拍摄 → 冲洗 → 灯箱 → 翻拍 → 色彩校正

旧故事 · 2024

当时,它只是一段命令行脚本

香港夜路原始负片原始负片
香港夜路校正结果校正结果
香港海港原始负片原始负片
香港海港校正结果校正结果
2024 · CLI $ python -m filmcvt negatives/ positives/ --use-wbnn 输入一个文件夹 → 逐张裁切、反相、校色 → 输出 JPG

算法已经能跑,但每次都要回到电脑,用命令处理一整个文件夹。

2026 · 从脚本到 App

现在,可以直接在手机上完成

01定位并拉直胶片
在透视、反光与强背光下找到四角,再映射成标准画幅。
02负片反相
逐像素计算 1 − x,把负片的明暗关系翻回来。
03色彩校正
WBNN 预测 3×11 多项式变换,去除橙色片基与色偏。
第一步 · 找框 · 先亲手对一次

四个角,确定一次透视变换

把青色框套到画幅上——四角与矩阵互相确定;拖动任意一边,另一边同步 ↓
目标 · 相机画面(拖四个角点,矩阵跟着变)
橙点落到  (x′, y′) = 0.50, 0.50
x′·w
y′·w
w
=
·
x
y
1
上下拖矩阵数字 · 双击恢复 · 拖角点反解
橙点 = 式中的 (x, y) → (x′, y′)
源 · 标准画幅(橙点在这边拖)
(x, y) = 0.50, 0.50

FrameNet 只预测四角;拖动四角时,页面实时反解 H。

找框是学习问题 · 拉直是几何问题

网络找四角,几何求 H

FrameNet → 四角坐标 | 四组点对应 → H → 拉直

1 · 四组具体坐标
p₁(0.10,0.10)↔q₁(0,0)p₂(0.82,0.16)↔q₂(1,0)p₃(0.72,0.84)↔q₃(1,1)p₄(0.18,0.76)↔q₄(0,1)
2 · 定义 H,再逐组代入
H =
abcdefgh1
固定 H₃₃=1
p₁0.10a+0.10b+c = 00.10d+0.10e+f = 0
p₂0.82a+0.16b+c = 0.82g+0.16h+10.82d+0.16e+f = 0
p₃0.72a+0.84b+c = 0.72g+0.84h+10.72d+0.84e+f = 0.72g+0.84h+1
p₄0.18a+0.76b+c = 00.18d+0.76e+f = 0.18g+0.76h+1
3 · 页面实际求得 H
————————1.000

同一组数驱动上面的求解与下面的网格动画;App 中由 CIPerspectiveCorrection 完成对应步骤。

数据 · 自己拍

让标注免费

采集现场 · 对准青框,拍下即标注
采集样本
采好的样本 · 绿框 = 随图保存的标签
数据 · 一张变一百张

增强:已知答案的考试

训练时把每张采集的照片随机换窗口、转角度、调亮度、加噪声和假反光——变换是我们施加的,所以角点标签跟着同一个变换走,等于免费多拍了一百倍。

增强后的训练数据
同一批照片增强后的训练样本 · 绿框 = 跟随变换的标签 · 每个 batch 都是新的
冷知识 「把数据揉一揉再让网络做题」是大模型的同款配方——遮住词猜词,揉皱图猜角。自监督学习的通用套路。
微调与验证

FrameNet:2.6 px

20未参与训练的真实图像
2.6 px平均角点坐标误差
8.3 px最差样本
≈ 1.6%相对 161 px 画幅宽度
2.6 像素角点误差的尺度示意
尺度示意 · 绿 = 真值 · 红 = 按 2.6 px 偏移绘制 · 右侧放大 4×

合成数据 + 真实手机图像微调约 30 分钟;指标在 224 × 224 输入空间计算。

第二步 · 反相

逐像素,把明暗翻回来

一个像素 = (R, G, B) // 红、绿、蓝三个 0–1 的数

(R′, G′, B′) = (1−R, 1−G, 1−B) // 三个通道分别反相

在任意一边移动鼠标选一个像素,两边同步显示它的三个数 ↓
源 · 负片
( – , – , – )
→
每个数 1−x
目标 · 正片
( – , – , – )
第二步 · 反相 · 我们试过的

四种反相,四个答案

同一张负片,四种「负转正该怎么算」。x线 = 按 sRGB 公式还原成线性光,片基估计为 (211,127,88);四张只调了曝光,没做任何白平衡。

码值补数的结果
① 码值补数x′ = 1 − x发灰发青,黑压不下去
线性域补数的结果
② 线性域补数x′线 = 片基线 − x线整体偏橙,暗部塌掉
除以片基的结果
③ 除以片基x′线 = 片基线 ÷ x线黑是黑了,蓝得更狠
密度域加 gamma 的结果
④ 密度域 + γx′线 = (x线 ÷ 片基线)−1/γ层次最好,颜色最离谱
四种都能把明暗翻过来,没有一种能把颜色带回来。 差别不只是「偏色」——它们连偏的方向都不一样,说明剩下的不是一个乘法,而是一整条形状未知的曲线。 于是我们不再猜这条曲线:反相就用最简单的 ①,剩下的交给数据。
第三步 · 白平衡 · 你来试试

你来当调色师

图上移动鼠标选像素(两边同一位置);上下拖动矩阵里的数字,整张图跟着变 ↓
源 · 反相后的蓝图
目标 · 矩阵作用之后
参考答案
参考答案
–
–
–
=
·
–
–
–
上下拖动矩阵数字调色 · 双击恢复 · 9 个自由度

9 个数还只是「线性」调色,已经拧不明白了——那么,交给网络?

第三步 · 白平衡 · 让网络拧

33 个旋钮,网络来拧

φ(r,g,b) = [ r, g, b, r², g², b², rg, rb, gb, rgb, 1 ]ᵀ

[R′,G′,B′]ᵀ = M3×11 · φ(r,g,b) ← M 的 33 个数由网络给出

多项式核出自 Afifi & Brown, “When Color Constancy Goes Wrong” (WB-sRGB), CVPR 2019 · §3.3
11 张基图
一张图先变成 11 张「基图」 · 输出的每个通道 = 它们的线性组合(权重就是 M 的一行)
白平衡 · 数据与目标

WBNN:像素级 MSE

约 50 万张正常照片组成干净目标 y;随机色偏生成输入 x = D(y)。

干净图像与随机色偏输入
同一张干净图 y,可以生成许多种带色偏输入 x
通道乘法 0.5–1.5
通道偏置 0–0.5
Gamma 0.8–1.2
全局缩放 0.8–1.0
合起来

完整的一条链

原始
取景
→
裁切
找框拉直
→
反相
反相
→
白平衡
白平衡
从协作到产品

两天做出原型,再把它变成小胶片

模型解决“看见胶片”,产品设计解决“什么时候拍、能不能反悔、照片去哪里”。

小胶片正在寻找画幅
对准 · ALIGN
小胶片快门充能
停稳 · HOLD
小胶片成片确认
揭晓 · REVEAL
D1先让旧研究跑起来。
复活两年前的代码,转换 Core ML,装进 iPhone,再接通实时取景与自动找框。
D2再用真实手机画面修正它。
合成训练、采集 100 张灯箱照片、微调到 2.6 px,让模型适应现场反光与曝光。
UX最后把能力变成可理解的体验。
充能提示让快门可预期,轻晃可以取消;拍完先揭晓,只有用户确认才保存。
AI 编程助手PyTorchCore MLSwiftUI
下一步

谢谢,来试试小胶片

去反光模型 更高分辨率 App Store
小胶片图标

把灯箱,变成一间小暗房。
产品与幻灯片:filmcvt.enhuiz.com

filmcvt.enhuiz.com
← → 翻页