OpenImageIO: [BUG] OIIO seems to consider that the alpha channel is gamma-compressed when reading/writing 8-bit formats

Describe the bug

Use the attached images ramp.png and solidblack.png. ramp.png has a linear alpha variation from left to right (8-bit values checked with the GIMP) and do:

oiiotool ramp.png --ch "R,G,B" -o rampoiiorgb.png # result way too dark
oiiotool --no-autopremult ramp.png --tocolorspace linear --premult --ch "R,G,B" --tocolorspace sRGB -o rampoiioopaque.png # GOOD expected result
oiiotool ramp.png --ch "R,G,B" -o rampoiiorgb.exr # result not fully OK, the red and black bands are too narrow
oiiotool rampoiiorgb.exr --tocolorspace sRGB -o rampoiiorgbviaexr.png # same

# composite over solid black
oiiotool ramp.png solidblack.png -over -o rampoversolidblackoiio.png # result too dark
oiiotool --no-autopremult ramp.png --tocolorspace linear --premult solidblack.png -over --tocolorspace sRGB -o rampoversolisblackoiio2.png # GOOF expected result
oiiotool ramp.png solidblack.png -over -o rampoversolidblackoiio.exr # result not fully OK
oiiotool rampoversolidblackoiio.exr --tocolorspace sRGB -o rampoversolidblackoiioviaexr.png # result OK

I checked manually the values at x=960, y=540:

  • ramp.png has RGBA value (177,0,196,128) (50% alpha, unassociated because PNG)
  • rampoiiorgb.png and rampoversolidblackoiio.png have RGB value (89,0,98)
  • rampopaque.png (attached image) has RGB value (129,0,143) (it was obtained by linearizing RGB from ramp.png, then compositing over black with the linear alpha or equivalently multiply by alpha, then delinearizing RGB)
  • rampoiioopaque.png and rampoversolisblackoiio2.png have RGB value (129,0,144), close to what I compute by hand
  • rampoiiorgbviaexr.png and rampoversolidblackoiioviaexr.png have RGB value (159,0,167), which I cannot explain, where do these values come from?
  • R_good = ((177/255)^2.2 * (128/255))^(1/2.2) * 255 = 129 <-- I should get this value
  • B_good = ((196/255)^2.2 * (128/255))^(1/2.2) * 255 = 143 <-- id
  • R_bad = ((177/255)^2.2 * (128/255)^2.2)^(1/2.2) * 255 = 89 <-- I get that instead: alpha was not considered linear it seems! Or alpha was applied on the nonlinear RGB: this is also simply 177 * (128/255) = 89
  • B_bad = ((196/255)^2.2 * (128/255)^2.2)^(1/2.2) * 255 = 98 <-- id, this is 196 * (128/255) = 98

I see two or three potential bugs, or I may not have understood what happened:

  1. converting via EXR should give the same result as to PNG
  2. PNG result is too dark (darker than the EXR version)
  3. Even the EXR version seems wrong with respect to what I compute

This may also be the root cause of bug https://github.com/NatronGitHub/Natron/issues/582

See also:

Platform information:

  • OIIO branch/version: 2.2.7 (from Mac homebrew)
  • OS: macOS 11.2
  • C++ compiler: from Xcode
  • Any non-default build flags when you build OIIO: homebrew build

ramp.png: ramp

solidblack.png: solidblack

rampopaque.png: rampopaque

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I think the minimal fix (and maybe the maximum one we can do practically, without unwelcome behavior changes) is what I offer in #2890: just give the option to have --autocc do the unpremult/premult bracketing and call it a day.

I’m 90% convinced that the unpremult dance is what you usually want (and then 10% of the time my brain flips and I bolt awake in a sweat unable to understand why comping over opaque black and transforming should yield different results than transforming the fg element only with partial alpha). I’m still groping for how to explain this all in the docs so that people understand in simple terms when they should turn that option on and when not to.

I was thinking the same as you, @michdolan, which is that in practice most of our transformations are not just a 3x3 matrix, there’s always a little bit of nonlinearity (even if just the gentle roll-off at the extremes) even for semantically-almost-linear color spaces.

And you put your finger right on what bugs me about PNG, which is that it not only allows but requires two concepts I don’t want to see anywhere near production – unassociated alpha, and sRGB colors. It seems like a recipe for people and software to do the wrong things and to reason about it all incorrectly, as I have so clearly demonstrated myself.

As an aside, I think I have always had trouble understanding why the color conversion is typically implemented by dividing by alpha, doing the color space transform, and then re-multiplying by alpha. I had trouble wrapping my mind around why that was more correct that directly color correcting the premultiplied color. I’ve talked to color scientists about this and never got an explanation that I fully grokked, never quite agreed, but just decided they must know better than me. This example finally makes me understand in a simple way that I can keep in my head and reason simply about.