【问题标题】:Is there a way to check if an XYZ triplet is a valid color?有没有办法检查 XYZ 三元组是否是有效颜色?
【发布时间】:2020-03-05 16:17:02
【问题描述】:

XYZ 颜色空间包含所有可能的颜色,而不仅仅是由特定设备(如显示器)生成的颜色。并非所有 XYZ 三元组都代表物理上可能的颜色。给定一个 XYZ 三元组,有没有办法确定它是否代表真实颜色?

我想为自己生成一个 CIE 1931 色度图(见下图),但不知道如何去做。例如,很容易获取 sRGB 三元组的所有组合,然后将它们转换为色度图的 xy 坐标,然后绘制它们。您不能在 XYZ 颜色空间中使用相同的方法,因为并非所有组合都是有效颜色。到目前为止,我想出的最好的方法是一种随机方法,我通过对随机数的随机高斯函数求和来生成随机光谱分布,然后使用标准观察函数将其转换为 XYZ。

【问题讨论】:

  • 我不明白。如果将 sRGB 值转换为 xy 会导致图表有效,那么为什么将它们转换为 XYZ 不会导致图表有效?可观察到的颜色比 sRGB 跨越更大的空间,因此您无法了解全部内容,但在两种情况下都是如此。
  • 这是stackoverflow.com/questions/48390558/…的副本?请注意,Colour 现在具有用于该确切目的的 colour. is_within_visible_spectrum 定义。
  • @Cris sRGB 颜色空间是 XYZ 空间的子集,因此只会填充图表的一部分。
  • 但是,如果您显示 xy 图,这与情况有何不同?
  • @CrisLuengo 该图表示所有可能(在人眼中尽可能)颜色的色度。 XYZ 颜色空间是这些颜色空间的超集,这就是为什么某些 XYZ 值不代表真实颜色的原因。 sRGB 颜色空间是一个 sub 集。这意味着如果您将所有可能的 sRGB 颜色映射到上图中,它们只会占据光谱“莲花”形状内的一个小区域。 sRGB 颜色空间包含所有可能的颜色。这张图片展示了这一点:upload.wikimedia.org/wikipedia/commons/6/60/…

标签: image-processing colors computer-vision


【解决方案1】:

再想一想,我觉得显而易见的解决方案是生成光谱轨迹边缘周围的 xy 点列表,对应于纯单色。在我看来,这可以通过将可见频率(~380-780nm)直接输入 CIE XYZ 标准观察者颜色匹配函数来完成。将这些点视为凸多边形,您可以使用一种或另一种算法确定一个点是否在光谱轨迹内。就我而言,因为我真正想做的只是生成色度图,所以我只需将这些点输入到图形库的多边形绘制例程中,然后对于多边形的每个像素,我都可以将其转换为 sRGB。

我相信此解决方案类似于 Kel 在评论中链接的库所使用的解决方案。我不完全确定,因为我不熟悉 Python。

function RGBfromXYZ(X, Y, Z) {
    const R = 3.2404542 * X - 1.5371385 * Y - 0.4985314 * Z
    const G = -0.969266 * X + 1.8760108 * Y + 0.0415560 * Z
    const B = 0.0556434 * X - 0.2040259 * Y + 1.0572252 * Z
    return [R, G, B]
}

function XYZfromYxy(Y, x, y) {
    const X = Y / y * x
    const Z = Y / y * (1 - x - y)
    return [X, Y, Z]
}

function srgb_from_linear(x) {
    if (x <= 0.0031308) {
        return x * 12.92
    } else {
        return 1.055 * Math.pow(x, 1/2.4) - 0.055
    }
}

// Analytic Approximations to the CIE XYZ Color Matching Functions
// from Sloan http://jcgt.org/published/0002/02/01/paper.pdf

function xFit_1931(x) {
    const t1 = (x - 442) * (x < 442 ? 0.0624 : 0.0374)
    const t2 = (x -599.8) * (x < 599.8 ? 0.0264 : 0.0323)
    const t3 = (x - 501.1) * (x < 501.1 ? 0.0490 : 0.0382)
    return 0.362 * Math.exp(-0.5 * t1 * t1) + 1.056 * Math.exp(-0.5 * t2 * t2) - 0.065 * Math.exp(-0.5 * t3 * t3)
}

