提示

返回博客列表

给视频套滤镜不只是调饱和度:3D LUT、Log 素材与 ffmpeg 调色工作流

有次客户要求把一批素材调成"电影感"。我当时的理解就是调调饱和度、对比度,于是用了 eq 滤镜:

bash ffmpeg -i in.mp4 -vf "eq=contrast=1.1:saturation=1.2" out.mp4

调完发过去,回复是"不对,我要的是那种感觉"。我也说不清哪里不对——画面确实变艳了,但就是不像样片。

后来对方发来一个 .cube 文件,说"套这个 LUT"。我用 lut3d 一加载,立刻就是那个效果。那一刻我才明白:调色不是"把饱和度拉高",而是一套完整的色彩映射。

但直接套 LUT 也踩了坑:素材是 Log 拍的,直接套普通 LUT 出来灰蒙蒙;在 8bit 上调色出现了色带。这篇把这些都写下来。

TL;DR:LUT 是一张"输入颜色 → 输出颜色"的查找表,.cube 是通用格式。ffmpeg 用 -vf lut3d=file=xxx.cube。四个要点:Log 素材必须先转换到显示色彩空间(Log 画面看起来灰,直接套风格 LUT 是错的);在 10bit 上调色再输出(8bit 上做色彩映射会产生色带);顺序是"曝光 → 白平衡 → 对比/饱和 → 风格 LUT → 输出转换";LUT 太重就用 blend 混合原图来减强度。验证用 waveform(波形图)和 vectorscope(矢量图),别只靠眼睛。

目录

一、调色的三个层次

层次 工具 做什么
基础校正 eq(亮度/对比度/饱和度/gamma)、colorlevels(色阶)、colorbalance(白平衡) 把画面"修正常"(曝光准、白平衡准)
曲线调整 curves 精细控制各通道的映射曲线
风格化 LUT(lut3d) 一次性套用一套完整的色彩风格

大多数人以为的"调色"只是第一层——调饱和度和对比度。这能对画面产生影响,但做不到"电影感",因为那是一整套非线性、跨通道的色彩映射(比如把暗部往青蓝偏、亮部往暖橙偏,同时压缩高光)。

这种复杂的映射用 eq 参数是表达不出来的,只能靠 LUT。

二、LUT 到底是什么

LUT = Look-Up Table(查找表):给定输入颜色,查表得到输出颜色。

类型 结构 能表达什么
1D LUT 每个通道一张一维表(R、G、B 各自独立映射) 只能做 gamma、通道独立的调整
3D LUT 三维立方体(比如 33³ = 35937 个采样点) 任意的色彩映射(包括跨通道、饱和度联动)
HALD CLUT 把 3D LUT 编码成一张图片 同上,用图片方式传递

我们用的都是 3D LUT,因为它能表达完整的色彩变换。

.cube 文件格式(文本,可以直接打开看):

# Generated by ...
LUT_3D_SIZE 33
DOMAIN_MIN 0.0 0.0 0.0
DOMAIN_MAX 1.0 1.0 1.0

0.000000 0.000000 0.000000      ← 输入 (0,0,0) → 输出 (0,0,0)
0.030303 0.000000 0.000000
...

LUT_3D_SIZE 33 表示每边 33 个采样点(常见的还有 17、25、65)。采样点越多越精确,但文件越大、计算越慢。33³ 是最常见的平衡点。

重要认知:LUT 是"死的"映射。它不知道你的素材是什么样——如果素材本身曝光不准、白平衡偏了,套 LUT 只会把错误一起放大。所以先做基础校正,再套 LUT。

三、ffmpeg 里怎么用 LUT

基本用法

ffmpeg -i input.mp4 -vf "lut3d=file=filmic.cube" -c:v libx264 -crf 18 output.mp4

用第二个输入的方式(更灵活)

ffmpeg -i input.mp4 -i filmic.cube \
  -filter_complex "[0:v][1:v]lut3d[out]" \
  -map "[out]" -map 0:a -c:v libx264 -crf 18 -c:a copy output.mp4

插值方式

lut3d 有个 interp 参数,决定"落在采样点之间的颜色怎么算":

