Hacker News new | comments | show | ask | jobs | submit login
Don’t Convert SRGB U8 to Linear U8 (demofox.org)
55 points by ingve 6 months ago | hide | past | web | favorite | 28 comments

If you want to interpolate between colors, anything-RGB is wrong, even for the best value of "anything". You want the LAB space, or something along those lines.

If we interpolate between one RGB color to another color of equal intensity, the intensity along the interpolation will not be constant.

The gradient from pure red to pure green in linear RGB space isn't bad at all. The problem comes when you interpolate between endpoints of different intensities.

LAB isn't perfect either. Depending on your endpoints it can transition through colors that are hardly related.

Yeah it seems like you'd want to interpolate along an equiluminant plane using a colourspace that separates chrominance and luminance (and is ideally perceptually uniform)

The author mentions that one should always convert to f32 for storage or computation on linear values, but why not just use u16 or i16? You'd get both the benefits of having an integer format and a smaller data type at that, while still having the expanded space to prevent precision loss (depending on what you want to do, you may have to be a touch careful, of course).

Component values outside the (normalized) range [0,1] are necessary to represent certain colors as sRGB (and most other color spaces). So if you're color-space aware, at least i16 is warranted. f32 just means you have to worry less about overflow, quantization effects, etc., that crop up with various image manipulations. Plus, on GPUs, 32-bit floating point math is generally at least as fast as integer math. So it comes only at a loss of space.

At first it may seem alien to think about negative brightness but it really is necessary when dealing with different color spaces.

The area inside the "RGB" color triangle is represented by positive (non-negative) values. The sRGB color triangle only covers some part of the colors that physically can exist. If you have colors outside the sRGB color triangle at least one component will turn negative.

Not all colors you could represent by RGB-values do actually exist. You can even define a bigger color triangle covering all colors that exist with positive values but also some colors that don't exist.

It doesn't physically make sense but it is a very useful tool when doing calculations. By using floating point of sufficient precision you have to worry less about how you calculate your things. It's not needed but useful. If you know your calculations well you probably don't need float.

Here is an image of some color triangles and what area they cover: https://en.wikipedia.org/wiki/Color_space#/media/File:CIE193...

On GPUs, fp16 is quite a bit more power-efficient (and bandwidth-efficient) than fp32.

Aren't there problems with FP16 support being far from universal, and many GPUs having far less FP16 throughput than FP32? In some situations FP16 is definitely a better choice, but I don't think it can be given as a blanket recommendation.

I work exclusively on embedded GPUs these days, so I was surprised to read this. It's easy to repurpose FP32 hardware to at least support FP16 without reduced performance. However, seems that Nvidia has intentionally reduced FP16 performance on some consumer products for market segmentation reasons.

you are totally correct. u16 is more than enough (as is u12 apparently!). F16 is also sufficient. I'll update the text to point this out.

Note: that relies on doing proper sRGB -> linear instead of naive gamma conversion, unless I'm completely mistaken:

    In [16]: math.log2(1./colour.models.oetf_reverse_sRGB(1.0/256.0))
    Out[16]: 11.6915341649192

    In [17]: math.log2(1./((1.0/256.0)**2.2))
    Out[17]: 17.6

    In [18]: math.log2(1./((1.0/256.0)**1.8))
    Out[18]: 14.4

Although the stuff you will be losing even if you use 2.2 gamma with 12 bit depth should be fairly minimal if I'm interpreting the numbers correctly

The right thing to use is really f16, half precision floating point. It's a crying shame that CPU manufacturers have ignored f16 for so long, because there are so many applications where it's the perfect choice. Imaging, audio, machine learning.

Agreed for imaging and ML, but fp16 is kind of crappy for audio. It adds noise at -66 dB relative to the signal at every operation, which you can definitely hear. Audio data is never so large that memory bandwidth is significant, so you should use fp32.

Excuse me for my ignorance on the subject, but I thought gamma died with CRTs and all RGB values on images are intensity representations on a linear scale. Am I wrong? Is sRGB used for publishing or something?

Gamma still exists! Check out this really nice article for more info "The rehabilitation of gamma"


The transition from CRTs to LCDs didn't happen at the flick of a switch, it happened gradually. The signals between the computer and the display remained the same, based on standards maintaining backward compatibility.

I don't know if gamma was a happy accident or a result of clever engineering, but it has a benefit even today. The eye's response to light is not linear, and a gamma-corrected signal has nearly even steps of brightness between each of the 256 levels. A linear signal does not, and would require more bits for an acceptable display.

sRGB is used for encoding pretty much all JPEGs and other format bitmaps too. (Although some are in higher gamut colour spaces like AdobeRGB or DCI-P3, as many displays support this wider range of colours too.)

If you want to be efficient with your encoding, you'll always use a gamma-like function. This is because our eyes have a logarithmic response to brightness, and the gamma is essentially an inverse of that.

Gamma is alive and well on pretty much all displays.

After reading up on it I'm so glad the implementation is transparent and I don't have to deal with it, but I'm also really dissatisfied with the discrete and imperfect nature of lookup tables and perceived brightness approximations.

Physically, gamma died with the CRT, but every LCD recreates it for compatibility.

LCDs still have their own inherent "gamma" curve which is actually a sigmoid shape. They just emulate CRT gamma for backwards compatibility and hide their own low level details in the process.

I built a RGB LED Display and at first implemented brightness levels by just PWM modulating in a linear way. I quickly found out that it was lacking in dark shades or that the bright shades were almost indiscernible. The solution was gamma correction which made the shades look distributed evenly across the brightness range.

Or just use the standard BCn or DXT formats. The compression in these as texture formats is way smarter (for example, additional bits are used to encode green which our eyes are more sensitive to). These are "lossy" formats but when you go from PNG or something to a u8 format, you get artifacts for sure.

Additional bits for green is hardly "way smarter". It is old hat, expressed in representations like the ancient "RGB 565" format.

Yes but it also palettizes per-block and it's not something the r8g8b8a8unorm format provides.

Regardless of it being older, why isn't it way smarter?

Follow up question, why are you so snarky?

Please don't use those DXTn formats; they're still legally toxic due to patents.

Even if they were "legally toxic" (which thankfully someone else took the trouble to disprove), you're making religious statements in a world where people use things for practical reasons. Pretty much every game engine in existence has DXT support, and I'd loooove to hear about how many got in trouble for these toxic patents.

Applications are open for YC Winter 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact