This provides a Julia wrapper around the Advanced Normalization Tools image registration and motion correction suite.
] add https://github.com/timholy/ANTsRegistration.jl
Image data and file format
If you are passing the data via filenames, ensure that you have stored your images in an ITK-readable format. NRRD is recommended. For those performing acquisition with Imagine, you can write out an NRRD header.
Unfortunately, the NRRD format lacks a file-validator, and a few aspects of the standard description seem to leave room for interpretation. If you encounter bugs, it is possible that Julia and ITK differ with respect to the implementation of the NRRD header. Try copying the command and running it the shell prompt, then report the error here.
The process of registering images can be broken down into stages, and multiple stages can be cascaded together. A stage is specified as follows:
stage = Stage(fixedimg, transform, metric=MI(), shrink=(8,4,2,1), smooth=(3,2,1,0), iterations=(1000,500,250,5))
The transform might be one of the following:
transform = Global("Rigid") transform = Global("Affine") transform = Syn()
The last one is for a diffeomorphism (warping) registration.
stage uses the
MI metric for comparing the two
MI is short for
a generalization of the notion of cross-correlation. This can be a
good choice particularly when the images differ in ways other than
just a spatial transformation, for example when they may be collected
by different imaging modalities or exhibit intensity differences due
to calcium transients. (With
MI you can optionally specify various
parameters such as the number of histogram bins.) Alternatively you
MeanSquares (where the images are compared based on their
CC (which stands for neighborhood cross
Finally, the last arguments in the example above indicate that we want to use a 4-level registration. For the first (coarsest) level, the image will be shrunk by a factor of 8, smoothed over a 3-pixel radius, and then aligned, allowing the parameters to be tweaked up to 1000 times when trying to minimize the metric. Choosing to shrink can improve performance, because small image pairs require fewer pixelwise comparisons than large images, as long as you don't shrink so much that features useful for alignment are eliminated. Likewise, smoothing can help find a good minimum by increasing the size of the "attraction basin," as long as you don't blur out sharp features that actually aid alignment.
Once the rigid transformation has been found for this coarsest level,
it will be used to initialize the transformation for the next
level. The final level uses a
shrink of 1 (meaning to use the images
at their provided size), a
smooth of 0 (meaning no smoothing), and 5
iterations. This will ensure that the transformation doesn't miss
opportunities for sub-pixel alignment at the finest scale.
All parameters after
transform have default values, so you only need
to assign them if you need to control them more precisely.
Note on physical units: if your images have anisotropic resolution, you should strongly consider using physical units for your smoothing. For example,
using Unitful: μm smooth=(50μm,5μm)
would be appropriate for a two-iteration stage.
To register the single image
moving to the single image
imgw = register(fixed, moving, pipeline; kwargs...)
pipeline is a single
Stage or a vector of stages. For
example, you can begin with an affine registration followed by a
stageaff = Stage(fixed, Global("Affine")) stagesyn = Stage(fixed, Syn()) imgw = register(fixed, moving, [stageaff,stagesyn]; kwargs...)
This choice will align the images as well as possible (given the default parameters) using a pure-affine transformation, and then introduce warping where needed to improve the alignment.
If instead you'd like to correct for motion in an image sequence, consider
motioncorr((infofilename, warpedfilename), fixed, movingfilename, pipeline)
Here you represent the moving image sequence via its filename. The
first argument stores the names of the files to which the data should
be written. Of course, you can alternatively call
for each image in the series.
For more detailed information, see the help on individual types and functions.
Some notes for working on Windows
Currently, ANTs does not actively support Windows system, officially, they suggest using Linux subsystem instead.
And the latest binaries they built and released is 4 years old, and it may not work on every machine. Because of this, if you just install this package on Windows system and use the default Binary files downloaded by
deps.jl, it's highly possible it will not work.
However, it's tested ANTs can be built from source in Windows system. (Windows 10, VS 2017) (Although officially they suggest compiling under Linux subsystem ). Conceptually it's the same as compiling under MacOS / Linux. It majorly involves using
Cmake to determine the specificities of your machine / system and automatically generate the configuration file. And then using the compilers of Windows system (e.g. Visual Studio) to build the binary files from source.
After building from source (usually takes hours), you get your path containing the binary files (supposing it's
D:\ANTs_2.1.0_Windows_new_build\bin\Release). Then, we have to inform
ANTsRegistration.jl package of the correct location of the binary files. Currently, we have to do it by manually changing
deps.jl file generated when building this julia package.
For example, change the relevent lines in
deps.jl to be
const ants_bin_dir = raw"D:\ANTs_2.1.0_Windows_new_build\bin\Release\\" # use this variable for outside ANTs binary const ants = joinpath(ants_bin_dir, "ANTS.exe") #joinpath(dirname(@__FILE__), "usr\\bin\\ANTS.exe") const antsRegistration = joinpath(ants_bin_dir, "antsRegistration.exe") # joinpath(dirname(@__FILE__), "usr\\bin\\antsRegistration.exe") const antsMotionCorr = joinpath(ants_bin_dir, "antsMotionCorr.exe")
Then, hopefully, the package can pass all of the tests!
Note that if you rebuild the package (
Pkg.build("ANTsRegistration")) it is likely that these customizations will be overwritten. You are advised to save a copy of the modified
deps.jl file and/or change the permissions to prevent it from being overwritten.