做图像交付这行时间长了,早晚会撞上一次"导出灾难"。我之前接一个产品图库的活,四百多张原片,客户要三种格式:给电商用的 JPEG、给官网用的 AVIF、给后期归档留的 DNG,全都得带统一水印、统一命名。当时图省事,在 LightCraft 里手动选了十几批,中间去泡了杯咖啡,回来发现有两批文件名撞了,后导出的直接把先导出的覆盖掉,等到交付那天才发现少了二十多张,只能连夜重跑。从那以后我就把批量导出当成一条正经的工程流水线来做,而不是点几下按钮的事。
这篇就聊 LightCraft 的批量导出怎么搭。JPEG、AVIF、DNG 多格式导出、水印合成、命名模板这三块,是整条链路里最容易出问题、也最值得花时间调优的部分。如果你只是偶尔导十几张图,随便点也行;但只要你面对的是上百张、上千张、需要重复交付的场景,那这套东西迟早得建起来。下面按我自己的落地顺序讲:先讲整体思路怎么拆,再一个个抠格式参数、水印、命名的细节,最后给一套能直接照着跑的流程和一份排查表。新手能看懂参数为什么这么设,老手能拿走几个能省几小时的坑。
1. 批量导出的整体架构与格式分工
1.1 把"导出"拆成渲染、编码、落盘三段
很多人对导出的理解是"选个格式、点确定",但在批量场景下,一次导出其实是三个完全独立的阶段串起来的:渲染负责把原始数据按输出尺寸重采样、套用色彩变换、做输出锐化;编码负责把像素压成目标格式的字节流;落盘负责命名、建目录、写文件、做校验。这三段拆开之后,好处立刻就出来了。渲染和编码是 CPU 密集的,落盘是 IO 密集的,两者的瓶颈完全不在一个地方,混在一起做就没法优化。
更实际的价值在于失败可重试。四百张图导出到第三百张时,某一张因为色彩配置文件损坏报错,如果三段揉在一起,你只能整批重来;拆开之后,编码失败的只是那一张,其他已经编码好的字节流还在内存或临时目录里,重试时只补那一张就行。我现在的习惯是在落盘阶段给每张图打一个状态标记:pending、encoded、verified,中途断掉重启,程序只看 pending 的往下跑,前面做完的不再碰。这个设计多花不了多少代码,但在大批量场景下能救命。
还有一个容易被忽略的点:拆段之后,同一份渲染结果可以喂给多个编码器。你要同时出 JPEG 和 AVIF,渲染只需要做一次,两张的输出像素完全一致,后面只是走两条不同的编码路径。如果揉在一起做,等于渲染两遍,大批量下这就是实打实的时间翻倍。我实测过一组六千像素长边的图,渲染占总耗时的三成左右,省掉一遍渲染,整体快接近 25%。
1.2 三种格式各自的定位,别混着用
选格式不是"哪个体积小用哪个",而是要看它在链路里扮演什么角色。这三种格式在 LightCraft 的导出面板里是并列的,但用途差别很大,我这边的固定分工是这样:
| 格式 | 主要用途 | 典型体积(6000×4000) | 兼容性 | 可再编辑 |
|---|---|---|---|---|
| JPEG | 通用交付、印刷送审、老系统对接 | 4–8 MB(质量 82) | 几乎全兼容 | 差,有损 |
| AVIF | 网页、App 内嵌、带宽敏感场景 | 1.2–2.5 MB(质量 60 等效) | 新设备好,老设备解码慢 | 差,有损 |
| DNG | 归档、二次修图、客户要"原始数据" | 60–90 MB(无损压缩) | 需专用工具 | 好,接近原始 |
这个表看起来简单,但实际踩过的坑都在细节里。比如有客户说"我要最高质量的图",结果你给了 JPEG 质量 98,体积翻了三倍,他打开一看说跟质量 85 没区别——因为 JPEG 在质量 90 以上基本是给后续再编辑留余量的,终端展示完全用不上。反过来,DNG 当交付格式发给电商运营,对方电脑上根本打不开,最后还是要你补一套 JPEG。所以我的原则是:交付格式跟着使用方的实际能力走,归档格式跟着你自己未来可能的需求走,两者不要互相替代。
AVIF 这一项要多说一句。它的压缩效率确实高,同等观感下比 JPEG 小一半是常态,但编码慢得多,而且部分老旧设备解码会卡顿甚至不显示。我的做法是网页场景一律 AVIF 加 JPEG 双份,用<picture>那种思路做回退,别指望单一格式通吃。至于 DNG,它真正的价值不是"体积大所以专业",而是它保留了线性化之后的完整位深信息和色彩矩阵,后面客户要改白平衡、要重做局部调整,都还有余量。
1.3 批处理的调度模型:并发数怎么定
批量导出最容易拍脑袋的就是并发数。有人直接拉满十六个线程,结果内存爆掉,系统开始疯狂换页,速度反而比四个线程还慢。并发数的上限其实由两个东西卡住:CPU 核数和内存峰值。前者决定编码能并行到多少,后者决定你能同时拿住多少张图的像素数据。
先算内存。一张 6000×4000 的图,内部按 16 位三通道处理,单张占用是 6000 × 4000 × 3 × 2 字节 ≈ 144 MB。渲染阶段可能还要一份中间缓冲,编码阶段(尤其 AVIF)编码器内部还会再要一块,保守按单张 400 MB 估算。如果你开 8 个 worker,峰值就是 3.2 GB,加上系统本身和其他应用,16 GB 内存的机器勉强够,8 GB 的机器必然爆。
并发数的经验公式我一般这么写:worker = min(物理核心数, 可用内存 / 单张峰值 × 0.7)。取 0.7 是给系统留余量,别把内存吃干净。另外要注意,纯 IO 阶段(比如从网络盘读原图、往网络盘写成品)并发可以开得比 CPU 阶段高,因为线程大部分时间在等。如果你的原图和输出都在本地 SSD,那就老实按 CPU 核数来,多开无益。
提示:LightCraft 的导出队列默认会按顺序处理,开启并行后顺序会乱,这时候命名模板里的序号必须依赖独立的时间戳或稳定排序,不能依赖"处理完成的先后",否则每次导出的编号都不一样,后续对账会非常痛苦。
2. 三种格式的核心参数怎么调
2.1 JPEG:质量、色度抽样与元数据的取舍
JPEG 的参数里,质量值是最被滥用的一个。它不是一个线性的"好坏刻度",而是一个量化表的缩放系数。质量从 82 提到 90,体积大概涨 40%;从 90 提到 95,体积再涨 50% 以上,但肉眼在正常观看距离下基本分辨不出。我做过一组对比,同一张产品图放大到 200% 逐像素看,质量 88 和 95 的差别只出现在高对比边缘的轻微振铃上,正常浏览完全看不出来。所以交付用的 JPEG 我一律落在82–88这个区间,印刷送审才提到 92。
色度抽样是第二个关键开关。4:2:0 会丢弃四分之三的色度信息,体积能再省 15%–20%,对风景、人像这类色彩平缓的图几乎无损;但对有大面积纯色边界的内容(比如带彩色文字的截图、有品牌色的产品图),4:2:0 会让色边糊掉。我的做法是:照片类默认 4:2:0,含文字、线条、品牌色的图强制 4:4:4。LightCraft 里可以在预设里按目录打标,别图省事全局设一个值。
渐进式(Progressive)值得开。它让图片在加载时先出模糊轮廓再逐层清晰,网页体验明显好于基线式,体积增加只有 2%–5%。代价是老旧的某些解码器兼容性差一点点,但我这几年没遇到过实际问题。
元数据处理要单独说,因为它是批量导出里最容易出事故的地方。EXIF 里的方向标记必须处理:要么在渲染阶段就把方向"烘焙"进像素然后清掉标记,要么原样保留标记让阅读器自己转。两者混用会导致一部分图横着显示。我的固定做法是渲染阶段烘焙方向、清空 Orientation 字段,这样无论谁拿到图、用什么工具打开,方向都是对的。ICC 配置文件必须嵌入,这个后面单独讲。版权和作者字段看交付要求,给客户的东西一般清空,给自己归档的保留。
2.2 AVIF:编码速度与画质的平衡点
AVIF 的编码器有一个 speed 参数,通常 0–10,数字越大越快、压缩效率越低。这是 AVIF 导出里唯一需要认真调的东西,因为它对耗时的影响是数量级的。我的实测数据(六千像素长边、10 位、4:2:0):speed 4 单张约 9 秒,speed 6 约 3.5 秒,speed 8 约 1.4 秒,而体积差距在 speed 4 和 speed 8 之间大约是 8%–12%。也就是说,你花六倍时间,只换来一成的体积。
批量场景下我的默认值是speed 6–7。理由很直白:批量意味着数量大,总时间的绝对值很敏感,四百张图从 1.4 秒变 9 秒,总耗时从十分钟变成一小时,这个代价换 10% 体积不值。但如果这批图是首屏大图、数量只有几张,那 speed 4 甚至更低完全合理,因为加载速度的收益会被成千上万的访客放大。
位深选择上,8 位够用就别上 10 位。10 位对渐变天空、暗部过渡有实际帮助,能减少色带,但体积会增加 10%–15%,而且部分解码路径对 10 位的支持不如 8 位稳。我的判断标准是:如果原图本身有大量平滑渐变(风光、产品打光背景),上 10 位;如果是对比强烈、色彩块状的图,8 位足够。
色度抽样方面,AVIF 支持 4:2:0、4:2:2、4:4:4。和 JPEG 同理,4:2:0 是体积最优解,4:4:4 用在含文字和锐利彩色边缘的图上。这里有个细节:AVIF 的色彩空间标记必须写对,标成 BT.709 还是 BT.2020 会影响解码后的显示,标错了颜色会整体偏移。LightCraft 默认跟随源,但如果你手动改过工作空间,一定要回头检查这个标记。
还有一个隐蔽的坑:AVIF 的 alpha 通道。如果你的源图带透明,导出时没注意,编码器可能把 alpha 丢掉或者做预乘处理,结果透明区域变成黑边或者白边。带透明的素材导出前先确认一下通道设置,别等交付了才发现。
2.3 DNG:线性化、压缩与色彩矩阵
DNG 这块最容易被误解。很多人以为"导出 DNG 就是把原图另存一份",其实 LightCraft 里导出的 DNG 有两种截然不同的形态,选错了后面全是麻烦。
第一种是未去马赛克的原始 DNG,保留传感器原始数据和拜耳阵列,体积最小,后续在 RAW 处理器里可以重新做去马赛克、重新选算法。第二种是线性 DNG,已经做完去马赛克和色彩变换,存的是线性空间的 RGB 数据,体积大约是前者的三倍,但兼容性好得多,很多不支持拜耳数据的工具也能打开。
我的选择逻辑是:归档留底用原始 DNG,跨工具交付用线性 DNG。因为客户那边大概率用的工具不一定能正确处理拜耳阵列,你给了原始 DNG,他打开发现是灰的或者满屏网格,沟通成本比体积成本高多了。
压缩方式上,无损压缩是必须的,别选未压缩。一张六千像素的 16 位三通道图,未压缩是 144 MB,无损压缩后通常能到 60–80 MB,压缩率四到五成,而且完全无损。LightCraft 里如果有无损 JPEG XL 的选项,压缩率还能再高一些,但要确认接收方能解。有损压缩的 DNG 我不建议用于归档,那等于把"可回溯"这个核心价值给丢了。
色彩矩阵是 DNG 里最专业也最容易出错的部分。DNG 文件里通常要写两组矩阵:ColorMatrix1 对应一个参考光源(一般是 D65),ColorMatrix2 对应另一个(一般是标准 A 光),再加上校准光照。如果这两个矩阵缺失或者写错,接收方打开时白平衡会整体偏,而且很难调回来——因为矩阵错了,等于色彩空间的定义就错了,后面怎么拉曲线都是错的。LightCraft 导出时如果源文件带完整的矩阵信息,会原样写入;但如果你是拼接合成的图、或者从非 RAW 来源转过来的,矩阵可能是空的,这时候要么手动指定,要么干脆别用 DNG 交付。
注意:DNG 里嵌一张预览图是强烈建议的。没有预览的 DNG 在文件管理器里就是一个空白图标,客户翻文件夹时完全不知道里面是什么,体验极差。预览尺寸给到 1024 长边就够,体积增加可以忽略。
2.4 色彩管理与位深链路:最后一步才降到 8 位
这条是我认为整篇里最值得记住的一条。整条链路保持 16 位,直到编码前的最后一刻才降到 8 位。原因很简单:每一次对像素的运算(缩放、锐化、水印合成、色彩空间转换)在 8 位精度下都会累积舍入误差,多次运算之后就会出现色带、暗部断层。而 16 位下这些误差小到看不见。
具体到 LightCraft 的导出流程,我建议的顺序是:读取原始数据(保持原生位深)→ 工作空间转换(线性或 ProPhoto 这类宽色域)→ 缩放重采样 → 输出锐化 → 水印合成 →色彩空间转换到目标空间(sRGB 或 Display P3)→ 降位深到 8 位 → 编码。注意色彩空间转换要放在降位深之前,因为大色域转小色域时,超出色域的像素会被裁剪,这个裁剪在 16 位下做比 8 位下做损失小得多。
渲染意图一般选相对比色。感知意图会整体压缩色域,适合有大面积超色域的图,但会让所有颜色都轻微变化;相对比色只裁剪超出的部分,其余保持不变,适合绝大多数商业交付。黑点补偿建议开启,能让暗部映射更自然。
最后,ICC 配置文件必须嵌进导出文件。我见过太多次"导出后颜色发灰"的投诉,九成是因为文件里没有 ICC,阅读器只能按 sRGB 猜,而你的图其实是在更宽的色域里。JPEG 和 AVIF 都支持嵌入 ICC,DNG 的色彩信息走矩阵不走 ICC,别搞混了。
3. 水印的工程化实现
3.1 尺寸按长边百分比,别用固定像素
水印最容易犯的错是固定像素尺寸。你做了个 300 像素宽的水印,放在六千像素的图上很好看,放在一千像素的缩略图上就糊成一片,占了四分之一画面。批量导出往往同时输出多个尺寸,固定像素必然翻车。
正确做法是按目标图长边的百分比定义水印宽高。我的经验值:文字类水印宽度占长边的 8%–12%,图形类 logo 占 6%–10%。边距同样用百分比,一般是长边的 3%–5%。这样无论输出多大,水印的视觉重量都是一致的。落地时要注意取整和最小尺寸保护:如果算出来只有 40 像素宽,人眼根本认不出,那就该考虑这个尺寸干脆不打水印,或者换一个简化版的水印素材。
还有一个细节是水印要在目标尺寸下重新栅格化,而不是把一个大水印缩下来。矢量素材(SVG)在 LightCraft 里可以直接指定渲染尺寸,出图最锐利;如果只有位图,那素材本身的尺寸至少要达到目标尺寸的两倍,缩下来才不会糊。用一个四千像素的 PNG 缩到 60 像素,效果远比用一个 100 像素的 PNG 原样贴上去好。
3.2 不透明度与可读性:单一颜色必然失效
水印的核心矛盾是:既要不抢主体,又要在任何背景上都看得清。单一颜色的水印做不到这一点——白色水印在浅色背景上消失,黑色水印在暗部消失。批量场景下,你没法给每张图单独调颜色,所以必须用"自适应"的方案。
我常用的两种做法。第一种是双层描边:主体文字用一种颜色,外面套一圈对比色的细描边,描边宽度大约是字号的 4%–6%。这样无论背景深浅,总有一层能形成对比。第二种是半透明衬底:水印下面垫一层圆角矩形色块,透明度 30%–50%,水印本身用高对比色。第二种更稳,但会遮挡一小块画面,适合角落位置。
不透明度的取值范围,我一般落在25%–40%。低于 20% 在亮背景上基本看不见,高于 50% 就开始明显干扰观感。如果是衬底方案,衬底 25%–35%、文字 85%–100%,这个组合在绝大多数图上都能读清。
混合模式要慎用。正片叠底(multiply)在浅背景上好看,但遇到暗背景就直接糊掉;滤色(screen)反过来。批量场景下不要依赖混合模式做自适应,老老实实用描边或衬底,确定性高得多。
3.3 批量下的性能:预渲染与缓存
水印在单张图上合成,成本可以忽略;但在四百张图上,如果每张都重新加载一次素材文件、重新做一次缩放、重新做一次抗锯齿,累积起来就不小了。我测过,一张 6000×4000 的图,水印合成大约占单张总耗时的 8%–12%,其中一大半浪费在重复的素材预处理上。
优化方式很直接:按输出尺寸分组,每组只预渲染一次水印,之后复用。因为同一批导出的尺寸通常只有两三种,水印的栅格化结果完全可以缓存。缓存 key 用「水印素材 ID + 目标宽 + 目标高 + 位置 + 不透明度」,命中就直接贴。这样一改,水印这块的开销基本降到可以忽略。
另一个性能点是合成的实现方式。如果是在主线程逐像素做 alpha 混合,那会很慢;正确做法是把水印渲染成一张带 alpha 的小图,然后用一次带混合的图像叠加操作贴上去,交给底层的图形库去优化。LightCraft 内部走的是这条路,所以你只需要关心素材和参数,不用自己写混合循环。
3.4 位置策略与批量一致性
位置这块看似简单,其实有讲究。九宫格(四角 + 四边中点 + 中心)覆盖了绝大多数需求,但批量导出时,同一批图必须用同一个位置,不能这张放右下、那张放左下,否则客户翻图时视觉上是乱的。
角落位置的选择要看画面主体分布。产品图主体通常在中心偏下,右下角最安全;人像主体偏中上,左下或右下都行;风光图地平线在中间,四个角都相对安全。如果一批图的主体分布很杂,那就选右下角 + 小尺寸这个最保守的组合,被遮挡的概率最低。
还有一个细节:边距要考虑安全区。有些平台会对图片做裁切(比如某些社交平台的预览),如果你的水印贴着边缘 1% 放,很可能被裁掉一半。留 3%–5% 的边距,既美观又安全。另外,水印不要压在画面的关键信息上——比如产品图上的型号文字、人像的眼睛,这类位置即使按规则算出来在角落,也要人工确认一下。
4. 命名模板的设计与冲突处理
4.1 占位符体系:让文件名自己说明一切
命名模板的价值在于让文件名承载元数据。一个设计得好的文件名,不用打开图就知道它是什么、什么时候拍的、什么尺寸、什么格式。我常用的模板长这样:
{project}_{shotdate}_{seq:4}_{longedge}px_v{version}展开后是productA_20240315_0027_6000px_v2.jpg这样的形式。每个占位符都有讲究:
{project}:项目短代号,别用全称,太长影响阅读。注意只允许出现字母数字和下划线。{shotdate}:拍摄日期,格式YYYYMMDD。不要用MMDDYYYY,跨年归档时会乱序。时区要固定,别一半本地时间一半 UTC。{seq:4}:序号,冒号后面是补零位数。位数由总数决定,公式是ceil(log10(N)),N 是这批的总数。一百张需要 3 位,一千张需要 4 位(因为 1000 本身占四位)。位数不够的后果是排序错乱:img_10会排在img_9前面。{longedge}:长边像素数,方便快速筛选不同尺寸的版本。{version}:版本号,客户改稿时递增。这个字段能救命,避免你分不清哪版是最终稿。
序号的排序依据必须明确。先按拍摄时间升序,时间相同再按原始文件名做稳定排序。这里的关键词是"稳定"——排序算法必须是稳定的,否则相同时间的两张图每次导出顺序可能不一样,序号就全乱了。这个坑我在早期项目里踩过,同一批图导两次,序号对不上,对账对到怀疑人生。
4.2 冲突检测与原子写入
文件名冲突在批量场景下不是小概率事件,而是必然事件。原因有三个:序号位数不够导致溢出重复、不同目录下的同名文件被平铺到一个输出目录、以及大小写不敏感的文件系统(某些系统上IMG_01和img_01是同一个文件)。
处理方式分三层。第一层是导出前预检:把整批的目标文件名先算出来,做一次集合去重,发现重复就在开始前报错,而不是导到一半才发现。第二层是写入时加锁:每写一个文件前检查目标是否存在,存在就按策略处理——加后缀、跳过、或覆盖。我一般默认"加后缀",比如_1、_2,绝不默认覆盖。第三层是原子写入:先写到同目录下的临时文件(.tmp后缀),写完、flush、关闭之后再 rename 成目标名。rename 在同一文件系统内是原子操作,这样即使中途断电,也不会留下一个半截的损坏文件。
提示:不要先删旧文件再写新文件。这两步之间有一个窗口期,如果程序在这时候挂掉,你就同时失去了旧文件和新文件。正确顺序是写到临时文件 → rename 覆盖,一步到位。
大小写问题要单独处理。如果你的输出目录可能被同步到大小写不敏感的系统上,那命名模板里最好避免依赖大小写区分,直接统一用大写或统一小写,别混着来。另外,文件名里要过滤掉这些字符:/ \ : * ? " < > |,还有首尾的空格和点。有些系统还会把CON、PRN、AUX这类名字当保留字,虽然概率极低,但批量生成时如果项目代号刚好撞上,会写失败。加个前缀就避开了。
4.3 目录结构与分桶策略
所有文件平铺在一个目录里,在几百张的时候还能忍,上千张就开始难受了,等到几万张,打开文件夹都要卡半天。我的做法是按日期 + 项目两级分桶:输出根目录/20240315/productA/。这样单目录下的文件数控制在几百以内,浏览和批量操作都顺畅。
如果同一批要出多种格式,有两种组织方式。第一种是同目录多扩展名,xxx.jpg、xxx.avif、xxx.dng放在一起,好处是同一张图的所有版本都在一处,方便对照;坏处是文件数翻三倍,而且某些工具在缩略图视图下会显示得很乱。第二种是按格式分子目录,jpg/、avif/、dng/,好处是交付时打包清爽,坏处是要对照得来回切。
我的选择是:交付用按格式分子目录,归档用同目录。因为交付时你往往只给客户某一种格式,分开放打包更省事;而归档时你希望一张图的所有版本都在手边,同目录更方便。
4.4 导出后的自动校验
这一步很多人省掉,但它能挡住八成以上的交付事故。校验的内容我一般做四项:
第一,文件魔数检查。读文件头几个字节,确认真的是 JPEG(FF D8 FF)、AVIF(ftyp后面跟avif)、DNG(TIFF 头II*\0或MM\0*)。有时候编码器出错会写出一堆垃圾字节但扩展名还是.jpg,肉眼看不出来,魔数一查就露馅。
第二,尺寸和位深回读。重新打开文件,确认像素尺寸等于预期的输出尺寸,位深等于设定值。缩放参数写错、位深没降下来,这两个问题都靠这步抓。
第三,ICC 存在性检查。确认文件里真的有嵌入的配置文件,而不是靠阅读器猜。
第四,数量对账。预期输出数量 = 输入数量 × 格式数,实际文件数必须相等。少一个都要查清楚为什么少。
这套校验跑一遍,四百张图大概多花十几秒,但能让你在点"发送"之前就知道东西是齐的。
5. 完整实操流程:从预设到验收
5.1 三套预设的配置清单
我这边固定维护三套预设,覆盖九成场景。参数不是拍脑袋定的,每一项都在前面几节解释过理由:
| 参数 | 交付版(JPEG) | 网页版(AVIF) | 归档版(DNG) |
|---|---|---|---|
| 长边像素 | 3000 | 2000 | 原始尺寸 |
| 位深 | 8 | 8 或 10 | 16 |
| 色彩空间 | sRGB | sRGB | 原生 |
| 质量/压缩 | 85,4:2:0 | speed 7,质量 62,4:2:0 | 无损压缩 |
| 渐进式 | 开 | 不适用 | 不适用 |
| 水印 | 开,右下,长边 10% | 开,右下,长边 10% | 关 |
| 元数据 | 清版权,保 ICC | 清版权,保 ICC | 全保留 |
| 命名模板 | {project}_{date}_{seq:4}_{size} | 同左 | 同左 +_raw |
这张表可以直接抄,但两个地方要根据实际情况改:一是长边像素,看客户的实际展示需求,三千对电商详情页足够,四千以上留给印刷;二是 AVIF 的质量值,62 是一个偏保守的起步值,如果对比后觉得细节不够,可以提到 68 左右,体积会涨一成多。
5.2 一组真实的参数计算
拿一批 120 张原图举例,原始尺寸 6000×4000,要出交付版(3000 长边 JPEG)、网页版(2000 长边 AVIF)、归档版(原始尺寸 DNG)。先把三套的体积估出来:
- 交付版单张按质量 85 估算,3000×2000 大约 1.4 MB,120 张约 168 MB。
- 网页版 2000 长边、AVIF 质量 62,单张约 180 KB,120 张约 21 MB。
- 归档版原始尺寸 16 位无损压缩,单张约 75 MB,120 张约 9 GB。
总体积约 9.2 GB,其中归档版占了 98%。这个比例很说明问题:归档成本远高于交付成本,所以归档格式的选择要更谨慎,别为了"保险"用未压缩,白白多占三四倍空间。
时间估算:渲染单张按 1.5 秒(含缩放、锐化、水印),JPEG 编码 0.3 秒,AVIF 编码 2.5 秒(speed 7),DNG 编码 1.2 秒。单张总计约 5.5 秒。8 个 worker 并行,理论吞吐是单张 0.7 秒左右,120 张约 84 秒。实际会慢一些,因为 IO 和内存带宽会成为瓶颈,我实测这类任务通常是理论值的一点五到两倍,也就是两到三分钟。
5.3 分步执行清单
照着这个顺序走,基本不会出岔子:
- 先导三张样本。挑一张最暗的、一张最亮的、一张有大量渐变的,走完整链路,逐个打开确认颜色、水印、尺寸、格式都对。这一步花两分钟,能省掉后面两小时的重导。样本一定要包含极端情况,只测一张正常曝光的图等于没测。
- 锁定参数预设。把三套预设存好,导出时只选预设不手改。手改是最容易出错的地方,尤其是改了一个参数忘了改另一个。
- 跑命名预检。生成全部目标文件名,做一次去重,确认没有冲突再开始。
- 串行导第一批十张。不要一上来就全量并行,先串行确认整条链路没问题,包括落盘位置和文件权限。
- 开启并行跑全量。观察内存占用,如果持续超过七成,把 worker 数降下来。
- 跑自动校验。魔数、尺寸、ICC、数量四项全过。
- 随机抽检十张。人工打开看,重点看暗部有没有断层、水印位置有没有压到主体、AVIF 在常用浏览器里显示是否正常。
- 打包交付。按格式分目录压缩,附一份文件清单,写清每个目录里是什么。
提示:第 1 步和第 7 步是人工环节,其他都可以自动化。别把这两步也自动化掉,机器判断不了"这张水印压在产品文字上了"这种问题。
6. 常见问题与排查速查表
6.1 高频问题对照
下面这些是这几年反复遇到的,按出现频率排:
| 现象 | 最可能的原因 | 处理方式 |
|---|---|---|
| 导出后整体发灰、发淡 | 文件里没嵌 ICC,阅读器按 sRGB 猜 | 检查导出设置里的 ICC 嵌入开关,确认工作空间转换在降位深之前 |
| 部分电脑上 AVIF 显示不出来 | 解码器不支持或版本太老 | 网页场景补一份 JPEG 回退,别单一格式 |
| DNG 打开后整体偏色 | 色彩矩阵缺失或写错 | 检查源文件的矩阵信息是否完整,非 RAW 来源转 DNG 要手动指定 |
| 文件名序号错乱 | 序号位数不够,或排序不稳定 | 位数按ceil(log10(N))算,排序算法必须稳定 |
| 水印在不同尺寸的图上大小不一 | 用了固定像素尺寸 | 改成按目标长边的百分比定义 |
| 水印很糊、边缘发毛 | 位图素材分辨率不足,或被放大使用 | 素材至少达到目标尺寸两倍,优先用矢量 |
| 批量跑到后面越来越慢、最后卡死 | 内存泄漏或并发过高 | 降 worker 数,观察内存曲线,确认每张处理完释放了缓冲 |
| 进度条走完了但文件不全 | 写入缓冲没 flush,或异常被吞掉 | 用原子写入,加最后的数量对账 |
| 有些图方向是横的 | EXIF 方向标记没烘焙,又被清掉了 | 渲染阶段烘焙方向并清空 Orientation 字段 |
输出目录里出现.tmp残留 | 写入中断,临时文件没清理 | 启动时扫描并清理过期临时文件,别让它们混进交付包 |
6.2 几个不那么常见但很坑的问题
问题一:同一张图导出两次,体积差很多。这个通常是因为编码器用了多线程,而多线程下的码率控制在某些实现里不是完全确定的,导致每次输出略有差异。如果你需要可复现的输出,把编码器设成单线程或者固定随机种子。批量场景下这个差异通常无所谓,但如果你要做前后版本体积对比,就得注意。
问题二:AVIF 在某个尺寸下体积反而比更大的尺寸还大。这不奇怪,编码器的块划分在某些尺寸下刚好不友好,比如长宽比接近 1:1 时某些块的利用率低。遇到这种情况,把输出尺寸微调几个像素(比如 2000 改成 1998)往往就能改善,因为 1998 更容易被 2 的幂整除。
问题三:水印压到了产品文字上。这个纯靠自动规则解决不了,因为"哪里是主体"没法用百分比算。我的做法是在预检环节把带水印的缩略图拼成一张联系人表,一眼扫过去就能发现异常。这个步骤看起来土,但比任何自动检测都靠谱。
问题四:DNG 归档后,几年后发现某个工具的版本已经不支持了。这是一个长期的格式风险。我的应对是归档时同时保留一份高比特率的 TIFF 或者高质量的 JPEG,作为"保底可读"的副本。DNG 是给未来保留编辑余量的,JPEG 是给当下保底能看的,两者不冲突。
6.3 我踩过的三个坑
第一个是并行导出时的序号错乱。早期我以为序号会按输入顺序分配,结果开了并行之后,谁先处理完谁拿小号,同一批图导两次编号完全不同,跟客户对账对到崩溃。后来改成在预处理阶段就把序号按稳定排序写死,处理顺序和编号彻底解耦,问题消失。
第二个是水印在深色背景上隐形的。当时用的是纯白半透明水印,一批图里有几张深色背景的,水印完全看不见,客户直接投诉"没打水印"。改成了描边方案之后,同一套参数在深浅背景上都能读清。这件事让我明白,水印的鲁棒性比美观重要。
第三个是内存没释放导致的越跑越慢。批量导到两百多张时,速度明显下降,最后直接卡死。查下来是每张图的中间缓冲没主动释放,等着垃圾回收,结果回收跟不上分配,内存一直涨。改成显式释放之后,内存曲线平稳,全程速度一致。
7. 几个能长期省时间的小习惯
预设文件要纳入版本管理,每次改动记一笔为什么改。听起来小题大做,但半年后你回头看"为什么这个预设的质量值是 78 而不是 85",没有记录就只能重新试一遍。
归档目录里放一个纯文本的说明文件,写清这批图的命名规则、导出参数、用的什么色彩空间。这个文件只有几百字节,但在交接给别人的时候价值极高,能省掉一堆来回问。
校验脚本不要只跑一次,每次导出都跑。它的成本是十几秒,收益是避免一次几小时的返工。我现在的习惯是把校验做成导出流程的最后一步,不通过就不让打包,物理上杜绝"忘了检查"。
最后一条,也是我觉得最有用的:永远先导三张样本。全套参数配好之后,人总有一种"应该没问题"的乐观,但实际跑起来总会遇到意外——色彩空间的、水印位置的、编码参数的。三张样本的成本是两分钟,能挡住的返工是按小时算的。这个习惯我保持了几年,救过的场次数不清。