将 LAB 颜色存储为整数

问题描述 投票:0回答:2

当使用 RGB 值时,.NET 内置了将其从整数转换的方法,并且将颜色转换为整数很简单。有没有办法将 LAB 颜色存储为整数?与 RGB 不同,LAB 颜色值可以为负值。如果我可以避免的话,我不想将颜色存储在 RGB 中并在运行时转换它们,因为我不需要 RGB 值。

c# serialization colors rgb
2个回答
4
投票

因此,使用旧的 2 度观察器和 CIE 标准光源 D65 -> CIELAB 进行的转换是 RGB -> XYZ。下面给出了用于执行该操作的代码(仅供参考)(假设 R、G、B 位于 [0, 1] 中)。

考虑到这些变换从 RGB 中每通道 8 位开始,L* 的范围为 [0, 100]、a* (-85.92, 97.96]、b* (-107.54, 94.20]。这些值是近似值。理论上,a* 和 b* 是无界的,但你会发现有些地方谈论 +-128、+-110 等的限制。我的建议是简单地将 128 与每个值相加,然后乘以 100,然后然后舍入为整数(对于颜色来说应该足够精确)。任何给定的 L*a*b 三元组都可以用 16 位无符号整数表示。您可以将它们打包成 64 位整数。解包后您将减去每个值都是 128。如果你可以保留三个有符号短整数,事情就会变得简单得多。

def rgb_xyz(r, g, b): # 2o. D65
    triple = [r, g, b]

    v, d = 0.04045, 12.94
    for i, c in enumerate(triple):
        triple[i] = 100 * (c / d if c <= v else ((c + 0.055)/1.055)**2.4)

    multipliers = (
            (0.4124, 0.3576, 0.1805),
            (0.2126, 0.7152, 0.0722),
            (0.0193, 0.1192, 0.9505))

    x, y, z = [sum(row[i] * triple[i] for i in range(3))
            for row in multipliers]
    return x, y, z

def xyz_cielab(x, y, z):
    triple = [x, y, z]

    t0, a, b = 0.008856, 7.787, 16/116.
    ref = (95.047, 100.0, 108.883)
    for i, c in enumerate(triple):
        triple[i] /= ref[i]
        c = triple[i]
        triple[i] = c ** (1/3.) if c > t0 else a * c + b

    l = 116 * triple[0] - 16
    a = 500 * (triple[0] - triple[1])
    b = 200 * (triple[1] - triple[2])
    return l, a, b

0
投票

受到 @mmgp 的回答的启发,我决定检查乘以 100 并在每个通道使用

uint16
是否足够。答案是肯定的,我的 Python 程序仍然通过其断言的最低整数乘数实际上是 82!

它依赖于此图像,其中包含使用此Python程序生成的所有1600万种RGB颜色。

这里断言的是,每一种可能的 RGB 颜色都可以无损地从 RGB -> LAB as doubles -> LAB as uint16s -> LAB as doubles -> RGB 进行无损转换。

# Verifies all_colors.png unsigned integer translation loses no information

import numpy as np
from PIL import Image
from skimage import color


def main():
    # "The L* values range from 0 to 100; the a* and b* values range from -128 to 127."
    # https://scikit-image.org/docs/stable/api/skimage.color.html#skimage.color.lab2rgb
    signed_to_unsigned = 128

    # Unsigned integer precision loss compensation multiplier
    # 82 is the lowest integer value that works
    precision_compensation = 82

    print("Loading input RGB image")
    input_img = Image.open("input/all_colors.png").convert("RGBA")
    pixels = np.array(input_img, dtype=np.float32)

    print("/= 255")
    # I do [:, :, :3] everywhere so only RGB is affected, and not a potential A
    pixels[:, :, :3] /= 255

    print("Running rgb2lab()")
    pixels[:, :, :3] = color.rgb2lab(pixels[:, :, :3])

    print("+= signed_to_unsigned")
    pixels[:, :, :3] += signed_to_unsigned

    print("*= precision_compensation")
    pixels[:, :, :3] *= precision_compensation

    print("Rounding to uint32")
    pixels = np.round(pixels).astype(np.uint16)

    print("pixels.astype(np.float32)")
    pixels = pixels.astype(np.float32)

    print("/= precision_compensation")
    pixels[:, :, :3] /= precision_compensation

    print("-= signed_to_unsigned")
    pixels[:, :, :3] -= signed_to_unsigned

    print("pixels.copy()")
    new_pixels = pixels.copy()

    print("Running lab2rgb()")
    new_pixels[:, :, :3] = color.lab2rgb(pixels[:, :, :3])

    print("*= 255")
    new_pixels[:, :, :3] *= 255

    print("Rounding to uint8")
    new_pixels = np.round(new_pixels).astype(np.uint8)

    assert (
        np.array(input_img, dtype=np.uint8) == new_pixels
    ).all(), "❌ There was loss of information during the translation!"

    print("🎉 There was no loss of information during the translation!")


if __name__ == "__main__":
    main()
© www.soinside.com 2019 - 2024. All rights reserved.