C++ 为sRGB到CIELAB颜色空间转换生成3DLUT(.3dl文件)

C++ 为sRGB到CIELAB颜色空间转换生成3DLUT(.3dl文件),c++,image-processing,C++,Image Processing,我们的API中已经有一个高度优化的类,用于读取3D Lut(Nuke格式)文件并将转换应用于图像。因此,与其逐像素迭代,并使用复杂的公式将RGB值转换为Lab(RGB->XYZ->Lab)值,不如为RGB到Lab(或XYZ到Lab)转换生成一个查找表。这可能吗 我了解3D Lut如何用于从RGB到RGB的转换,但我对RGB到实验室感到困惑,因为L、a和b的范围不同。有什么提示吗 编辑: 你能解释一下Lut是如何工作的吗? 有一种解释: e、 g以下是我对RGB->RGB变换的3D Lut的理解:

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

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

编辑:

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

e、 g以下是我对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位的smiliar源代码


类似地,您将如何进行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)
vec3 rgbToLab(vec3 rgb) {
    vec3 lab = ...;
    return lab;
}
然后可以创建如下表:

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)
vec3 rgbToLab(vec3 rgb) {
    vec3 lab = ...;
    return lab;
}

请注意,空间消耗是
16M*sizeof(TargetColor)
(假设
Lab
的32位为
64MBytes
),因此应该对表进行堆分配(如果类将位于堆上,则可以将其存储在类中,但最好使用
new[]
在构造函数中,并将其存储在智能指针中)。

假设您的源颜色空间是一个三字节组(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)
vec3 rgbToLab(vec3 rgb) {
    vec3 lab = ...;
    return lab;
}
然后可以创建如下表:

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)
vec3 rgbToLab(vec3 rgb) {
    vec3 lab = ...;
    return lab;
}

请注意,空间消耗是
16M*sizeof(TargetColor)
(假设
Lab
的32位为
64MBytes
),因此应该对表进行堆分配(如果类将位于堆上,则可以将其存储在类中,但最好使用
new[]
在构造函数中,并将其存储在智能指针中)。

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

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

首先,将RGB-to-Lab转换函数移植到GLSL。它应该适用于浮点数,所以如果您在原始转换中使用整数值,请去掉它们。OpenGL使用“钳制”值,即介于
0.0
1.0
之间的浮点值。它将如下所示:

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)
vec3 rgbToLab(vec3 rgb) {
    vec3 lab = ...;
    return lab;
}
然后,写入着色器的其余部分,它将获取(RGB)纹理的像素,调用转换函数并将像素写入颜色输出变量(不要忘记alpha通道):

相应的顶点着色器应在填充整个屏幕(帧缓冲区)的目标四边形的左下角写入
texCoord
(0,0)
,在右上角写入
(1,1)


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

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

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

首先,将RGB-to-Lab转换函数移植到GLSL。它应该适用于浮点数,所以如果您在原始转换中使用整数值,请去掉它们。OpenGL使用“钳制”值,即介于
0.0
1.0
之间的浮点值。它将如下所示:

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)
vec3 rgbToLab(vec3 rgb) {
    vec3 lab = ...;
    return lab;
}
然后,写入着色器的其余部分,它将获取(RGB)纹理的像素,调用转换函数并将像素写入颜色输出变量(不要忘记alpha通道):

相应的顶点着色器应在bot中写入
(0,0)
texCoord