【AI前沿】滤镜背后的色彩科学:LUT 是什么?

2026-07-23

2026年07月22日24 分钟阅读滤镜背后的色彩科学:LUT是什么?主作者关注Nnnobel少数派作者Enchanted to meet u.Nnnobel关注Nnnobel少数派作者Enchanted to meet u.联合作者关注Nnnobel少数派作者Enchanted to meet u.Nnnobel关注Nnnobel少数派作者Enchanted to meet u.Enchanted to meet u.微信扫码分享以图片分享分享到微博点击下方按钮可复制链接分享收藏举报前言莫名其妙的「相机模拟」滤镜如果你也常使用国内的某些移动端修图 App,能看到一个有些诡异的现象:在成百上千个滤镜中,专门出现了一个分类——「相机模拟」。什么时候设备型号也能当滤镜了?当你真的将这些所谓的「滤镜」套在你的照片上时,结果往往是迎合大众在互联网上对这个品牌刻板印象的认知,在某个颜色通道拉高饱和度鲜明度,最后得到一张充满味精感的成品。最幽默的是,笔者将自己用 Pocket 3 拍摄的照片再套上「Pocket 3 模拟滤镜」,得到的是一张蓝到快要溢出的废片。可以看到左下角的「Pocket 3」滤镜,颜色实在是有点诡异。让我们先抛开这些莫名其妙的滤镜,聊回这些频繁出现的品牌。诚然,这些滤镜的效果大多是图一乐。但那些名字之所以能被拿来当作滤镜名称,似乎说明了一件事:我们对不同影像品牌的颜色,有着某种约定俗成的印象。「佳能味」「徕卡德味」「哈苏味」「富士味」……从近年来各大手机厂商喜欢找具有强烈影调风格的相机厂商联名这件事也可见一斑。我们就以颇具代表性,并一直以「HNCS 自然色彩」这一方面进行营销的哈苏来看看。「HNCS」有什么魔力?哈苏的色彩长时间来一直被各大平台的各大博主反复吹捧。如果你经常刷摄影相关内容,应该很容易看到类似的说法:哈苏颜色很「高级」,哈苏直出很「自然」。这些词听起来都很抽象,甚至有些玄学。图源:Hasselblad 官网哈苏对自己色彩表现的官方说法,叫做哈苏自然色彩解决方案(Hasselblad Natural Colour Solution,简称 HNCS)。看起来很高深,对吧?但如果你仔细顺着哈苏官网对 HNCS 的介绍页看下去,会发现哈苏其实很直接:Hasselblad’s understanding of natural colors brings together HNCS. It includes an independently developed Hasselblad look-up-table (LUT), Hasselblad Film Curve, and unique color processing that together adapts to any illumination. The color data undergoes a series of transformations that remap the captured values. This ensures true contrast, rich saturation, and tricky subtle tones – like skin tones – are kept smooth, even between highlights and shadows.翻译过来大意是:HNCS 的核心,是一张自主研发的哈苏颜色查找表(LUT),配合哈苏胶片曲线以及独有的色彩处理流程,共同完成对不同光照条件的适配。这其中主要起作用的就是 LUT,即本文将要讨论的对象。Look-Up TableLUT,全称 Look-Up Table,翻译过来就是「查找表」。这个词本身其实不是影像圈发明的,稍微学过一点编程的人应该更熟悉。查找表是计算机数据结构里一种常见的优化手段,思路很简单:与其每次都临时计算一个结果,不如提前把所有可能的输入和对应的输出算好,存成一张表,简单的查询操作替换运行时计算的数组或者哈希表,牺牲一定的空间效率换取时间效率。从内存中提取数值经常要比复杂的计算速度快很多,能省去重复运算的开销。你也可以把 LUT 简单理解为一个离散化的函数(即不连续,有限个采样点),比如下面这个例子,提供一个输入值,经过 LUT 后输出映射值:输入(x)输出(x²)112439416525更准确地说,LUT 是把某个函数的结果提前采样、离散化并存起来。使用时,它不再重新计算完整函数,而是通过查表,得到输出结果。放到图像处理这件事上,逻辑是一样的。一张 LUT,本质上就是一份「颜色到颜色」的映射关系,将索引号与输出值建立联系:给定特定图像中每一像素的输入颜色数据,去表里查该颜色数据对应映射的是哪一个新的颜色数据,就能拿到对应的输出颜色。简单的套 LUT 流程示例1D LUT对于灰度图像来说,一个 1D LUT 的结构和上述演示Look-Up Table 的例子十分类似,是一个一对一的列表。我们设所使用的图像为8-bit 色深(颜色深度,表示一个像素点中可以显示的色阶数) ,有 2⁸ = 256 阶灰度值,即 0 - 255。同样,我们举一个简单的例子:输入输出1325……254249255251只要将每个像素的灰度值进行替换就能完成。这类的1D LUT ,可以方便地调整图像的Gamma 值、RGB 平衡、白场等基础响应关系。RGB 的 1D LUT上述讲的是灰度图像的情况,只有一个通道需要处理。彩色图像会复杂一层——每个像素由 R、G、B 三个数值组成,如果想用 1D LUT 的思路来处理彩色图像,最直接的做法就是把这套逻辑分别应用在 R、G、B 三个通道上,各自独立地进行处理。用简单的伪代码演示,我们也能看出这种分通道处理的逻辑(代码中的r_lut、g_lut、b_lut 为 R、G、B 三个通道对应的颜色查找表):r_lut = [0, 2, 5, 9, …, 250, 255]

长度 256,索引是输入值,元素是输出值

g_lut = [0, 1, 4, 8, …, 248, 255] b_lut = [0, 3, 6, 10, …, 252, 255]