-vf "lut3d=file=filmic.cube:interp=tetrahedral"
取值 说明
nearest 最近邻,最快,可能有色阶
trilinear 三线性插值,默认,质量和速度平衡
tetrahedral 四面体插值,质量最好,慢一点

建议用 tetrahedral——尤其是对渐变(天空)多的素材,三线性插值可能产生轻微的色带。

其他 LUT 相关滤镜

滤镜 用途
lut1d 应用 1D LUT
lut3d 应用 3D LUT
haldclut 用 HALD 图片做 LUT(有些 LUT 以图片形式分发)
lut / lutrgb / lutyuv 用表达式自定义映射(适合简单的数学变换)

四、Log 素材:最常见也是最大的坑

这是我踩的第一个坑。

Log 曲线(Sony S-Log、Canon C-Log、Panasonic V-Log、ARRILogC)是相机为了保留更多动态范围设计的:把传感器记录的亮度用一条对数曲线"压扁"存储。

所以 Log 素材看起来是灰的、低对比度、低饱和度——这是正常的,信息都在,只是需要"展开"。

错误的做法:直接给 Log 素材套一个"风格 LUT"(比如某个电影模拟 LUT)。

原因:那种风格 LUT 的设计前提是"输入是 Rec.709 的正常画面"。你喂给它一个灰扑扑的 Log 画面,出来的结果就是对比度不够、颜色发闷。

正确的流程:

Log 素材
   ↓ ① 转换到 Rec.709(这一步由"技术 LUT"或色彩空间转换完成)
Rec.709 正常画面
   ↓ ② 基础校正(曝光/白平衡)
校正后画面
   ↓ ③ 套风格 LUT
成片

第①步有两种做法:

做法 A:用厂商提供的"Log → Rec.709"技术 LUT(推荐,最准)

ffmpeg -i log_footage.mp4 \
  -vf "lut3d=file=SLog3_to_Rec709.cube:interp=tetrahedral" \
  -c:v libx264 -crf 18 rec709.mp4

做法 B:用 zscale 做色彩空间转换(没有 LUT 文件时)

ffmpeg -i log_footage.mp4 \
  -vf "zscale=t=linear:npl=100,zscale=p=709:t=709:m=709:r=tv" \
  -c:v libx264 -crf 18 rec709.mp4

(HDR 的 PQ/HLG 素材转 SDR 需要 tone mapping,项目里 HDR 那篇讲过,这里不重复。)

两步合并(技术转换 + 风格 LUT):

ffmpeg -i log_footage.mp4 \
  -vf "lut3d=file=SLog3_to_Rec709.cube,lut3d=file=filmic.cube" \
  -pix_fmt yuv420p10le \
  -c:v libx264 -crf 18 output.mp4

滤镜链按顺序执行——先技术转换,后风格 LUT,这个顺序不能反。

五、位深:为什么 8bit 上调色会断层

第二个坑。

8bit 意味着每个通道只有 256 个可能的值。LUT 的映射是"连续"的,但输出必须四舍五入到 256 个档位之一——在渐变区域(天空、墙面、皮肤),相邻档位之间就会出现可见的跳变,也就是色带(banding)。

Log 素材尤其严重:它原本的动态范围被压缩在 8bit 里,展开之后每个档位代表的亮度跨度很大,色带非常明显。

解决:在 10bit(或更高)上调色,最后再输出目标位深。

ffmpeg -i input.mp4 \
  -vf "lut3d=file=filmic.cube,format=yuv420p10le" \
  -c:v libx264 -pix_fmt yuv420p10le -crf 20 output_10bit.mp4

如果最终要 8bit(兼容性考虑),输出时加抖动(dither)把量化误差打散:

ffmpeg -i input.mp4 \
  -vf "lut3d=file=filmic.cube,format=yuv420p10le" \
  -c:v libx264 -pix_fmt yuv420p \
  -vf "zscale=d=error_diffusion" \      # 注意:不要写两个 -vf
  -crf 20 output_8bit.mp4

(正确写法是把所有滤镜放一个 -vf 里):

ffmpeg -i input.mp4 \
  -vf "lut3d=file=filmic.cube,format=yuv420p10le,zscale=d=error_diffusion,format=yuv420p" \
  -c:v libx264 -crf 20 output_8bit.mp4

抖动的效果:色带变成一层细微的噪点,人眼对噪点的容忍度远高于对色带的——这是"用不明显的噪声换取没有明显色带"的经典手法。

判断有没有色带:看渐变区域(天空、虚化背景),或者用 histogram 滤镜看直方图是否出现"梳齿"。

六、正确的调色顺序

我现在的标准流程(ffmpeg 滤镜链从前到后):

1. 曝光/色阶         colorlevels / eq=brightness
2. 白平衡            colorbalance / colorchannelmixer
3. 对比度/饱和度      eq=contrast:saturation
4. 曲线(可选)       curves
5. 风格 LUT          lut3d
6. 位深/范围转换      format + zscale
7. 锐化(可选)       unsharp

为什么是这个顺序:

  • 前面的步骤是"修正",要在"风格化"之前完成(风格 LUT 会放大一切);
  • 位深转换放最后(中间过程保持高精度);
  • 锐化放最后(锐化色彩映射后的画面,而不是之前)。

完整示例:

ffmpeg -i log.mp4 \
  -vf "lut3d=file=SLog3_to_Rec709.cube,\
colorlevels=rimin=0.02:gimin=0.02:bimin=0.02:rimax=0.98:gimax=0.98:bimax=0.98,\
colorbalance=rs=0.03:gs=0.0:bs=-0.03:rm=0.02:gm=0.0:bm=-0.02,\
eq=contrast=1.05:saturation=1.05,\
lut3d=file=filmic.cube:interp=tetrahedral,\
format=yuv420p10le" \
  -c:v libx264 -pix_fmt yuv420p10le -crf 20 -preset slow \
  -c:a copy output.mp4

注意多行 -vf 的写法:用反斜杠换行时,行首不要有空格(否则会被当成滤镜名的一部分导致解析失败)。这是很常见的错误。

七、LUT 太重怎么办:强度控制

第三个坑:很多风格 LUT 是为特定素材设计的,套到你的素材上可能"太重"(比如暗部压得太死、肤色偏得太厉害)。

ffmpeg 的 lut3d 没有内建的强度参数,但可以用 blend 滤镜把调色结果和原图混合:

ffmpeg -i input.mp4 \
  -filter_complex "\
[0:v]split[a][b];\
[a]lut3d=file=filmic.cube[lut];\
[lut][b]blend=all_mode=normal:all_opacity=0.6[out]" \
  -map "[out]" -map 0:a -c:v libx264 -crf 18 -c:a copy output.mp4

解释:

  • split 把原图复制成两份;
  • 一份套 LUT;
  • blend 把"调色后"和"原图"按 all_opacity=0.6 混合——0.6 就是 LUT 强度;
  • all_mode=normal 表示上层(第一个输入)以不透明度 0.6 覆盖下层。

强度参考:

强度 效果
0.3~0.5 轻微风格化(更自然)
0.6~0.8 明显的风格(常用)
1.0 完整 LUT(可能过重)

我的习惯:先套 1.0 看效果,然后逐步降到 0.7 左右——大多数情况下 0.6~0.8 比 1.0 好看,因为保留了素材本身的色彩特征。

八、性能

lut3d 是逐像素操作,成本不低。实测(1080p,8 核):

配置 处理速度 说明
8bit + trilinear 45 fps 快
10bit + trilinear 28 fps
10bit + tetrahedral 16 fps 质量最好,慢
4K 10bit + tetrahedral 4 fps 很慢,要有耐心

优化建议:

  1. LUT 尺寸:33³ 够用,别用 65³(慢 4 倍,肉眼差别极小);
  2. 插值:不追求极致用 trilinear;
  3. 先缩放在调色前还是后:如果需要输出 720p,先缩放再调色(像素少,快得多)——画质差异可忽略;
  4. 调色和编码合并成一条命令(避免中间落盘:调色输出 raw 再编码会非常慢且占磁盘)。

不要先调色输出一个无损中间文件再编码——1080p 10bit 无损中间文件每分钟几个 GB,磁盘和 IO 都是灾难。一条命令做完。

九、验证:用示波器看,别只靠眼睛

ffmpeg 自带示波器滤镜,这是我后来养成的习惯(输出一张图,看数据而不是凭感觉):

波形图(waveform):看亮度分布和曝光

ffmpeg -i input.mp4 -vf "waveform=m=1:i=0.1:filter=lowpass,scale=1024:512" \
  -frames:v 1 waveform.png
  • 波形压在顶部 → 过曝;
  • 压在底部 → 欠曝;
  • 理想是分布在中下部(暗部有细节、亮部不溢出)。

矢量图(vectorscope):看色彩和饱和度

ffmpeg -i input.mp4 -vf "vectorscope=m=color:i=0.1,scale=512:512" \
  -frames:v 1 vectorscope.png
  • 点越靠中心 → 饱和度越低;
  • 越靠边缘 → 越饱和(可能溢出,广播标准有上限);
  • 皮肤色调应该落在"肤色线"附近(矢量图上通常有标记)。

直方图:

ffmpeg -i input.mp4 -vf "histogram=level_height=256:display_mode=stack" \
  -frames:v 1 hist.png

对比调色前后:把原片和成片的波形图并排看,能立刻确认"暗部有没有被压死""亮部有没有溢出"。这比盯着屏幕猜靠谱得多——眼睛会被整体观感欺骗,示波器不会。

一个实用技巧:抽几个代表性帧(暗场景、亮场景、人物特写)各做一次示波器,批量处理时作为验收依据。

十、坑清单

  1. 用 eq 调饱和度当调色 → 只能做很有限的调整。风格化必须用 LUT。
  2. 给 Log 素材直接套风格 LUT → 灰蒙蒙。先做 Log → Rec.709 转换。
  3. 8bit 上调色 → 色带。在 10bit 上调色,输出 8bit 时加抖动。
  4. 两个 -vf 参数 → 后者覆盖前者,前面的滤镜被丢掉。所有滤镜写在一个 -vf 里。
  5. 多行 -vf 行首有空格 → 解析失败(空格被当成滤镜名)。
  6. LUT 文件路径含空格或特殊字符 → 要转义(file='my lut.cube')。
  7. LUT 插值用 nearest → 渐变区域色阶明显。用 tetrahedral 或 trilinear。
  8. 先调色后缩放 → 慢(对 4K 像素做调色)。先缩放。
  9. 先调色输出中间文件再编码 → 磁盘爆掉。一条命令做完。
  10. LUT 套太重 → 用 blend 混合原图减强度(0.6~0.8)。
  11. 不看示波器只靠眼睛 → 主观判断容易被"变艳了"误导。看波形图和矢量图。
  12. 忽略色彩范围(tv/pc) → limited 和 full 混用会导致发灰或者过曝。确认 color_range。
  13. HDR 素材当普通素材调色 → 需要 tone mapping,不然高光全白。
  14. 批量套同一个 LUT → 不同镜头的曝光不同,套同样 LUT 效果不一致。理想是分镜头调,至少要抽查。
  15. 用网上的 LUT 不看授权 → 很多 LUT 是商业授权的。确认能不能商用。
  16. 调色后直接 -c:a copy 但视频流参数变了 → 这没问题(音频独立),但要确认封装兼容性。

最后说说这次之后我对"调色"的理解变化。

以前我觉得调色是"审美活"——是摄影师/调色师的领域,做技术的插不上手。做完这批之后我明白:调色的审美部分确实在人工,但它的工程部分(色彩空间、位深、LUT 格式、验证方法)完全是技术活,而且后者决定了前者的下限。

具体来说:

  • 色彩空间搞错 → 再好的 LUT 也出不来正确结果;
  • 位深不够 → 一定会出色带,跟 LUT 好坏无关;
  • 没有验证手段 → 你不知道自己的调整是不是"修好了"还是"修过头了"。

所以现在遇到调色需求,我的第一句话是"素材是什么色彩空间、什么位深、有没有 Log",而不是"你想要什么风格"。前者决定了能不能做对,后者只是参数选择。

还有一点:示波器是性价比最高的学习工具。把同一段素材套不同 LUT、不同强度,各出一张波形图并排看,很快就能建立"参数变化 → 画面变化"的直觉。比看十篇教程有用。

想亲手试试?用 VidDown 一键解析下载

粘贴视频链接即可解析,多平台支持、网页端即用;下载桌面客户端解锁海外平台本地解析,开通会员更享不限次下载。

顶部