Problem in reading TIFF floats file

When I read a tiff floats image (001.tif) using FileIO by real(load("001.tif")), I get a matrix with only 1.0 and 0.0 elements.

360×2304 Array{N0f32,2} with eltype FixedPointNumbers.N0f32:
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  …  1.0  1.0  1.0  1.0  1.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  0.0
 0.0  0.0  0.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 ⋮                        ⋮              ⋱                 ⋮              
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  0.0
 0.0  0.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  0.0  0.0

But using importdata('001.tif') in MATLAB, I can get its raw data as shown below, only (1:10,1:10)

ans =
  10×10 single matrix
            0            0            0            0            0            0            0            0            0       949.15
            0            0            0            0            0       872.99       864.98       862.22       911.13        940.6
            0            0            0          906       905.21       876.51        866.1       862.07       910.67        940.6
            0            0          869       887.65       905.21       876.51        866.1       862.07       910.67        940.6
            0            0          869       887.35       905.21       876.51        866.1       862.07       910.67        940.6
            0            0          869       887.35       905.21       876.51        866.1       862.07       910.67        940.6
            0            0          869       887.35       905.21       876.51        866.1       862.07       910.67        940.6
            0            0       871.22       887.35       905.21       876.51        866.1       862.07       910.67        940.6
            0            0       877.19       887.35       905.21       876.51        866.1       862.07       910.67        940.6
            0            0       877.19       887.35       905.21       876.51        866.1       862.07       910.67        931.8

The MATLAB result looks real. I wonder how I can get the same data as in MATLAB with Julia code. Thanks

Try

using GMT

G = gmtread("001.tif")
1 Like

Could you show us the current output of the following?

using Pkg
Pkg.status()
Pkg.status(mode=PKGMODE_MANIFEST)

@tlnagy any insights here? Are floating point tiffs aomething that TiffImages.jl can handle?

TiffImages.jl and ImageMagic.jl both seem to have be having some trouble accessing your tiff file. So I decided to create a new wrapper around libtiff via Clang.jl.

LibTIFF.jl

julia> using Pkg

julia> Pkg.add("https://github.com/mkitti/LibTIFF.jl")

julia> using LibTIFF

julia> function read_float32_tiff(filepath)
           tif = TIFFOpen(filepath, "r")
           _length = TIFFGetField(tif, TIFFTAG_IMAGELENGTH, Int32)
           _width = TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, Int32)
           _rowsperstrip = TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, Int32)
           _bitspersample = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, Int32)
           _samplesformat = TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, Int8)
           @info "Image fields" _length _width _rowsperstrip _bitspersample _samplesformat
           image = Matrix{Float32}(undef, _width, _length)
           for (i,col) in zip(0:_length÷_rowsperstrip, eachcol(image))
               TIFFReadEncodedStrip(tif, i, pointer(col), -1)
           end
           TIFFClose(tif)
           image = permutedims(image, (2,1))
           return image
       end
read_float32_tiff (generic function with 1 method)

The result looks like it matches the MATLAB example.

julia> read_float32_tiff("001.tif")[1:10, 1:10]
TIFFFetchNormalTag: Warning, ASCII value for tag "ImageDescription" does not end in null byte.
┌ Info: Image fields
│   _length = 360
│   _width = 2304
│   _rowsperstrip = 1
│   _bitspersample = 32
└   _samplesformat = 3
10×10 Matrix{Float32}:
 0.0  0.0    0.0      0.0      0.0      0.0      0.0      0.0      0.0    949.15
 0.0  0.0    0.0      0.0      0.0    872.994  864.982  862.223  911.128  940.604
 0.0  0.0    0.0    906.0    905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  869.0    887.646  905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  869.0    887.351  905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  869.0    887.351  905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  869.0    887.351  905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  871.225  887.351  905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  877.187  887.351  905.209  876.51   866.097  862.072  910.672  940.604
 0.0  0.0  877.187  887.351  905.209  876.51   866.097  862.072  910.672  931.802

TiffImages.jl

TiffImages.jl does not seem to recognize the file and errors with the following.

ERROR: MethodError: no method matching interpretation(::Val{TiffImages.PHOTOMETRIC_MINISWHITE})
Stack trace for TiffImages.jl
ERROR: MethodError: no method matching interpretation(::Val{TiffImages.PHOTOMETRIC_MINISWHITE})

Closest candidates are:
  interpretation(::TiffImages.PhotometricInterpretations, ::Val{TiffImages.EXTRASAMPLE_UNSPECIFIED}, ::Val{4})
   @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:63
  interpretation(::TiffImages.PhotometricInterpretations, ::TiffImages.ExtraSamples, ::Int64)
   @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:45
  interpretation(::TiffImages.PhotometricInterpretations)
   @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:38
  ...

