RGB -> XYZ 转换不正确。

4

我从这里得到了一个将XYZ转换为RGB的转换器,并将其编写成了C#程序,但是,我遇到了一些问题。

public static XYZColor RGBtoXYZ(RGBColor rgb)
    {
        XYZColor xyz = new XYZColor();

        float r, g, b;
        r = rgb.R;
        g = rgb.G;
        b = rgb.B;


        if (r > 0.04045f) r = (float)Math.Pow((r + 0.055)/1.055, 2.4);
        else r = r / 12.92f;
        if (g > 0.04045) g =(float)Math.Pow((g + 0.055f)/1.055f , 2.4f);
        else g = g/12.92f;
        if (b > 0.04045f) b = (float)Math.Pow((b + 0.055f)/1.055f , 2.4f);
        else b = b/12.92f;

        r *= 100;
        g *= 100;
        b *= 100;

        xyz.X = r*0.4124f + g*0.3576f + b*0.1805f;
        xyz.Y = r*0.2126f + g*0.7152f + b*0.0722f;
        xyz.Z = r*0.0193f + g*0.1192f + b*0.9505f;

        xyz.A = rgb.A;
        return xyz;
    }

    public static RGBColor XYZtoRGB(XYZColor xyz)
    {
        RGBColor rgb = new RGBColor();

        float x, y, z;
        x = xyz.X;
        y = xyz.Y;
        z = xyz.Z;

        x = x/100;        //X from 0 to  95.047      (Observer = 2°, Illuminant = D65)
        y = y/100;        //Y from 0 to 100.000
        z = z/100;     //Z from 0 to 108.883

        rgb.R = x*3.2406f + y*-1.5372f + z*-0.4986f;
        rgb.G = x*-0.9689f + y*1.8758f + z*0.0415f;
        rgb.B = x*0.0557f + y*-0.2040f + z*1.0570f;

        if (rgb.R > 0.0031308f) rgb.R = 1.055f*(float)Math.Pow(rgb.R, (1/2.4f)) - 0.055f;
        else rgb.R = 12.92f*rgb.R;
        if (rgb.G > 0.0031308f) rgb.G = 1.055f*(float)Math.Pow(rgb.G ,(1/2.4f)) - 0.055f;
        else rgb.G = 12.92f*rgb.G;
        if (rgb.B > 0.0031308f) rgb.B = 1.055f*(float)Math.Pow(rgb.B, (1/2.4f)) - 0.055f;
        else rgb.B = 12.92f*rgb.B;

        rgb.A = xyz.A;

        return rgb;
    }

在我的测试应用程序中,我制作了一个具有不同颜色的tilemap,然后通过HSV给它们赋予色调,并循环它们以显示完整的光谱。然后,每次更新都会添加色调,使其通过光谱移动。 但是,当我将颜色转换为和从XYZ时,它看起来像这样。 在此处查看代码,如果您感兴趣,“HSVtoRGB”方法按预期工作。
            Hue += HUEINCREASE;
            RGBColor c = ColorMath.HSVtoRGB(Hue, 1, 1, 1);

            //Convert to XYZ and back
            XYZColor xyz = ColorMath.RGBtoXYZ(c);
            c = ColorMath.XYZtoRGB(xyz);

            Render.Color = c;

XYZColor和RGBColor都是结构体。RGBColor包含浮点数,如果它的值大于1,则会将颜色环绕。因此,如果它的红色值为1.1f,则会将其包裹在.1f中;


4
似乎是你提到的那种环绕效应。我计算了Blue (0,0,255),得到的蓝色通道数值大约是1.0000002。根据你所述,这会环绕回0.0000002,导致输出黑色(这也是你的结果所显示的)。每次计算后,你可能需要将数值夹紧在0到1的范围内。 - lukegravitt
1
非常好!如果你把它作为答案,我可以接受它。我必须更加小心那个环绕业务。我正在考虑创建一个枚举,用于保存可能的操作值,例如DoNothing、WrapAround、Clamp等等。感谢您的帮助! - redcodefinal
@lukegravitt 快问一下,你知道XYZ颜色空间的最大值是多少吗?我似乎找不到它们的任何信息... - redcodefinal
限制取决于您的实现,通常最大值为1.0f或255i(适用于RGB和XYZ)。顺便说一下,由于XYZ是非线性的(它是波长的人眼平均响应),而RGB仅是仅在3个不同波长上的平均响应,因此无法完全正确地从RGB中获取XYZ。 - Spektre
我觉得转换效果还不错,只要你不一直来回反复地转换,每次都会使颜色变浅一点。就像我说的:能够胜任政府工作就行。XD - redcodefinal
显示剩余6条评论
1个回答

0
你可以使用 ColorHelper 库来完成这个任务:
RGB 转 XYZ:
using ColorHelper;

RGB rgb = new RGB(100, 100, 100);
XYZ xyz = ColorConverter.RgbToXyz(rgb);

XYZ to RGB:

using ColorHelper;

XYZ xyz = new XYZ(0, 0, 0);
RGB rgb = ColorConverter.XyzToRgb(xyz);

链接:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接