全部作品

01 / 04

独立产品 · 开源

2026.08 – 今

SpektraLab

不模仿胶片的结果,模拟胶片的过程。

一款 macOS 上的物理胶片模拟与数字暗房应用。打开一张 RAW,选好胶片和相纸,约一秒得到一张按物理过程建模的照片。我一个人完成了市场调研、产品定义、界面与品牌、引擎移植、打包分发和冷启动,并根据用户反馈持续迭代。

角色
独立完成
周期
2026.08 – 今
平台
macOS 15 及以上,Apple 芯片;iPhone 版在 TestFlight 公测
技术
SwiftUI · Metal · C++20
许可
GPL-3.0-or-later
SpektraLab 1.3.3 的窗口:左栏选中 Kodak Portra 400 胶片与 Kodak Professional Portra Endura 相纸,中间是带 6×9 片边的曼哈顿下城暮色,右栏是显影前的参数
SpektraLab 1.3.3 · macOS曼哈顿下城 · Kodak Portra 400 · Kodak Portra Endura
4500 万像素出图。参考实现约 30 s
1.13s
1 亿像素峰值内存。首个版本为 27.4 GB
13.7GB
导出文件与屏幕软打样的差异
0px
与参考实现的最大误差
1.1 × 10−5
GitHub,截至 2026.10.19
218Stars
首发后 21 天内发布
15个版本

01缺口Gap

能用的不真实,真实的不能用。

比较项RGB LUT 滤镜物理模型 spektrafilmSpektraLab
怎么算输入三个数,查表,输出三个数81 个光谱波段,21 个节点,实测的胶片与相纸数据同一个物理模型,原生移植到 C++ 与 Metal
得到什么快,容易上手,能长得像换一卷胶片、多给一档曝光,颜色的变化有物理依据物理真实,并且能实时拖动滑杆
代价回答不了「颜色为什么这样变」只有 Python 命令行;4500 万像素一张约 30 秒Mac 版只支持 Apple 芯片;iPhone 版仍在公测

摄影师不需要懂光谱,只需要懂暗房。产品要做的,是把正确的物理放在熟悉的操作后面。

02定位Positioning

数字暗房,不是图库。

做

  • 按暗房流程组织:进片、胶片曝光、显影、印相、呈现
  • 任何参数改动都能实时看到
  • 导出的成片,等于屏幕上确认过的样子

不做

  • 图库与素材管理
  • 复刻化学操作台
  • 一键滤镜、玻璃拟态、触屏尺寸的控件

参照:Capture One 的专业工作流与工具栏;Dehancer 连续的胶片模块。

03过程Process

一张照片,走完 21 个节点。

场景的光先被还原成光谱,再依次经过胶片的感光、显影、成色剂耦合与颗粒,由放大机印到相纸上,最后在标准光源下被「看」出来。每一步都是一个有物理意义的方程,界面上的每个控件都对应其中一步。

优胜美地山谷的冬季清晨,经 Kodak Portra 400 胶片与 Portra Endura 相纸显影后的成片 同一张照片未经显影的 RAW 解码结果,色彩平淡
拖动对比Yosemite Valley, CAKodak Portra 400 · Kodak Portra EnduraNikon 45.7 MP NEF
输入:线性 ProPhoto RGB 图像
01输入线性 ProPhoto RGB,每个像素 3 个数
光谱上采样:同一画面按波长从 400 到 720 纳米切成 17 条
02光谱上采样还原为 380 至 780 nm 的 81 个波段。图中是其中 17 个
胶片感光:感蓝、感绿、感红三层乳剂各自的曝光
03胶片感光感蓝、感绿、感红三层乳剂各自曝光,加入光晕
负片:带橙色片基的彩色负片,放在 D50 灯箱上观看
04负片特性曲线、成色剂耦合与颗粒,得到负片密度
相纸印放:相纸上青、品红、黄三层染料的密度
05相纸印放放大机把负片印到相纸的青、品红、黄三层染料上
成片:在 D50 标准光源下观看的照片
06成片在 D50 光源下观看,做一次色域映射后输出