Stacktrace:
  [1] interpretation(p::TiffImages.PhotometricInterpretations)
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:38
  [2] interpretation(p::TiffImages.PhotometricInterpretations, extrasamples::TiffImages.ExtraSamples, samplesperpixel::Int64)
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:46
  [3] interpretation(ifd::TiffImages.IFD{UInt32})
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:29
  [4] getcache(ifd::TiffImages.IFD{UInt32})
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/layout.jl:91
  [5] load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOStream, String}}, ifds::Vector{TiffImages.IFD{UInt32}}, ::Nothing; verbose::Bool)
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:71
  [6] load
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:69 [inlined]
  [7] load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOStream, String}}; verbose::Bool, mmap::Bool, lazyio::Bool)
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:36
  [8] load(tf::TiffImages.TiffFile{UInt32, Stream{DataFormat{:TIFF}, IOStream, String}})
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:18
  [9] load(io::IOStream; kwargs::@Kwargs{})
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:17
 [10] load
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:17 [inlined]
 [11] #13
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:13 [inlined]
 [12] open(::TiffImages.var"#13#14"{@Kwargs{}}, ::String, ::Vararg{String}; kwargs::@Kwargs{})
    @ Base ./io.jl:396
 [13] open
    @ Base ./io.jl:393 [inlined]
 [14] #load#12
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:12 [inlined]
 [15] load(filepath::String)
    @ TiffImages ~/.julia/packages/TiffImages/yETMK/src/load.jl:11
 [16] #invokelatest#2
    @ Base ./essentials.jl:950 [inlined]
 [17] invokelatest
    @ Base ./essentials.jl:947 [inlined]
 [18] #_#1
    @ LazyModules ~/.julia/packages/LazyModules/d9Be6/src/LazyModules.jl:29 [inlined]
 [19] (::LazyModules.LazyFunction)(args::String)
    @ LazyModules ~/.julia/packages/LazyModules/d9Be6/src/LazyModules.jl:27
 [20] load(f::File{DataFormat{:TIFF}, String}; canonicalize::Nothing, kwargs::@Kwargs{})
    @ ImageIO ~/.julia/packages/ImageIO/XthUV/src/ImageIO.jl:111
 [21] load(f::File{DataFormat{:TIFF}, String})
    @ ImageIO ~/.julia/packages/ImageIO/XthUV/src/ImageIO.jl:109
 [22] #invokelatest#2
    @ Base ./essentials.jl:950 [inlined]
 [23] invokelatest
    @ Base ./essentials.jl:947 [inlined]
 [24] action(::Symbol, ::Vector{Union{Base.PkgId, Module}}, ::Formatted; options::@Kwargs{})
    @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:219
 [25] action
    @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:196 [inlined]
 [26] action
    @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:185 [inlined]
 [27] load(::String; options::@Kwargs{})
    @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:113
 [28] load(::String)
    @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:109
 [29] top-level scope
    @ REPL[13]:1
Stacktrace:
 [1] handle_error(e::MethodError, q::Base.PkgId, bt::Vector{Union{Ptr{Nothing}, Base.InterpreterIP}})
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/error_handling.jl:61
 [2] handle_exceptions(exceptions::Vector{Tuple{Any, Union{Base.PkgId, Module}, Vector}}, action::String)
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/error_handling.jl:56
 [3] action(::Symbol, ::Vector{Union{Base.PkgId, Module}}, ::Formatted; options::@Kwargs{})
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:228
 [4] action
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:196 [inlined]
 [5] action
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:185 [inlined]
 [6] load(::String; options::@Kwargs{})
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:113
 [7] load(::String)
   @ FileIO ~/.julia/packages/FileIO/BE7iZ/src/loadsave.jl:109
 [8] top-level scope
   @ REPL[13]:1

ImageMagick.jl

ImageMagick.jl seems to read the file as a 32-bit Int TIFF as the original poster displayed.

julia> load("/home/mkitti/Downloads/001.tif")
[ Info: Precompiling ImageMagick [6218d12a-5da1-5696-b52f-db25d2ecc6d1]
┌ Warning: some versions of ImageMagick give spurious low-order bits for 32-bit TIFFs
└ @ ImageMagick ~/.julia/packages/ImageMagick/KDZC2/src/ImageMagick.jl:99
2 Likes

Thanks! It works.
(The time loading GMT may be the only pain…:sweat_smile:)

Excellent update! Hope it can be a built-in function.
Suh TIFF file was produced by Fit2D (a software processing diffraction images). It’s wired that no one using these packages has encountered this problem before (perhaps because Tiff float isn’t popular).

Hmmm, how long? It’s quite fast for me

julia> cd("c:/v"); @time using GMT
  0.696514 seconds (610.75 k allocations: 43.228 MiB, 12.31% gc time, 1.25% compilation time)

julia> @time gmtread("lixo.tiff");
  0.194171 seconds (43.40 k allocations: 3.930 MiB, 45.89% compilation time)

julia> @time gmtread("lixo.tiff");
  0.094855 seconds (160 allocations: 1.021 MiB)

On the contrary, they are very popular in Earth Sciences.

This is a halfway solution. Really we should have another kind of “color” than “gray” that represents when the minimum is meant to be white.

julia> using Pkg

julia> Pkg.activate(; temp = true)
  Activating new project at `/tmp/jl_vRiPuv`

julia> Pkg.add(url="https://github.com/mkitti/TiffImages.jl", rev="mkitti-miniswhite")

julia> using TiffImages

julia> TiffImages.load("/home/mkitti/Downloads/001.tif")[1:10,1:10]
10×10 Array{Gray{Float32},2} with eltype ColorTypes.Gray{Float32}:
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(0.0)      …  Gray{Float32}(0.0)      Gray{Float32}(949.15)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(0.0)         Gray{Float32}(911.128)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(0.0)         Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(869.0)       Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(869.0)       Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(869.0)    …  Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(869.0)       Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(871.225)     Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(877.187)     Gray{Float32}(910.672)  Gray{Float32}(940.604)
 Gray{Float32}(0.0)  Gray{Float32}(0.0)  Gray{Float32}(877.187)     Gray{Float32}(910.672)  Gray{Float32}(931.802)

xref: Interpret PHOTOMETRIC_MINISWHITE as Gray by mkitti · Pull Request #134 · tlnagy/TiffImages.jl · GitHub

1 Like

Nice! Yeah, I never implemented inverted TIFFs because there wasn’t a convenient way to represent them in the Colors.jl ecosystem. Your PR works for now, but I agree that an inverted color type would be most faithful to the underlying data.