def apply_1d_lut(pixel): r, g, b = pixel return (r_lut[r], g_lut[g], b_lut[b])每个通道的映射规则,只取决于这个通道自己的输入值,跟另外两个通道当前的输入值无关。三个函数各自独立,互不耦合。这种结构的存储成本很低——三张长度 256 的表,一共 256 × 3 = 768 个数据点,就能覆盖 8-bit 图像里每个通道所有可能的输入值,连插值都不需要,因为每一个整数输入都有精确对应的输出。「曲线」中的 1D LUT你其实早就在用 1D LUT 了。修图软件里的「曲线」工具,本质上操作的就是这张表——你在曲线上往上拖一个点,相当于往函数里改写了一条映射规则。这里使用Pixelmator Pro中的「颜色调整」功能进行演示。Pixelmator Pro 编辑界面可以看到左侧「曲线」模块中鼠标指针附近有一个浮窗:「in:141   out:187」,「in」也就是颜色输入值,「out」则是输出值。但 1D LUT 有个写在结构里的「硬伤」。前文谈到了 1D LUT 中 R、G、B 三个颜色通道使用的是三个独立的查找表,「独立」也就意味着他们不能够相互影响。而对于一张由较为复杂颜色画面组成的图像来说,我们无法精细地调整每一种颜色的状态,难以实现一些颜色效果的表达。依旧举个具体的例子,皮肤颜色和天空颜色,在 RGB 数值上可能是这样的:RGB肤色220170150天空色130170220注意两者的 G 分量完全一样,都是 170。如果你想通过 1D LUT 里的 f(G​) 函数去调整肤色的绿色分量,天空里数值相同的绿色分量会被一起改动——因为 f(G​) 根本不知道这个 170 来自谁,它只会同时对 G 值为「170」的像素进行映射更改。要解决这个问题,必须让输出结果同时依赖 R、G、B 三个输入值。3D LUT这里 R、G、B 的不再是三个独立函数,而是一个统一的三元函数——这就是 3D LUT 要实现的东西。它的数据结构,可以理解成一个三维数组。 我们用伪代码演示:# 一个 3x3x3 的采样示例(真实场景通常是 17x17x17 或 33x33x33 或 65x65x65)

结构映射:lut_3d[r_idx][g_idx][b_idx] = (输出R, 输出G, 输出B)

lut_3d = [[[None for _ in range(3)] for _ in range(3)] for _ in range(3)]

lut_3d[2][1][1] = (225, 165, 145) # 特定(R,G,B)组合专属的映射结果从下图中我们可以很直观地看到,3D LUT在三维坐标下如同一个立方体。坐标系上的X、Y、Z三个维度分别对应着 R、G、B 三个颜色通道,与1D LUT不同的是,在这个坐标系下的三个颜色通道会相互影响。软件:Lattice回到刚刚1D LUT中的例子,肤色 (220,170,150) 和天空蓝 (130,170,220) 虽然共享同一个 G 分量(170),但作为完整的三元组,它们在这个立方体里对应的是两个完全不同的位置,可以被分别赋予不同的映射结果 —— 这正是 1D LUT 做不到的事。3D LUT的实际存储方式如果你对数字有些敏感度,不难留意到,如果我们按照一开始1D LUT的数据存储方式存储 3D LUT,空间复杂度将会飙升。回顾一下,1D LUT 处理的是单个通道,三个通道各自独立处理,三张表加起来也不过 768 个数据点,用大 O 表示法来说,空间复杂度是 O(n),对现代计算机的存储空间来说几乎可以忽略不计。但 3D LUT 处理的是 R、G、B 三个维度的组合。如果照搬 1D LUT 那种思路 —— 也就是不做任何采样压缩,直接对 R、G、B 三个通道的全部取值组合建表,空间复杂度就从 O(n)变成了 O(n³)。代入 8-bit 图像的实际数字:256³ = 16,777,216。一千六百多万条映射记录。哪怕每条记录只用最省空间的方式存(比如每个输出通道用 1 字节表示,一条记录 3 字节),整张表也要占用大约48MB 的空间。这还只是 8-bit 的情况。前面提到的哈苏的 HNCS 处理的是 16-bit 色深数据 —— 如果我们同样全量存储每一个数据点的值,三通道组合数会飙升到约 280 万亿种可能性,所需要的存储空间大约为1536 TB。采样与精度上述的方案显然不现实,我们不愿意也做不到付出如此大的代价,只是为了改变影像的影调。因此,我们应该考虑使用「采样」的方式,按选取的精度在固定间隔上取有限个采样点。常见的精度规格是 17×17×17、33×33×33 与 65×65×65。17×17×17 :这一档精度主要出现在现场监看的场景中,比如摄影机内置的 LUT 槽位、便携监视器等。由于硬件本身的算力和内存都有限,且拍摄现场需要的是「实时」而不是「精确」,只需要大致判断曝光和颜色,主要用作参考用途而非成片的调色。33×33×33 :这则是后期制作行业里最为广泛使用的标准,是在调色精度与文件体积间寻找到的平衡点。DaVinci Resolve、Premiere Pro、Final Cut Pro 等主流调色软件默认导出规格基本都是这一档,网上能下载到的官方还原 LUT 或者胶片模拟 LUT,绝大多数也停留在这个规格。65×65×65:这主要是留给对精度要求最苛刻的场景使用,如电影最终成片精修、HDR 和广色域项目等。不过对大多数消费级素材和普通显示设备来说,这一档精度带来的收益已经超出了肉眼能感知的范围,很少在日常场景里被用到,大部分使用场景为院线电影或专业视频制作。插值既然 LUT 只记录了一部分的采样点,那没被