81 个波段上的 Kodak Portra 400

引擎里的实测光谱感光度:感蓝、感绿、感红三层乳剂,各自对哪一段光敏感。380 至 780 nm,每 5 nm 一个波段。

感蓝层感绿层感红层

预处理 · 3
input_cast → auto_exposure → crop_rescale
胶片 · 8
upsample → exposure → boost → halation → log → curves → dir_couplers → grain
印相 · 3
enlarger_spectral → print_exposure → print_curves
观看 · 7
scan_spectral → bw_correction → glare → xyz_to_rgb → gamut_compress → unsharp → cctf

04引擎Engine

先定验收,再写实现。

8 月 24 日的第一批提交不是功能,而是一套 1600 万像素的基线、一个视觉等价测试框架和 RFC-001。之后 21 个节点从 Python 逐个移植到 C++ 与 Metal,每个节点都与参考实现对拍。性能和内存不是感觉,是写进基线的数字。

峰值内存,按照片像素数

单位 GB,越低越好。M3 Max 实测;RAW 分别来自 Hasselblad CFV 100C、Nikon Z7 II、Sony α7 III。

v0.3.1,首个公开版本v1.0.3,两天后

1 亿像素102 MP 27.4 13.7 −50%
4500 万像素45 MP 9.3 7.0 −24%
2400 万像素24 MP 5.6 4.1 −26%

取舍:对齐参考实现,还是对用户正确?

引擎移植 · 已知差异

现象
参考实现在构建计算图时,把「镜头模糊」当成无效节点剪掉了。用户拖这个滑杆,什么都不会发生。
约束
移植版必须能和参考实现逐节点对拍,任何差异都要能解释。
洞察
对拍是为了守住物理,不是为了复制缺陷。「一致」不等于「正确」。
方案
每次运行时按实际像素尺寸计算模糊半径,让参数真正生效。
验证
对拍测试把这里登记为「已知差异」,而不是把它「修」成一致。
结果
界面上的每个滑杆都真的做了点什么。差异有记录,可追溯。

05界面与身份Interface, identity

控件在哪一侧,说明移动它的代价。

左栏回答「照片如何形成」:胶片、画幅、相纸,改这里等于重新显影一次。右栏回答「成片长什么样」:白平衡、曲线、色彩平衡,改这里只是重新绘制。信息架构本身就在告诉用户,每一次调整要付出多少。

三种「变亮」,三个位置

胶片曝光、印相曝光、后期调色,物理上是三件事。所以名字不同,位置不同,重算的代价也不同。

一个「120」,四种画幅

由画幅类型、对齐边与照片比例,推导出 645、6×6、6×7、6×9。用户不必选四次。

裁切不重算颗粒,默认关

这也是物理上正确的答案:裁切一张底片,颗粒不会变细。

第一版界面参考图:圆角卡片堆叠,橙色选中态,编号步骤
第一版 · 否决AI 生成的参考图。圆角卡片堆叠,大面积橙色选中,仪表盘式的层级。它在装饰界面,而不是让位给照片。
第二版设计稿:石墨底色,发丝线分隔,照片占据中心
第二版 · 我画的设计稿石墨底色,1 pt 发丝线就是栅格,一点琥珀色表示激活。以这一张 1920 × 1080 的稿子为唯一事实源,推导全部设计 token。
上线的 SpektraLab 1.3.3 界面
成品 · 1.3.3在三种窗口尺寸下逐项量测,与设计稿的差异写进文档。照片是主角。
SpektraLab 图标:蓝、品红、黄三层染料叠在深色片基之上
三层染料,叠在片基之上。图标是一张彩色负片的截面。名字里的 Spektra 是光谱,Lab 是暗房。品牌、图标和界面讲的是同一件事:照片是怎么形成的。
Portra 400 颗粒的原始像素裁切:酋长岩的岩壁与天空
颗粒,原始像素裁切颗粒按画幅以微米为单位计算

