How to warp large arrays (Tiff stacks) efficiently?

Hello,

In order to avoid the XY-problem I will try to provide a context and a desired result first, and only later some particular questions.

Context: I don’t have much experience in Julia and (partially in order to fix that) I am making an application for analysis of the fast camera footage now. The raw files are collections of Tiffs or Tiff-stacks with file size around several GB. Camera captures 3 different projections of the same object which are filtered through different optical filters. These images are combined side-by-side (via optics) on a single camera sensor. The analysis is based on relation of spectral intensities captured from the object. This implies that 3 ROIs have to be selected from raw footage, precisely aligned and pixel-wise divided one by another.

Algorithm:

  1. Load raw footage in a camera-native format (12bit) → N4f12 to minimize memory footprint.
    Output: Array{N4f12,3}
  2. Crop out and align 3 ROIs → WarpedView.
    Expected output: 3 of view(::Array{N4f12,3})
  3. Apply same transformations to a whole-sensor calibration image (this time with allocation) → warp. Expected output: 3 Array{N4f12,2}
  4. Divide (pixel-wise) (for calibration) cropped views of stacks (2) by the corresponding calibration matrices (3)
    Output: 3 of Array{Float32,3}. This will be an intermediate result that should be saved, hence the allocation.
  5. Perform pixel-wise division of stacks yielding 2 ratio (e.g. for stack a,b,c → a/b, b/c)
    Output: 2 of Array{Float32,3}

I was successful implementing a MWE which did allocations at every stage of the process and worked on a rather small test-stack (30 frames). Now the intention is to substitute views where appropriate in order to make it to work with the full size files, (preferably without freezing my laptop :upside_down_face:).

Initial naive implementation of (2) was:

for i = 1:size(normed_stack,3)
      L1[:,:,i] = warp(normed_stack[:,:,i], L1trfm,          crop_size);
      L2[:,:,i] = warp(normed_stack[:,:,i], L2trfm,          crop_size);
      L3[:,:,i] = warp(normed_stack[:,:,i], L3trfm,          crop_size);
end

However as this the first time working with views in Julia I am not quite sure how I can return a reference to it after iterating? In order to iterate over it and/or append to it is should be declared in some way, but I don’t quite get how does one declare an empty view? :melting_face:
This is what I am trying to do:

using ImageTransformtations, Rotations, CoordinateTransformations

rawstack = rand(UInt16, (100,600,10)) # Just 10 frames for now 
trfm1=  recenter(RotMatrix(pi/128), center(rawstack[:,:,1]))  ∘ Translation(-10, 20) 

function warpncrop(rawstack::AbstractArray, trfm::Transformation, cropsize::NTuple{2,Int})
      for (i,slc) in enumerate(eachslice(rawstack, dims=3))
              wv[:,:,i] = WarpedView(slc, trfm, cropsize) 
      end
      return wv
end

I am not insisting on that particular implementation and will welcome any comment on the general approach as well.

Thanks in advance and all the best,
Igor

Hi @gltchr, welcome.

Are these geotiff files by any chance?

Hello Julio!

Nope,
just plain tiff files from a Photron camera if that helps…

1 Like

If you can’t find an answer here, you can try to start a discussion on the Images.jl project: