-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Different png RGBA values using Julia, C or Python #48
Comments
@t-bltg Thanks for looking into this! Sadly, I'm not a real image theorist myself but the following is my understanding of the issue. The two files are different in how they encode gamma information:
To get a little bit more explicit, this is how the gamma values are used (assuming 8-bit depth): corrected = round(Int, 255 * ((raw / 255) ^ (1.0 #=viewing_gamma=# / (file_gamma * display_gamma)))) while
which means that our formula ends up being corrected = round(Int, 255 * ((raw / 255) ^ (1.0 / (file_gamma * display_gamma))))
corrected = round(Int, 255 * ((raw / 255) ^ (1.0 / (1.0 * 2.2))))
corrected = round(Int, 255 * ((raw / 255) ^ (0.45455))) This is how we arrived at the julia> round.(Int, 255 * ((UInt8[233, 173, 4] ./ 255) .^ (0.45455)))
3-element Vector{Int64}:
245
214
39 So what does this mean for our implementation?
Should we completely skip gamma correction when the file_gamma is 1.0? How about for file_gamma 0.99999? Are our results correct? Sadly, I'm not sure... maybe @IanButterworth, @johnnychen94 or @timholy, if they have a moment, could advise as they know much more than I do about images?
The transformation you are using, which is linear for small values of and x and then uses an exponent of 2.4 + some shift and scale, is in fact an sRGB compliant transformation, while gamma correction itself is just a simple power transformation. An exponent of 2.2 is generally used as a default for gamma correction and with this value the two transformations tend to be (visually) similar. |
Thanks for the detailed explanations, I have a better understanding now.
Isn't the spec mentioned obsolete ? "PNG (Portable Network Graphics) Specification Version 1.0" From what I understand, the current spec is sRGB
Gamma (not part of the PNG spec)Decoder gamma handling
What does
Encoder gamma handling
$ identify -verbose 5c5cdaa477dcae516c7183c3e710ef65a0bb6ab0.png | grep 'gAMA\|Gamma\|sRGB'
Colorspace: sRGB
Gamma: 0.454545
png:sRGB: intent=0 (Perceptual Intent)
$ identify -verbose img.png | grep 'gAMA\|Gamma\|sRGB'
Gamma: 1
png:gAMA: gamma=1 (See Gamma, above) Not easy 😅 ! |
Ok, so let's forget about
Nearly all the plugins in I've updated pngpixel.py and I must say that nearly all plugins (except |
Changing this line to $ julia pngf.jl 2 2 img.png -1.
rgba(233,173,4,244)
|
Well, but is it desirable? If someone stored the pixels with a specific To turn off gamma correction in PNGFiles.jl, one can provide elseif gamma != 1
image_gamma[] = gamma / screen_gamma # before: was image_gamma[] = gamma
png_set_gamma(png_ptr, screen_gamma, image_gamma[])
end and make it explicit, that the gamma argument is the "end-to-end" gamma... But as a "turn-off" switch for gamma correction it works. Is the source of difference between PNGFiles / ImageMagick and the python packages you mentioned the fact that we do gamma correction by default? |
Let's wait for the experts answer ;)
For this to work, you'd have to set
I'm digging into |
Yes! I forgot
I've attempted and gave up reading that 14k LOC file multiple times... Thanks for investigating this! |
I don't know much about color space management, so my words here are opinioned from my own understanding of the specification. We should skip gamma correction when it's 1.0 since the correction degenerate to an identity map (This could potentially give better decoding performance by telling more information to the libpng implementation but who knows 🤷♂️). If it's 0.99999 it has negligible differences so it doesn't matter in practice. It's still my opinioned understanding, I assume our results are correct as it follows the specification suggestion. In https://www.w3.org/TR/2003/REC-PNG-20031110/#11gAMA "11.3.3 Colour space information", it says
I assume that we should stick to applying gamma correction by default, and also provide a way to override it (if we don't yet have it). Personally, I would insist to say this inconsistency between Julia/C/Python is implementation differences since the specification doesn't say what is strictly correct. We can choose to make the gaps as close as possible. However, since there can be more unidentified cases like this and it can never be made so. |
I missed to quote another suggestion in the same chapter:
This seems to indicate that pure image IO backends (pillow, PNGFiles, etc) are discouraged to use the gAMA component? |
Thanks for looking into this, @johnnychen94 . IIUC, there are two high-level component: the screen/display gamma and the file gamma. The corrected = round(Int, 255 * ((raw / 255) ^ (1.0 / (file_gamma * display_gamma))))
corrected = round(Int, 255 * ((raw / 255) ^ (1.0 / (1.0 * 2.2))))
corrected = round(Int, 255 * ((raw / 255) ^ (0.45455))) our current
I guess the question is: do we want to always pass pixels data in raw form (i.e. disregard gamma correction, but the pixels might be stored gamma corrected or not) or should we always do gamma correction (and then the pixels are always gamma corrected, or at least attempted to be). My gut feeling was that if data was stored in png (and not HDF5, or Arrow...), the the focus was on display-ability, but I think the spec is rich enough to accomodate even the data processing use case. For different viewing conditions, the gamma correction could be adjusted for e.g. viewing in a dim room... not sure if that is what is meant in the quote? |
The
Adding debugging to a custom build of
Some answer elements from libpng-manual.txt
From what I understand, if the file format if the EDIT1: getting close, from libpng, in
what we have just after calling EDIT2: this is were the palette is transformed pngrtran.c. EDIT3: to build EDIT4: ImageMagick:
PNGFiles:
In |
I've updated https://github.com/t-bltg/PngPixel.jl, the I can get
Since the two implementations give different results on
I think yes, it should be left to the CMS, unless explicitly requested. Here are the two references quoted in the imageio issue resuming the situation: |
Per https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599.
Reproducer at https://github.com/t-bltg/PngPixel.jl.
Xref: JuliaIO/ImageIO.jl#41 (comment), @johnnychen94.
For reference, the image used here is a scaled version of toucan.png from TestImages.jl:
$ convert toucan.png -scale 5% img.png
.Tested with #47 but still fails on
1.6.5
or1.7.1
(official binaries):@Drvi , this is getting weird, maybe I changed the file when uploading to discourse, sorry about the confusion:
OK, so this a Srgba vs rgba thing, let's use the SRGB -> linear transformation (https://en.wikipedia.org/wiki/SRGB#Transformation) (sorry i'm not very familiar with images theory, is that correct ?)
The text was updated successfully, but these errors were encountered: