Get an image from a matrix of signed integers

What’s the best way to get a PNG image out of a matrix of signed integers, with Images.jl?

I converted the signed integers to Fixed in the range [-1, 1) (e.g., Int64 to Fixed{Int64, 63}), but when I try to create a PNG image (for example, when doing this in a Jupyter notebook) I get an error.

Single-line recipe to reproduce the issue:

julia> using Images

julia> reprmime("image/png", Gray.(reinterpret.(Fixed{Int8, 7}, rand(Int8, 5, 5))))
WARNING: Mapping to the storage type failed; perhaps your data had out-of-range values?
Try `map(clamp01nan, img)` to clamp values to a valid range.
Errors encountered while saving "Nullable{String}()".
All errors:
   InexactError()
   ErrorException("function save does not accept keyword arguments")
Fatal error:
ERROR: InexactError()
Stacktrace:
 [1] kwfunc(::Any) at ./boot.jl:237
 [2] eval(::Module, ::Any) at ./boot.jl:235
 [3] #save#21(::Array{Any,1}, ::Function, ::FileIO.Stream{FileIO.DataFormat{:PNG},IOContext{Base.AbstractIOBuffer{Array{UInt8,1}}}}, ::Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2}, ::Vararg{Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2},N} where N) at /home/mose/.julia/v0.6/FileIO/src/loadsave.jl:115
 [4] (::FileIO.#kw##save)(::Array{Any,1}, ::FileIO.#save, ::FileIO.Stream{FileIO.DataFormat{:PNG},IOContext{Base.AbstractIOBuffer{Array{UInt8,1}}}}, ::Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2}) at ./<missing>:0
 [5] #show#93(::Int64, ::Int64, ::Function, ::Function, ::IOContext{Base.AbstractIOBuffer{Array{UInt8,1}}}, ::MIME{Symbol("image/png")}, ::Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2}) at /home/mose/.julia/v0.6/Images/src/showmime.jl:35
 [6] verbose_show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::MIME{Symbol("image/png")}, ::Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2}) at ./multimedia.jl:42
 [7] _binreprmime at ./multimedia.jl:71 [inlined]
 [8] reprmime(::MIME{Symbol("image/png")}, ::Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2}) at ./multimedia.jl:61
 [9] reprmime(::String, ::Array{ColorTypes.Gray{FixedPointNumbers.Fixed{Int8,7}},2}) at ./multimedia.jl:98

No problem with Matrix{Gray{Normed{T,N}}}, for unsigned integers, nor with Matrix{Gray{AbstractFloat}} for plain floating numbers (including negative values)

The problem is, the image values are meaningful for the range of <0, max_type_value> or <0,1> for floating point images. So you have to at least convert to UInt.

Why does a Matrix{Gray{AsbtractFloat}} with values outside [0,1] work? :confused:

I thought that Fixed type would have taken care of mapping the signed integers to numbers that can be digested by Images.jl, just like Normed for unsigned integers.

How to map signed integers to unsigned integers? Yesterday I came up with a method to do this (just add typemax(Int) + 1), but doesn’t work with Int64.

one way is:

using Images
img = rand(Int, 5, 5)
imadjustintensity(Float64.(img), extrema(img))

But it depends on what your minimum, maximum, and middle is.

By adding typemax(Int)+1 to an Int64 you would exceed the types typemax.
Use UInt(n+BigInt(typemax(Int)+1)) instead.

Why not imadjustintensity(Float64.(img), (typemin(Int), typemax(Int))) ?

Not “not”, as I said:

I’d like to avoid converting to float. Images can be quite large, so converting a Int8 to a Float64 is a waste of memory, in addition float numbers can convert precisely only numbers up to maxintfloat. This is for scientific purposes, I don’t want to leave any bit behind

I know I could use BigInt to overcome the limitation, but, as I said before, Images can be large and involving BigInt isn’t ideal.

Really, Fixed type looks just what I want, it transparently maps signed integer to another type of the same size compatible with Color types, I just don’t understand why I can’t create a PNG image out of a Matrix{Gray{Fixed}}.

julia> a = Int8[-123, 5]
julia> to_uint8(a::AbstractArray{T,N}) where {T<:Int8, N} = xor.(reinterpret.(UInt8, a, 0x80))

Now you can reinterpret to fixedpoint

Three-argument reinterpret doesn’t exist. Probably should have been

to_uint8(a::AbstractArray{T,N}) where {T<:Int8, N} = xor.(reinterpret.(UInt8, a), 0x80)

?

Yes. A typo.

Ok, thanks, your function is the mapping I was looking for. This doesn’t require higher-precision integers for the conversion.

But still, I’d like to know if there is a fundamental limitation in Matrix{Gray{Fixed}} or one just needs to write the code needed to convert the matrix to PNG.