如何使用 3d lookup table 并将其像素值映射到图像?

How to use 3d lookup table and map its pixel value to an image?

我正在尝试将 png 颜色 table 应用于图像,但无法将 png 中的像素与目标图像相匹配。

颜色table是64^3的png

根据我的理解,大图像中的每个像素都需要在颜色中使用相似的值table。这似乎将颜色限制为 262144 = 64 x 64 x 64。但我不确定这是我得到的效果,结果是一个完全黑色的图像,这意味着没有价值,或者颜色看起来很奇怪。

这是我的代码


// The table is 64 x 64 x 64
float size = 64.0;

// This is the original image
// This function returns a pixel value inside a 3d space
//  and the `rgb` method will return a vector with the rgb values
vec3 source_image = sample(src_i, samplerCoord(src_i)).rgb;
// Here I take the pixel value of the image for the red channel
//  and multiply it by 64.0, then divide by 255.0 for the 8-bit image
float x = floor(floor(source_image.r * size)/255.0);
// The same thing for the green value on the y axis
float y = floor(floor(source_image.g * size)/255.0);

// Match a value from the image in the color table
vec3 color = sample(src_l, vec2(x, y)).rgb;

src_i.r = color.r;
src_i.g = color.g;
// The blue should be on the z axis, or the nth tile, so I think for this
//   case it will be easier to convert the color table to one long row
src_i.b = floor(floor(source_image.b * size)/255.0);

// The image is black

原图

预期结果

如果我乘以 255(这似乎是正确的),那么我得到这个结果

float x = floor(source_image.r * 255.0);
float y = floor(source_image.g * 255.0);

如果你能指出数学上的错误,我将不胜感激

查找table不是64*64*64,而是8*8光栅中的64*64。 texture2D读取的颜色通道在[0, 1]范围内,纹理坐标也在[0, 1]范围内。

vec2 tiles    = vec2(8.0);
vec2 tileSize = vec2(64.0);

vec3 imageColor = texture(src_i, samplerCoord(src_i)).rgb;

图块的索引被编码到蓝色通道。有 64 个图块,第一个图块的索引为 0,最后一个图块的索引为 63。这意味着范围 [0, 1] 中的蓝色通道必须映射到范围 [0, 63]:

float index = imageColor.b * (tiles.x * tiles.y - 1.0);

必须计算范围 [0, 8] 内的二维图块索引,以此线性图块索引计算:

vec2 tileIndex;
tileIndex.y = floor(index / tiles.x);
tileIndex.x = floor(index - tileIndex.y * tiles.x);

纹理缩小功能(GL_TEXTURE_MIN_FILTER) and texture magnification function (GL_TEXTURE_MAG_FILTER should be set to GL_LINEAR。这导致每个图块上的颜色可以线性插值。
每个图块都有 64x64 纹素。左下纹素的相对坐标为(0.5/64.0, 0.5/64.0),右上纹素的相对坐标为(63.5/64.0, 63.5/64.0)。
范围 [0, 1] 中的红色和绿色通道必须映射到范围 [0.5/64.0, 63.5/64.0]:

vec2 tileUV = mix(0.5/tileSize, (tileSize-0.5)/tileSize, imageColor.rg);

最终可以计算出在 [0, 1] 范围内查找 table 颜色的纹理坐标:

vec2 tableUV = tileIndex / tiles + tileUV / tiles;

在片段着色器中解码颜色的最终代码可能如下所示:

vec2 tiles    = vec2(8.0, 8.0);
vec2 tileSize = vec2(64.0);

vec4 imageColor = texture(src_i, samplerCoord(src_i));

float index = imageColor.b * (tiles.x * tiles.y - 1.0);

vec2 tileIndex;
tileIndex.y = floor(index / tiles.x);
tileIndex.x = floor(index - tileIndex.y * tiles.x);

vec2 tileUV = mix(0.5/tileSize, (tileSize-0.5)/tileSize, imageColor.rg);

vec2 tableUV = tileIndex / tiles + tileUV / tiles;

vec3 lookUpColor = texture(src_l, tableUV).rgb;

可以通过在 table 的 2 个图块之间进行插值来进一步改进该算法。计算蓝色通道下方图块的索引和蓝色通道上方图块的索引:

float index     = imageColor.b * (tiles.x * tiles.y - 1.0);
float index_min = min(62.0, floor(index));
float index_max = index_min + 1.0;

最后使用 mix 函数在两个图块的颜色之间进行插值:

vec3 lookUpColor_1 = texture(src_l, tableUV_1).rgb;
vec3 lookUpColor_2 = texture(src_l, tableUV_1).rgb;
vec3 lookUpColor   = mix(lookUpColor_1, lookUpColor_2, index-index_min); 

最终代码:

vec2 tiles    = vec2(8.0, 8.0);
vec2 tileSize = vec2(64.0);

vec4 imageColor = texture(src_i, samplerCoord(src_i));

float index     = imageColor.b * (tiles.x * tiles.y - 1.0);
float index_min = min(62.0, floor(index));
float index_max = index_min + 1.0;

vec2 tileIndex_min;
tileIndex_min.y = floor(index_min / tiles.x);
tileIndex_min.x = floor(index_min - tileIndex_min.y * tiles.x);
vec2 tileIndex_max;
tileIndex_max.y = floor(index_max / tiles.x);
tileIndex_max.x = floor(index_max - tileIndex_max.y * tiles.x);

vec2 tileUV = mix(0.5/tileSize, (tileSize-0.5)/tileSize, imageColor.rg);

vec2 tableUV_1 = tileIndex_min / tiles + tileUV / tiles;
vec2 tableUV_2 = tileIndex_max / tiles + tileUV / tiles;

vec3 lookUpColor_1 = texture(src_l, tableUV_1).rgb;
vec3 lookUpColor_2 = texture(src_l, tableUV_2).rgb;
vec3 lookUpColor   = mix(lookUpColor_1, lookUpColor_2, index-index_min); 

查看比较原始图像(左上)和颜色查找修改图像(右下)的图像:

在颜色table中寻找对应位置的计算好像有问题。我认为您必须首先找到正确 "red-green plane" 内着陆的偏移量(由输入的蓝色通道确定,考虑到由于地图中的 8x8 布局,步幅为 8),然后添加此偏移量计算 xy 值。

但是,我建议您先查看内置的 CIColorCube filter,因为它完全符合您的要求。