XYZ to RGB miss match conversion.

So I was making a RGB to XYZ conversion and I came to position where I don’t understand the results I am seeing.

According to Easy RGB you do the conversion from sRGB to XYZ by using:

def rgb_to_xyz(self, r, g, b):# CORRECT ?
    if ( r > 0.04045 ):
        r = ( ( r + 0.055 ) / 1.055 ) ** 2.4
        r = r / 12.92
    if ( g > 0.04045 ):
        g = ( ( g + 0.055 ) / 1.055 ) ** 2.4
        g = g / 12.92
    if ( b > 0.04045 ):
        b = ( ( b + 0.055 ) / 1.055 ) ** 2.4
        b = b / 12.92
    x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805)
    y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722)
    z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505)
    return [x, y, z]
def xyz_to_rgb(self, x, y, z):# CORRECT ?
    var_r = (x *  3.2406) + (y * -1.5372) + (z * -0.4986)
    var_g = (x * -0.9689) + (y *  1.8758) + (z *  0.0415)
    var_b = (x *  0.0557) + (y * -0.2040) + (z *  1.0570)
    if var_r > 0.0031308:
        r = 1.055 * ( var_r ** ( 1 / 2.4 ) ) - 0.055
        r = 12.92 * var_r
    if var_g > 0.0031308:
        g = 1.055 * ( var_g ** ( 1 / 2.4 ) ) - 0.055
        g = 12.92 * var_g
    if var_b > 0.0031308:
        b = 1.055 * ( var_b ** ( 1 / 2.4 ) ) - 0.055
        b = 12.92 * var_b
    return [r, g, b]

I checked with the Colorizer site and the code is good. The results are good going back and forth but they are not the same used in Krita for some reason.

Openning a RGB file I place a color on the canvas and using the advanced color selector I change the mode to XYZ to see the code of the color I just used and they don’t match.

I have used standard RGB and adobe RGB with different results from Krita so I am a bit of a loss.

Also I am not able to locate the file that contains the RGB to XYZ code within Krita. XYZ to LAB is also not found so I imagine there might be some issues with it too since I can’t find it.

My issue is that I could use this code instead but it starts to fight Krita heavily when it updates side by side.

RGB document

That’s no surprise, because there is none.
It’s all handled by LittleCMS library, which implements the whole ICC color conversion pipeline.

Though the 8-bit/channel XYZ color space is not very useful to be honest, it’s nothing LCMS expects you to use, while you can specify one given the various flags, it appears to only use value range 0-127 (RGB white has Y of 127 while 255 would be expected).
But either way the huge color gamut give poor color resolution with only 8 bits.

And there’s another reson it doesn’t match: ICC V4 specifies D50 whitepoint for PCS, so that’s what LCMS’ built-in XYZ profile uses, while your formula assumes D65 for both RGB and XYZ.

1 Like

My idea was to use the Kelvin slider to adjust the luminant factor for the conversion when going back and forth but I was trying to do a stable D65 conversion first. Also I wanted to do XYZ in order to get too LAB colour space that I think is useful.

So what kind of route do you think I should take?

Match the correct document space and convert within pigmento independant?

Or you think o should match Krita conversion for matching sake and take a look at the LCMS library?

Also D50 sounds a bit odd, now that I think of it. Is not RGB D65 by default?

LCMS code is probably not too useful, it is rather low-level C code that focuses on executing the ICC specific operations with various optimizations for high throughput. It’s not exactly a nice read that explains a lot…

If I’m not mistaken you can convert ManagedColor to all the color spaces Krita understands, which then uses LCMS under the hood.
But I have my doubts it’s flexible enough for you, and probably rather slow too as it needs to search for a matching color space by strings on every call.

And yes, D50 seems like an odd choice, but ICC is more about printing than making things convenient for RGB devices.
That’s what the spec says about the profile connection space:

The PCS adopted white is defined to be the radiance of a perfect reflecting diffuser illuminated by a source with a spectral power distribution matching that of CIE Illuminant D50. ICC profiles contain the values of the media white, adapted to be relative to the chromaticity of the PCS adopted white.

1 Like

I had thought about that but I was sure it would lag.

But doing a stress test does not seem out of range considering my curiosity over this now, it will probably fail but it case it doesn’t, I will try.

I think I will take a better look into LCMS to see how that all works and in case all fails, I will just use an imaginary XYZ values for Krita I guess. Sounds weird to think about it that way. I would fall on the fact that using RGB document with XYZ values would be diferent from a XYZ document with XYZ values, and just kind of accept it in a way.

This might take a while for me to reach a decision so to speak I need to work on something else instead of this if it takes too long.

I was trying to use Krita to convert and I think there is a issue for my case. to convert you need to use the canvas as reference for colorForCanvas() and fromQColor().

So if I am using a CMYK document and want to do a RGB to XYZ or the other way around I can’t use a canvas input. or you can create a reference canvas to use?

The color conversion that knows about the display profile is basically tied to the canvas, yes. You need that for actual conversion to/from QColor for widget rendering.
DockWidget conveniently is a canvas observer though, it informs you when the currently active canvas changed.

You can’ create a canvas directly.

If your document is CMYK and want to use Krita for RGB<>XYZ conversion, you have to create a document in RGB or XYZ colorspace, and use it.
A 10x10pixels document for example (to reduce memory usage) with one layer.

Do not add created document to view, then it will be not visible for user.

You have to create document when plugin starts (avoid to create a document each time you need to do a conversion, then once created you can use it at any moment).
And created document should be saved somewhere (a temp directory, or document provided with plugin), to not have a document that is in modified status (otherwise if krita crash you might be asked if you want to restore autosave from it, for example)


But the Canvas isn’t part of a Document, but a View of a document. No View, no Canvas, hence no access to display profile.

Anyway, none of those are required for converting ManagedColor between its original (document) color space and any other color space for the purpose of color selecting, the display profile should be irrelevant at that point.

The canvas is only required to know how the colors will actually be displayed, as each monitor may have its own color profile assigned (and yes, this assumes your widget is on the same screen as the canvas, I’m afraid there is currently no way to color manage your widget for a different screen)

1 Like

You’re right… I didn’t take care about this :woozy_face:


For the display the colorForCanvas() and fromQColor() work amazingly well.

I dont know how to put it too words properly but what I needed would be something more sorta like:

output = ManagedColor.colorTranslate( comp, i_cm, i_cd, i_cp, o_cm, o_cd, o_cp)

output = output components
comp = input components [val1, val2, val3, alpha]
i_cm = input color model
i_cd = input color depth
i_cp = input color profile
o_cm = output color model
o_cd = output color depth
o_cp = output color profile

kinda like this would be the ideal I think. you set the components and the initial space and then choose the output space.

I was doing some tests a bit on the one way street with the conversion but it had a pretty acceptable performance using Krita to translate the color.

I am still gonna read more on the LCMS what I read still seems very vague but there is alot to it by the looks of it.

Also I found this Bruce Lindbloom and it was giving me thought because he has the matrices for variable illuminants that would work nicely with my kelvin temperature slider I think.

Also off-topic I found out I can make scripts to use functions of my plugins outside the plugin. I found it so funny.

1 Like

I was following Bruce Lindbloom’s site as before and I made a set up where I can swap from D65 and D50, and my D50 does not match LittleCMS. I am not even no were close I would say the results are actually pretty similar to D65.

gonna keep digging since they do not match. But I must say if they dont really match beyond the use of a ICC on it I might just give up the idea of compatibility and just opt the D65 route.