/CLUT-from-images

generate color lookup table from origin image and result images

Primary LanguagePythonMIT LicenseMIT

CLUT-from-images

generate color lookup table from origin image and result images

颜色查找表(Color LookUp Table)用蓝色作为索引, 用来分块, 每个小块的蓝色是固定的, 其中x轴是红色, y轴是绿色

CLUT原理

在这里我们以18bit色作为样例说明, 使用18bit色深主要是出于存储空间考虑, 18bit = 2^18 = 262144就是通常说的26万色, 这26万色可以分解成2^18 = (2^9) ^ 2 = 512 * 512, 所以通常CLUT也是一个512 x 512的图片

从图片大小来看宽高512的总像素数是262144, 假设我们的图片是24位色--也就是每个通道用8bit, 总共3byte, 那么最后图片大小是262144 * 3 = 786432也就是768KB. 同样的如果使用24位色的话就需要使用宽高4096的图片, 算下来最后图片大小有48MB

虽然图片精度下降了一些, 但是带来的空间节省却是巨大的, 把48MB的图片加载进GPU也肯定比768KB耗时更长

18bit色深

RGB每个通道使用6bit来表示颜色, 每个通道的集合应该是[0, 255], 但是2^6 = 64, 范围就是[0, 64] 所以最后的结果需要乘4来变换到[0, 255]

枚举所有颜色的查找表

这是一个8x8的查找表, 总共有64个block, 所以范围是[0, 64], 又因为每个block中蓝色是固定的, 所以蓝色可以完全表示 其中每个block中从左到右是红色, 取值范围是[0, 64], 从上到下是绿色取值范围是[0, 64], 这样所有的颜色就都可以表示了

查找表

img = np.zeros((512, 512, 3), dtype=np.uint8)
for by in range(8):                     # block y索引
    for bx in range(8):                 # block x索引
        for g in range(64):             # block内 y索引
            for r in range(64):         # block内 x索引
                x = r + bx * 64         # 整张图的 x坐标
                y = g + by * 64         # 整张图的 y坐标
                img[y][x][0] = int(r * 255.0 / 63.0 + 0.5)
                img[y][x][1] = int(g * 255.0 / 63.0 + 0.5)
                img[y][x][2] = int((bx + by * 8.0) * 255.0 / 63.0 + 0.5)
                # 每个点的(r, g, b)计算方法, 可以看出每个块内的蓝色都是一定的

使用查找表的过程

我们有了查找表之后要得到色彩C 对应查找表内的颜色 需使用如下方法:

  1. 分离色彩C的RGB通道为(Cr, Cg, Cb)
  2. 通过Cb计算block的位置, y = Cb // 4 // 8 x = Cb % 8
  3. block位置确定之后就在里面通过Cr, Cb计算具体的点P
  4. 取出点P的RGB通道(Pr, Pg, Pb)
  5. 目标的RGB值就是(Pr, Pg, Pb)

新查找表生成方法

因为这就是一个查表过程, 所以得到新滤镜的方法就很简单了

  1. 准备一张完整的查找表图片, 这里称为identity
  2. 然后用滤镜软件处理identity, 得到一张新的图片D

D就是我们新的查找表了, 因为本质上我们是用原集合(identity), 经过函数f(滤镜软件x)得到了新的集合, 所以就可以直接使用D了

因为identity包含了所有可能的颜色, 并且都在对应的位置上, 所以目标查找表B上面的位置都是正确的, 只要取出对应的RGB通道就是滤镜后的效果了

总结

其实颜色查找表是很聪明的一个构造方法, 把原色彩的RGB分别作为坐标轴, 对应像素的内容才是真正的值