# Reading png rgb channels, Julia vs Python

**URL:** <https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599>\
**Category:** Visualization\
**Created:** [December 25, 2021, 12:39am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599 "2021-12-25T00:39:01Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [December 25, 2021, 12:39am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/1 "2021-12-25T00:39:01Z")

</div>

So here is a very simple `8x8` `png` image:  
 ![img_5](https://global.discourse-cdn.com/julialang/original/3X/1/b/1b09f38213ff7d30bcffb96b5e8ae182827cf254.png)

Opening it in `GIMP`:

 ![rgb](https://global.discourse-cdn.com/julialang/original/3X/c/5/c5533b3df2dda41c38348bd5d1016e382654e5d2.png)

So this pixel has `RGBA(233, 173, 4, 244)` components.

Using `Python`:

```python
import imageio
img = imageio.imread('img.png')
r = img[:, :, 0]
g = img[:, :, 1]
b = img[:, :, 2]
a = img[:, :, 3]

print((r[2, 2], g[2, 2], b[2, 2], a[2, 2]))

```

gives `(233, 173, 4, 244)`, so far so good.

Now using `Julia`:

```julia
using ColorTypes
using ImageMagick
using PNGFiles

for mod ∈ (ImageMagick, PNGFiles)
  img = open("img.png") do io
    kw = mod == PNGFiles ? (;expand_paletted=true) : ()
    mod.load(io; kw...)
  end

  int255(c) = round.(Int, 255c)
  r, g, b, a = int255(red.(img)), int255(green.(img)), int255(blue.(img)), int255(alpha.(img))

  println((r[3, 3], g[3, 3], b[3, 3], a[3, 3]))
end

```

gives

```julia
(245, 215, 34, 244)
(245, 214, 39, 244)

```

Only the alpha channel is identical. `G` and `B` channels have different values using `PNGFiles` or `ImageMagick`. And the `RGB` values are far from what is displayed in `GIMP` or by using `Python`.

What am I missing here ?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [December 25, 2021, 2:39am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/2 "2021-12-25T02:39:18Z")

</div>

is this a linear vs srgb issue?

---

<div class="post-metadata">

**Author:** ![Chris\_Green](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chris_green/32/32265_2.png) [@Chris\_Green](https://discourse.julialang.org/u/Chris_Green)\
**Post date:** [December 25, 2021, 2:40am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/3 "2021-12-25T02:40:20Z")

</div>

It looks like Python is returning raw srgb values from the file but Julia is converting them to linear values.  
You want linear values if you’re doing any math on them. For instance, averaging or adding two values together in srgb space will return a wrong result.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [December 25, 2021, 2:43am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/4 "2021-12-25T02:43:55Z")

</div>

that said, the conversion does lose considerable precision unless Julia is using more than 8 bits.

---

<div class="post-metadata">

**Author:** ![evanfields](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/evanfields/32/1744_2.png) [@evanfields](https://discourse.julialang.org/u/evanfields)\
**Post date:** [December 25, 2021, 3:05am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/5 "2021-12-25T03:05:07Z")

</div>

Wait, isn’t it the other way around? (Disclaimer: I am very much not an expert)

From the _excellent_ [Color Spaces – Bartosz Ciechanowski](https://ciechanow.ski/color-spaces/), we see that  
`(intensity value) = (((encoded value) + 0.055)/1.055)2.4`

So if the encoded value is `245/255` then the actual intensity is indeed about `232/255`. So seems like Julia’s color and image packages are giving the encoded values, and Python’s is giving the actual linear intensity?

(I _think_ one way to double check this: do `using Colors; Gray(.5)` in an editor that displays color output, and you’ll see what looks to the human eye like mid-gray—meaning the argument to `Gray` must be the encoded value, not linear value, because the gray that humans see as halfway between dark and light actually has ~18% intensity.)

---

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [December 25, 2021, 8:06am UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/6 "2021-12-25T08:06:28Z")

</div>

Thanks for pointing towards srgb vs linear scale:

Using

```julia
  # ...
  linear(srgb) = begin
    # en.wikipedia.org/wiki/SRGB#Transformation
    lin = similar(srgb)
    for I in eachindex(srgb)
      lin[I] = if (v = srgb[I]) > .04045
        ((v + .055) / 1.055)^2.4
      else
        v / 12.92
      end
    end
    lin
  end
  lin255(x) = round.(Int, 255linear(x))
  raw255(x) = round.(Int, 255x)
  r, g, b, a = lin255(red.(img)), lin255(green.(img)), lin255(blue.(img)), raw255(alpha.(img))
  # ...

```

now gives the same `R`, `G`, `B`, `A` matrices as `Python` when using `ImageMagick` but different results for `PNGFiles` (rounding issues ?) so I’m more confident trusting the data output from `ImageMagick`, since it matches the implement of `imagio` (underlying `pillow` library).

---

<div class="post-metadata">

**Author:** ![drvi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/drvi/32/13236_2.png) [@drvi](https://discourse.julialang.org/u/drvi)\
**Post date:** [December 25, 2021, 12:52pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/7 "2021-12-25T12:52:35Z")

</div>

Hi, I’m one of the authors of PNGFiles.jl. Could you try importing the image using `gamma=1.0` and share the image in question?

---

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [December 25, 2021, 1:03pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/8 "2021-12-25T13:03:07Z")

</div>

The image in small but in the original post (right click, save as) or here is the direct url: [https://global.discourse-cdn.com/julialang/original/3X/5/c/5c5cdaa477dcae516c7183c3e710ef65a0bb6ab0.png](https://global.discourse-cdn.com/julialang/original/3X/5/c/5c5cdaa477dcae516c7183c3e710ef65a0bb6ab0.png).

With `gamma=nothing`: `(233, 171, 5, 244)`  
With `gamma=1.` or `gamma=.45455`: `(208, 107, 0, 244)`

Furthermore:

```bash
$ convert img.png txt:- | grep '2,2:'
2,2: (59881,44461,1028) #E9AD04 rgb(233,173,4)
$ julia -E '44461 / 65535 * 255'
173.0

```

---

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [December 25, 2021, 4:52pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/9 "2021-12-25T16:52:37Z")

</div>

@drvi, I have assembled a simple example at [https://github.com/t-bltg/PngPixel.jl](https://github.com/t-bltg/PngPixel.jl).

Using the `libpng_api` from `PNGFiles`, I can get the correct RGB values.

However I still don’t understand why using `PNGFiles.load(...)` gives the wrong values.

---

<div class="post-metadata">

**Author:** ![drvi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/drvi/32/13236_2.png) [@drvi](https://discourse.julialang.org/u/drvi)\
**Post date:** [December 25, 2021, 7:53pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/10 "2021-12-25T19:53:15Z")

</div>

@t-bltg Thanks for investigating! Can you try the following branch of PNGFiles?

```julia
] add PNGFiles#paletted_images_fix

julia> using PNGFiles

julia> map(x->(Int(x.r.i), Int(x.g.i), Int(x.b.i), Int(x.alpha.i)), PNGFiles.load("test.png"))[3,3]
(233, 173, 4, 244)

```

---

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [December 25, 2021, 8:17pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/11 "2021-12-25T20:17:08Z")

</div>

Odd, I get different results:

```julia
(@v1.7) pkg> add PNGFiles#paletted_images_fix
    Updating git-repo `https://github.com/JuliaIO/PNGFiles.jl.git`
    Updating registry at `~/.julia/registries/General.toml`
   Resolving package versions...
    Updating `~/.julia/environments/v1.7/Project.toml`
  [f57f5aa1] ~ PNGFiles v0.3.12 ⇒ v0.3.12 `https://github.com/JuliaIO/PNGFiles.jl.git#paletted_images_fix`
    Updating `~/.julia/environments/v1.7/Manifest.toml`
  [f57f5aa1] ~ PNGFiles v0.3.12 ⇒ v0.3.12 `https://github.com/JuliaIO/PNGFiles.jl.git#paletted_images_fix`
Precompiling project...
  1 dependency successfully precompiled in 4 seconds (44 already precompiled)

julia> using PNGFiles

julia> map(x->(Int(x.r.i), Int(x.g.i), Int(x.b.i), Int(x.alpha.i)), PNGFiles.load("img.png"))[3,3]
(245, 214, 39, 244)

```

---

<div class="post-metadata">

**Author:** ![drvi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/drvi/32/13236_2.png) [@drvi](https://discourse.julialang.org/u/drvi)\
**Post date:** [December 25, 2021, 8:32pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/12 "2021-12-25T20:32:43Z")

</div>

Hmm, would you mind opening an issue on the github repo? You can just link to this thread and preferably include the outputs of the following:

Just to be sure we’re talking about the same exact picture:

```julia
julia> (x->(Int(x.r.i), Int(x.g.i), Int(x.b.i), Int(x.alpha.i)))(PNGFiles.load(Downloads.download("https://global.discourse-cdn.com/julialang/original/3X/5/c/5c5cdaa477dcae516c7183c3e710ef65a0bb6ab0.png"))[3,3])
# expected: (233, 173, 4, 244)

```

and package versions

```julia
] st

```

and system info

```julia
julia> versioninfo()

```

Thanks!

---

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [December 25, 2021, 9:13pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/13 "2021-12-25T21:13:05Z")

</div>

Opened at [https://github.com/JuliaIO/PNGFiles.jl/issues/48](https://github.com/JuliaIO/PNGFiles.jl/issues/48).

---

<div class="post-metadata">

**Author:** ![Chris\_Green](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chris_green/32/32265_2.png) [@Chris\_Green](https://discourse.julialang.org/u/Chris_Green)\
**Post date:** [December 28, 2021, 6:43pm UTC](https://discourse.julialang.org/t/reading-png-rgb-channels-julia-vs-python/73599/14 "2021-12-28T18:43:01Z")

</div>

Doh I always forget which is approximately ^1/2.2 and which is approximately ^2.2.

Which is dumb. All I have to remember is that SRGB values are higher than linear at the low end and vice versa at the high end.
