JuliaIO / ImageMagick.jl

Thin Wrapper for the library ImageMagick
Other
53 stars 37 forks source link

save an array as png through ImageMagick generate broken image. #184

Open dskkato opened 4 years ago

dskkato commented 4 years ago

This issue was originally reported to Images.jl: https://github.com/JuliaImages/Images.jl/issues/909


When I saved an image which has values of multiples of 1/3, the saved image shrank along horizontal axis by factor of 1/4 and rest of the image was filled with random vertical pattern.

sample

Examples of generating broken images

What I tried was as follow.

julia> using Images
julia> img = zeros(Float64, 64, 64);
julia> for i in 8:16:64
           img[i, :] .= 1/3
           img[:, i] .= 1/3
       end
julia> save("sample.png", img)

An expected image from above snippet is a lattice pattern with vertical and horizontal lines. expected

But actual was those. These outputs were obtained by repeating save(...) function in the snippet. While the same img was passed to save, different vertical pattern appears in right 3/4 in images every time.

sample

sample

A curious point is that when some different value was assigned to the array, the save function works as expected.

julia> using Images
julia> img = zeros(Float64, 64, 64);
julia> for i in 8:16:64
           img[i, :] .= 1/3
           img[:, i] .= 1/3
       end
julia> img[1, 1] = 0.1;
julia> save("sample.png", img)

Another examples

Here, 1/5 was assigned instead of 1/3. This time, shrinking ratio seems to be about 1/2, though

sample

sample

sample

save with ImageIO worked as expected

The above examples were generated without ImageIO package. When you see FileIO package and search png format, it tries to use ImageIO if installed. So I added ImageIO and then re-run the above snipped. That condition created an expected lattice pattern image. So the main cause might be ImageMagick module.

https://github.com/JuliaIO/FileIO.jl/blob/c55ffe1439dbef1f9a2b44852dda52e89beb530a/src/registry.jl#L90-L98

Other info.

Following is my environment.

(@v1.5) pkg> st
Status `C:\Users\o028574\.julia\environments\v1.5\Project.toml`
  [c52e3926] Atom v0.12.19
  [12aac903] BinaryBuilder v0.2.5
  [864edb3b] DataStructures v0.17.20
  [31c24e10] Distributions v0.23.9
  [7a1cc6ca] FFTW v1.2.2
  [5789e2e9] FileIO v1.4.0
  [7073ff75] IJulia v1.21.2
  [6218d12a] ImageMagick v1.1.5
  [916415d5] Images v0.22.4
  [c601a237] Interact v0.10.3
  [a98d9a8b] Interpolations v0.12.10
  [e5e0dc1b] Juno v0.8.3
  [91a5bcdd] Plots v1.5.8
  [438e738f] PyCall v1.91.4
  [d330b81b] PyPlot v2.9.0
  [295af30f] Revise v2.7.3
  [2913bbd2] StatsBase v0.33.0
  [5e47fb64] TestImages v1.2.1
  [44d3d7a6] Weave v0.10.2
  [0f1e0344] WebIO v0.8.14

julia> versioninfo()
Julia Version 1.5.0
Commit 96786e22cc (2020-08-01 23:44 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-9.0.1 (ORCJIT, skylake)

Brief debug info

Following is @timholy's reply to original post.


I inserted some debugging code in ImageMagick.jl:

diff --git a/src/libmagickwand.jl b/src/libmagickwand.jl
index 51766d7..07c0ad8 100644
--- a/src/libmagickwand.jl
+++ b/src/libmagickwand.jl
@@ -237,7 +237,9 @@ end
 #     nothing
 # end

+const lastbuffer = Ref{Any}(nothing)
 function constituteimage(buffer::AbstractArray{T}, wand::MagickWand, colorspace::String, channelorder::String; x = 0, y = 0) where T<:Union{Unsigned,Bool}
+    lastbuffer[] = copy(buffer)
     cols, rows, nimages = getsize(buffer, channelorder)
     ncolors = colorsize(buffer, channelorder)
     p = pointer(buffer)

and then did this:

buf = ImageMagick.lastbuffer[]
using ImageView
imshow(buf)

and got this:

image

So, the image is fine up to the point where Julia hands the data off to the imagemagick C library. Consequently, fixing this bug requires that it be reported to the ImageMagick developers.

Originally posted by @timholy in https://github.com/JuliaImages/Images.jl/issues/909#issuecomment-673388448


dskkato commented 4 years ago

The broken images have 2-bit or 4-bit depth, curiously. When the bit depth is specified directly as below, the lattice pattern is preserved.

save("png8:sample.png", img)

sample

By the way, I knew this method from twitter (Japanese, though). https://twitter.com/LirimyDh/status/1293779034321518592?s=20

timholy commented 4 years ago

Oh, that's very interesting, thanks for sharing!