【问题标题】:Generating a 3DLUT (.3dl file) for sRGB to CIELAB colorspace transformation为 sRGB 到 CIELAB 颜色空间转换生成 3DLUT(.3dl 文件)
【发布时间】:2013-06-19 10:25:29
【问题描述】:

我们的 API 中已经有一个高度优化的类来读取 3D Lut(Nuke 格式)文件并将转换应用于图像。因此,与其使用复杂的公式逐个像素地迭代并将 RGB 值转换为 Lab (RGB->XYZ->Lab) 值,我认为最好为 RGB 生成一个查找表到 LAB(或 XYZ 到实验室)变换。这可能吗?

我了解 3D Lut 如何用于从 RGB 到 RGB 的转换,但我对 RGB 到 Lab 感到困惑,因为 L、a 和 b 具有不同的范围。有什么提示吗?

编辑:

你能解释一下 Lut 是如何工作的吗? 这里有一种解释:link

例如,以下是我对 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 转换?

【问题讨论】:

  • 源色彩空间中的离散值是可能的(例如,每个通道 8 位是合适的,>= 每个通道 16 位会导致表格非常大)。 目标色彩空间可以根据您的需要精确(它只确定您在表格单元格中写入的多少,而不是表格的大小是)
  • 替代方案:GPGPU(可能与 CUDA、OpenCL 或 GLSL 一起使用)
  • 您不必量化输出。您甚至可以为您的实验室存储三个花车;它只会使表格线性变大。但是,如果您将 8 位 RGB 与 16 位 RGB 进行比较,您的表有 1600 万个与 2.8e14 个条目(我什至不知道这个数字的名称......)所以如果您保留您的 输入信号 用低位数量化(即每通道 RGB 8 位 24 位),生成和使用表格是合适的(见我的回答)。
  • 是的,我不知道为什么我之前感到困惑(我找不到关于 3dlut 文件格式的好文章)。也许我不会使用所有 256 个值作为源 RGB 值,而是使用 17x17x17。
  • 请问您为什么不接受我的回答?有什么问题吗?

标签: c++ image-processing


【解决方案1】:

另一种方法是使用图形硬件,也就是“通用 GPU 计算”。为此有一些不同的工具,例如OpenGL GLSL、OpenCL、CUDA、...与 CPU 解决方案相比,您应该获得大约 100 倍甚至更多的令人难以置信的加速。

最“兼容”的解决方案是使用带有特殊片段着色器的 OpenGL,您可以使用它来执行计算。这意味着:将您的输入图像作为纹理上传到 GPU,使用特殊的着色器程序将其渲染到(目标)帧缓冲区中,该程序将您的 RGB 数据转换为 Lab(或者它也可以使用查找表,但大多数浮点计算在 GPU 上比表/纹理查找更快,所以我们不会在这里这样做)。

首先,将您的 RGB 到 Lab 转换功能移植到 GLSL。它应该适用于浮点数,因此如果您在原始转换中使用整数值,请摆脱它们。 OpenGL 使用“clamp”值,即 0.01.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)

最后,通过在与图像大小相同的帧缓冲区上进行渲染,在您的应用程序中使用此着色器程序。渲染一个填充整个区域的四边形(不设置任何变换,只需渲染一个从二维顶点(-1,-1)(1,1) 的四边形)。将统一值 texture 设置为您作为纹理上传的 RGB 图像。然后,从设备读回帧缓冲区,它应该包含您在 Lab 颜色空间中的图像。

【讨论】:

  • 我想我现在会坚持使用好的 ol' CPU :) 我编写了一个图像变换类,它使用 3x3 矩阵进行色彩空间变换。我使用过 OpenMP,它的速度足以满足我的需要。但是,您不能使用矩阵来近似 RGB -> Lab*。因此查找表。但这听起来很棒。
【解决方案2】:

假设您的源色彩空间是一个三元组字节(RGB,每个 8 位),并且两个色彩空间都存储在名称分别为 SourceColorTargetColor 的结构中,并且您有一个如下所示的转换函数:

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)(假设Lab是32位,这将是64MBytes),所以表应该是堆分配的(如果你的类要存储,它可以存储在类中存在于堆上,但最好在构造函数中使用new[] 分配它并将其存储在智能指针中。

【讨论】:

  • 那么,对于 Lab*,这将如何工作,因为 TargetColor 组件具有不同的范围。
  • 我看不出有什么问题...你们如何存储 Lab 值?只需在我的代码中替换此类型即可...假设您的 Lab 类型称为 Lab,而您的 RGB 类型称为 RGB,则表变量为 Lab table[256][256][256] 和函数 Lab convert(RGB color)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 2010-10-23
  • 2013-01-22
  • 2010-11-13
  • 1970-01-01
相关资源
最近更新 更多