06发布Launch

一级一级建立信任。

冷启动的内容不是「滤镜好看」,而是把过程本身当卖点。每一篇只证明一件事,从审美上的共鸣,一步步走到专业上的可信。渠道是小红书、知乎、GitHub 与官网 spektralab.app;Mac 安装包放在 GitHub Release 和国内网盘,iPhone 版通过 TestFlight 公测。

01 · 先被看见 小红书封面:SpektraLab,物理真实的胶片模拟应用
首发一张图讲清主张:照片由三层叠成
02 · 证明好看 小红书封面:样片日记 01,海纳百川
样片日记成片说话,统一的图注格式
03 · 证明是真的 小红书封面:从 spektrafilm 到 SpektraLab,高效的胶片模拟引擎
引擎原理RGB 只有三个数,还原成 81 个波段
04 · 证明能用 小红书封面:SpektraLab 1.0.3,优化的内存管理
内存对比1 亿像素,峰值减半
05 · 证明可靠 小红书封面:6 万行代码,近三分之一是测试
代码与测试再加一篇知乎长文:一张照片走完 21 个节点
小红书首发周末的互动数
1,000+
GitHub,另有 11 个 Fork。截至 2026.10.19
218Stars
上架平台的累计下载量。截至 2026.11.08
54,557次下载

发版节奏:21 天,15 个版本

每个圆点是一次公开发布,按北京时间的日期堆叠。标出的是每个新版本线的第一天。

0.3
1.0
1.1
1.2
1.3
以表格查看全部 15 次发布
版本发布时间(北京时间)备注
v0.3.12026.09.19 01:17首个公开版本
v1.0.12026.09.20 18:22
v1.0.22026.09.20 20:08
v1.0.32026.09.20 23:18内存管理,1 亿像素峰值减半
v1.0.42026.09.21 20:06更新检查
v1.0.52026.09.22 10:45快捷键与界面缩放
v1.1.02026.09.26 16:43新界面,场景宽容度与反差遮罩
v1.1.12026.09.26 17:42命令行与 MCP 服务器
v1.2.02026.09.29 11:34数字中间片导出,设置剪贴板
v1.2.12026.09.29 13:29
v1.2.22026.09.30 01:51
v1.3.02026.10.04 19:05片边、全景画幅与半格拼接
v1.3.12026.10.05 01:03
v1.3.22026.10.06 16:35
v1.3.32026.10.09 19:46黑白胶片;首个经 Developer ID 签名、Apple 公证的版本

07迭代Iteration

反馈不直接变成功能,先变成一个约束问题。

收到的信号来源处理记录
1 亿像素的 RAW 占满 24 GB 内存已发布版本的用户报告修掉缓冲池的问题,并砍掉原定的后续方案RFC-020
「JPG 进去,效果比 RAW 好」用户原话不改实测数据,拆成两个暗房里真实存在的操作RFC-023、024
一天发了三个版本,用户不知道自己是不是最新发版节奏一个按钮,一次请求,一个外链。不做自动更新,不做遥测RFC-021

案例 A:一亿像素,24 GB 内存

RFC-020

现象
用户报告:哈苏 X2D 的 16-bit RAW(101.9 MP)在 24 GB 内存的 Mac 上占到 23 GB。
约束
用户机器的内存是硬上限;画质零损失是硬门槛。新方案的输出必须与原来逐位一致,否则不发。
洞察
大头不在渲染本身。缓冲池在持有 13.1 GB 已经没用的缓冲时,还在继续申请新的。
方案与验证
修复后峰值从 28.9 GB 降到 19.1 GB,输出逐位一致;打包版实测,1 亿像素峰值 13.7 GB。
反转
原计划的条带渲染,在这些优化之后实测反而更差:9,914 MB 对 9,571 MB。我砍掉了已经规划好的下一篇 RFC。
结果
1 亿像素的中画幅用户可以在 24 GB 的 Mac 上工作。他们正是最专业的那一批人。

