生成用于sRGB到CIELAB色彩空间转换的3DLUT(.3dl文件)
Generating a 3DLUT (.3dl file) for sRGB to CIELAB colorspace transformation
我们的API中已经有一个高度优化的类来读取3D Lut(Nuke格式(文件并将转换应用于图像。因此,与其使用复杂的公式逐像素迭代并将 RGB 值转换为 Lab(RGB->XYZ->Lab( 值,我认为如果我生成一个查找表来为 RGB 到 LAB(或 XYZ 到 LAB(转换会更好。这可能吗?
我了解 3D LUT 如何用于从 RGB 到 RGB 的转换,但我对 RGB 到 Lab 感到困惑,因为 L、a 和 b 有不同的范围。有什么提示吗?
编辑:
你能解释一下LUT将如何工作吗?这里有一个解释:链接
例如,以下是我对RGB->RGB变换的3D LUT的理解:Nuke 3dl Lut 文件示例:
0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1023
R, G, B
0, 0, 0
0, 0, 64
0, 0, 128
0, 0, 192
0, 0, 256
.
.
.
0, 64, 0
0, 64, 64
0, 64, 128
.
.
这里不是为源 10 位 RGB 值生成 1024*1024*1024 表,而是将每个 R、G 和 B 范围量化为 17 个值,生成一个 4913 行表。 第一行给出了可能的量化值(我认为这里只有长度和最大值很重要(。 现在假设,如果源 RGB 值为 (20, 20, 190(,则输出将是行 # 4 (0, 0, 192((使用一些插值技术(。这是对的吗?这个是针对 10 位源的,您可以通过将范围从 0 更改为 255 来生成一个 8 位源?
同样,您将如何进行sRGB->Lab转换?
假设您的源颜色空间是字节的三元组(RGB,每个 8 位(,并且两个颜色空间都存储在分别名为 SourceColor
和 TargetColor
的结构中,并且您有一个如下所示的转换函数:
TargetColor convert(SourceColor color) {
return ...
}
然后,您可以创建如下所示的表:
TargetColor table[256][256][256]; // 16M * sizeof(TargetColor) => put on heap!
for (int r, r < 256; ++r)
for (int g, g < 256; ++g)
for (int b, b < 256; ++b)
table[r][g][b] = convert({r, g, b}); // (construct SourceColor from r,g,b)
然后,对于实际的图像转换,使用替代转换函数(我建议您编写一个图像转换类,该类在其构造函数中接受函数指针/std::function
,因此易于交换(:
TargetColor convertUsingTable(SourceColor source) {
return table[source.r][source.g][source.b];
}
请注意,空间消耗是16M * sizeof(TargetColor)
的(假设 32 位Lab
这将是64MBytes
的(,所以表应该是堆分配的(如果你的类要存在于堆上,它可以存储在类中,但最好在构造函数中使用 new[]
分配它并将其存储在智能指针中(。
另一种方法是使用图形硬件,也称为"通用GPU计算"。有一些不同的工具可以做到这一点,例如OpenGL GLSL,OpenCL,CUDA,...与 CPU 解决方案相比,您应该获得大约 100 倍甚至更高的令人难以置信的加速。
最"兼容"的解决方案是将 OpenGL 与特殊的片段着色器一起使用,您可以使用它执行计算。这意味着:将您的输入图像作为纹理上传到 GPU,使用特殊的着色器程序将其渲染在(目标(帧缓冲区中,该程序将您的 RGB 数据转换为 Lab(或者它也可以使用查找表,但 GPU 上的大多数浮点计算都比表/纹理查找更快,所以我们不会在这里这样做(。
首先,将您的 RGB 到实验室转换功能移植到 GLSL。它应该适用于浮点数,因此如果您在原始转换中使用了整数值,请删除它们。OpenGL使用"钳位"值,即0.0
和1.0
之间的浮点值。它将看起来像这样:
vec3 rgbToLab(vec3 rgb) {
vec3 lab = ...;
return lab;
}
然后,编写着色器的其余部分,它将获取 (RGB( 纹理的像素,调用转换函数并将像素写入颜色输出变量中(不要忘记 alpha 通道(:
uniform sampler2D texture;
varying vec2 texCoord;
void main() {
vec3 rgb = texture2D(texture, texCoord).rgb;
gl_FragColor = vec4(lab, 1.0);
}
相应的顶点着色器应在填充整个屏幕(帧缓冲区(的目标四边形的左下角写入(0,0)
值,在右上角texCoord
写入(1,1)
值。
与图像大小相同的帧缓冲区上渲染,在应用程序中使用此着色器程序。渲染一个填充整个区域的四边形(无需设置任何变换,只需从 2D 顶点渲染一个四边形(-1,-1)
到 (1,1)
(。将统一值texture
设置为您作为纹理上传的 RGB 图像。然后,从设备读回帧缓冲,它应该希望将您的图像包含在 Lab 色彩空间中。