Evercoder / culori

A comprehensive color library for JavaScript.
https://culorijs.org
MIT License
872 stars 30 forks source link

Incorrect YIQ conversion #185

Closed facelessuser closed 1 year ago

facelessuser commented 1 year ago

I was implementing YIQ and wanted to compare my numbers to another library as a sanity check. I found that our values differed quite a bit.

> culori.yiq('purple')
{
  mode: 'yiq',
  y: 0.0892318825202534,
  i: 0.059183790065278435,
  q: 0.11281239073268069
}
>>> Color('purple').convert('yiq')
color(--yiq 0.2075 0.13763 0.26233 / 1)

I then compared it against one more source just to get another data point. They are using FCC NTSC Standard (SMPTE C) as mentioned in the Wikipedia article, instead of what's shown in http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf, but the results are comparable.

>>> import colorsys
>>> colorsys.rgb_to_yiq(128 / 255, 0, 128 / 255)
(0.20580392156862742, 0.13919372549019607, 0.2635796078431373)

I think they are probably doing things wrong in a different way as they are using the SMPTE C values, but not using SMPTE C RGB which uses different chromaticities. I guess the original 1953 NTSC also used different chromaticities than sRGB as well. It may be that lots of people are doing it slightly differently. The research paper seems to suggest it is using sRGB, but I have no idea where exactly they got their matrix from either. This statement can probably be ignored as it seems there is the "official" YIQ used by TV, but people generally use YIQ like a color model for any RGB, even if it is specifically weighted for a specific RGB.

Looking over your code, I found out why yours differs so much; you are converting the color in linear sRGB and YIQ is supposed to be transformed using the gamma-encoded color, not linear:

From http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf (emphasis added)

The values of Y, I and Q can be directly calculated from the non-linear r’, g’ and b’ components in the following way [21]:

From https://en.wikipedia.org/wiki/YIQ (emphasis added)

These formulas allow conversion between YIQ and RGB color spaces, where R, G, and B are gamma-corrected values.

It seems that linear sRGB is not meant to be used with this transform. The one thing that does seem consistent across the different implementations I've looked at is that they don't use linear RGB values. It is certainly possible that I've somehow misinterpreted something.

danburzo commented 1 year ago

Hi Isaac, thanks for opening this issue. You're totally right! I must have misread non-linear for linear in the paper when I implemented the Kotsarenko-Ramos color difference formula, which was the impetus for introducing the YIQ color space as a mapping of sRGB (the color space assumed in the authors' paper).

I should also probably clarify in the color space reference what flavor of YIQ we're using.

facelessuser commented 1 year ago

Yep, no problem. I think it is a color space used casually here and there as a model instead of the "official" spec. Something I did not understand until I began researching it.

Anyways, glad I could help!

danburzo commented 1 year ago

The corrected conversion is included in the culori@2.0.4 release.