案例 B:「JPG 进去,比 RAW 好看」

RFC-023 · RFC-024

现象
用户原话:「jpg 进去效果比 raw 好,因为曝光的原因」。
约束
不能改实测的胶片与相纸数据。那是「物理真实」的根基。
洞察
这不是 bug,而是对产品主张的一次测量。相机 JPEG 在机内已被压到 5 至 8 档,接近相纸的宽容度;RAW 带着 12 至 15 档,直接进了胶片模型。
方案
拆成两个暗房里真实存在的操作:曝光时控制光比,印相时做反差遮罩。两者互不偷偷开启对方。
验证
在真实 RAW 上跑完设计。三处假设没有通过测量,已在文档里原地更正。
结果
用户的困惑变成暗房语言里的两个工具,而不是一个「自动修复」滑杆。

08工作方式Method

人保留判断,Agent 负责执行。

我保留

  • 需求与范围:什么做,什么不做
  • 架构取舍与性能预算
  • 验收:什么才算「好了」

用来约束 Agent 的

  • RFC 与 PRD,固定产品定义与技术路径
  • 参考实现对拍、回归、延迟与内存基线
  • 真实窗口截图验收:测试全绿,不等于用户看到的是对的
篇 RFC
30
个测试用例
560
次提交,自 2026.08.24 起
400+
代码,其中近三成是测试。2026.10.09 统计
8.7万行

8.7 万行,按用途拆开

单位:行,含空行与注释。统计于 v1.3.3(2026.10.09),工具为 cloc 2.10。只计 macOS 产品线里自己写的代码;不含 Windows 与 Linux 移植、第三方库、生成文件与数据。

产品与构建测试

Swift 应用SwiftUI / AppKit 36,106 41.6%
C++ 引擎21 个节点的移植 16,652 19.2%
Metal 着色器GPU 内核 2,799 3.2%
CLI / MCP脚本与 Agent 入口 1,465 1.7%
测试XCTest 与对拍 24,442 28.2%
构建脚本打包、基线与工具 5,322 6.1%
以表格查看文件数、行数与有效代码行数
部分文件行有效代码(不含空行与注释)
Swift 应用9936,10621,965
C++ 引擎4716,65211,059
Metal 着色器102,7991,930
CLI / MCP71,4651,163
测试8724,44216,821
构建脚本375,3223,674
合计28786,78656,612

09样片Samples

照片是主角。

以下照片由我拍摄,全部用 SpektraLab 显影。图注格式在所有物料里保持一致:地点、胶片、相纸。

哈德逊河对岸的曼哈顿下城,蓝调时刻,两束纪念光柱射向天空
New York, NYPortra 400 · Portra Endura
南澳岛退潮的滩涂上停着几艘渔船
Nan’ao Island, ChinaPortra 400 · Portra Endura
旧金山唐人街,大明星戏院的招牌与街道
San Francisco, CAPortra 400 · Portra Endura
优胜美地山谷,雾中的酋长岩与结霜的树
Yosemite, CAProvia 100F
旧金山圣母升天主教座堂,日落时分的混凝土立面
San Francisco, CAPortra 400 · Portra Endura

10致谢与下一步Credits, next

胶片过程模型、28 款实测的胶片与相纸配置,以及放大机 LUT,来自 Andrea Volpato 的开源项目 spektrafilm(GPLv3;配置数据为 CC BY-SA 4.0);4 款黑白胶片的配置由我建立。SpektraLab 是这个引擎的原生 C++ 与 Metal 移植,以及围绕它构建的应用。产品定义、界面、移植、品牌与发布由我完成。

命令行与 MCP 服务器已随 v1.1.1 发布,Agent 用和窗口同一套代码修图。接下来:iPhone 版正在 TestFlight 公测;Windows 开发版已经进入仓库,使用 C++ 与 Vulkan 后端。