function yFit_1931(x) {
    const t1 = (x - 568.8) * (x < 568.8 ? 0.0213 : 0.0247)
    const t2 = (x - 530.9) * (x < 530.9 ? 0.0613 : 0.0322)
    return 0.821 * Math.exp(-0.5 * t1 * t1) + 0.286 * Math.exp(-0.5 * t2 * t2)
}

function zFit_1931(x) {
    const t1 = (x - 437) * (x < 437 ? 0.0845 : 0.0278)
    const t2 = (x - 459) * (x < 459 ? 0.0385 : 0.0725)
    return 1.217 * Math.exp(-0.5 * t1 * t1) + 0.681 * Math.exp(-0.5 * t2 * t2)
}

const canvas = document.createElement("canvas")
document.body.append(canvas)
canvas.width = canvas.height = 512
const ctx = canvas.getContext("2d")

const locus_points = []

for (let i = 440; i < 650; ++i) {
    const [X, Y, Z] = [xFit_1931(i), yFit_1931(i), zFit_1931(i)]
    const x = (X / (X + Y + Z)) * canvas.width
    const y = (Y / (X + Y + Z)) * canvas.height
    locus_points.push([x, y])
}

ctx.beginPath()
ctx.moveTo(...locus_points[0])
locus_points.slice(1).forEach(point => ctx.lineTo(...point))
ctx.closePath()
ctx.fill()

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)

for (let y = 0; y < canvas.height; ++y) {
    for (let x = 0; x < canvas.width; ++x) {
        const alpha = imageData.data[(y * canvas.width + x) * 4 + 3]
        if (alpha > 0) {
            const [X, Y, Z] = XYZfromYxy(1, x / canvas.width, y / canvas.height)
            const [R, G, B] = RGBfromXYZ(X, Y, Z)
            const r = Math.round(srgb_from_linear(R / Math.sqrt(R**2 + G**2 + B**2)) * 255)
            const g = Math.round(srgb_from_linear(G / Math.sqrt(R**2 + G**2 + B**2)) * 255)
            const b = Math.round(srgb_from_linear(B / Math.sqrt(R**2 + G**2 + B**2)) * 255)
            imageData.data[(y * canvas.width + x) * 4 + 0] = r
            imageData.data[(y * canvas.width + x) * 4 + 1] = g
            imageData.data[(y * canvas.width + x) * 4 + 2] = b
        }
    }
}

ctx.putImageData(imageData, 0, 0)

【讨论】:

  • 您似乎在做两件不同的事情,生成色度图和检查某些给定的 CIE XYZ 三色值是否代表颜色值是完全不同的任务。对于前者,您实际上只需要将 xy 色度坐标网格转换为 sRGB,并使用通过将 CMF 转换为 xy 色度坐标给出的光谱轨迹来剪辑它们。对于后者,您需要按照我的评论和链接问题中的说明进行操作。
  • @KelSolaar 我提到了如何使用此方法通过确定颜色的坐标是否落在光谱轨迹的形状内来确定颜色是否真实。如果你能做一个,你就可以做另一个。
  • 对不起,我应该更清楚:您的测试仅在您可以使用 HDR 颜色时才有效:您会在 CIE xyY 中找到点,例如xyY = [0.2, 0.1, 0.9], sRGB = [ 1.30842 0.20565 6.57576],如果您不能使用 HDR 颜色,则不适合“有界”可见光谱的边界。看看那个页面:colour-science.org:8020 并只保留可见光谱和光谱轨迹组件。
  • 值得补充的是,有些CMFS一旦绘制在色度图中就不是凸的,所以2d方法在这里不起作用。
猜你喜欢
  • 1970-01-01
  • 2011-06-03
  • 2011-02-15
  • 2019-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-20
  • 1970-01-01
相关资源
最近更新 更多