# How can I register/align shifted image stacks?

**URL:** <https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244>\
**Category:** General Usage\
**Tags:** images\
**Created:** [September 27, 2019, 8:05pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244 "2019-09-27T20:05:22Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [February 25, 2020, 6:29pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/21 "2020-02-25T18:29:34Z")

</div>

[GitHub - HolyLab/RegisterDeformation.jl: Representation of spatial deformations](https://github.com/HolyLab/RegisterDeformation.jl) (it’s linked from [here](https://holylab.github.io/BlockRegistration.jl/stable/#Orienting-yourself-1))

---

<div class="post-metadata">

**Author:** ![tobias.knopp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobias.knopp/32/7551_2.png) [@tobias.knopp](https://discourse.julialang.org/u/tobias.knopp)\
**Post date:** [February 26, 2020, 8:03am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/22 "2020-02-26T08:03:07Z")

</div>

~~Sorry to bug you yet another time. I had a look at the deformation package and it seems that that focuses on the part where one applies the transformation and not the registration part of it. Or is that the part where BlockRegistration plugs in and solves the inverse problem?~~

Sorry, just tried it our and I seem to got it. The ϕs in the cookbook is actually the deformed grid and your link just was the documentation on the GridDeformation type.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [February 26, 2020, 8:32am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/23 "2020-02-26T08:32:03Z")

</div>

It’s a whole suite of tools. You asked about the displacement field, which is implemented and documented in RegisterDeformation. The other packages are built on top of that, and optimize the displacement field to approximately minimize the mean-square mismatch. The order in [this list](https://holylab.github.io/BlockRegistration.jl/stable/#Orienting-yourself-1) is roughly the “stacking order”: RegisterOptimize is the top-level package which minimizes the mismatch as a function of the displacement field.

Just step your way through the demo in the BlockRegistration documentation, I think it will become far clearer then.

---

<div class="post-metadata">

**Author:** ![tobias.knopp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobias.knopp/32/7551_2.png) [@tobias.knopp](https://discourse.julialang.org/u/tobias.knopp)\
**Post date:** [February 26, 2020, 8:33am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/24 "2020-02-26T08:33:17Z")

</div>

yes, thanks. Yesterday I had issues installing the HolyLabRegistry and was not able to step through. Now it works fine!

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [February 26, 2020, 8:38am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/25 "2020-02-26T08:38:03Z")

</div>

Great. Keep in mind that most of BlockRegistration was designed to handle the multi-terabtye datasets of light sheet microscopy, and it compromises on quality for the sake of speed. The key tradeoff is best documented in [Guaranteed improvement? · Issue #8 · HolyLab/RegisterOptimize.jl · GitHub](https://github.com/HolyLab/RegisterOptimize.jl/issues/8). If you want high quality, expect to have to polish its solutions a bit (and there’s a whole documentation page on what you can do to polish them, though it doesn’t mention options like “use a different package” e.g. ANTSRegistration.jl). On the other hand, it has some fairly innovative features that mimic some of the benefits of global optimization, so it might be quite useful for getting into a good configuration for polishing.

---

<div class="post-metadata">

**Author:** ![tobias.knopp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobias.knopp/32/7551_2.png) [@tobias.knopp](https://discourse.julialang.org/u/tobias.knopp)\
**Post date:** [February 26, 2020, 8:44am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/26 "2020-02-26T08:44:07Z")

</div>

I am not sure I fully understand what you mean by quality. If I understand correctly you are using a low resolution deformation grid, which inherently leads to regularization since the deformation cannot be arbitrarily fine. But isn’t it possible to change the blocksize and in that way control the resolution of the deformation?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [February 26, 2020, 8:49am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/27 "2020-02-26T08:49:02Z")

</div>

As stated in that issue link, the modeling of the effect of the deformation is “prospective” rather than “retrospective”: it tries to predict the effect of applying the deformation using the [fourier shift theorem](https://en.wikipedia.org/wiki/Fourier_transform#Translation_/_time_shifting) without actually calculating it precisely. That introduces some additional inaccuracy but makes it blazingly fast (for a voxelwise mean square error algorithm).

While the reasons are not fully described in the Documenter documentation, the issue of accuracy is extensively discussed in [Improving alignment · BlockRegistration.jl](https://holylab.github.io/BlockRegistration.jl/stable/improving/)

---

<div class="post-metadata">

**Author:** ![tobias.knopp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobias.knopp/32/7551_2.png) [@tobias.knopp](https://discourse.julialang.org/u/tobias.knopp)\
**Post date:** [February 26, 2020, 8:55am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/28 "2020-02-26T08:55:58Z")

</div>

ok, so since time is no issue in my application it seems I want to go with the `RegisterHindsight` approach. I will give this a spin and show how far I get. After going through the cookbook I am pretty impressed what I have seen so far! Well done.

Just out of interest: Is there a mathematical/algorithmical paper out there on the approach you are using here? Or is that new stuff?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [February 26, 2020, 8:56am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/29 "2020-02-26T08:56:51Z")

</div>

It’s new. I always seem to prefer to write software over papers.

---

<div class="post-metadata">

**Author:** ![tobias.knopp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobias.knopp/32/7551_2.png) [@tobias.knopp](https://discourse.julialang.org/u/tobias.knopp)\
**Post date:** [February 26, 2020, 9:03am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/30 "2020-02-26T09:03:34Z")

</div>

The community is appreciating that! 🙂 My colleagues in my group are always joking that there have to be multiple Tim Holys since you have created such a multitude of awesome Julia packages.

---

<div class="post-metadata">

**Author:** ![Zach\_Christensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zach_christensen/32/7220_2.png) [@Zach\_Christensen](https://discourse.julialang.org/u/Zach_Christensen)\
**Post date:** [February 26, 2020, 3:24pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/31 "2020-02-26T15:24:22Z")

</div>

@tim.holy, thanks for the great work! Some day I’d like to have a really thorough comparison of brain registration using a Julia based pipeline compared to an ANTs pipeline. I don’t know if I’ll ever get the time to take the lead on something like that, but maybe I can convince someone in the biophysics department to do it at some point.

---

<div class="post-metadata">

**Author:** ![Moses](https://avatars.discourse-cdn.com/v4/letter/m/278dde/32.png) [@Moses](https://discourse.julialang.org/u/Moses)\
**Post date:** [March 24, 2020, 9:36am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/32 "2020-03-24T09:36:15Z")

</div>

@tim.holy We are now frequently using the QuadDirect algorithm in our lab. However, I got a strange issue on one computer, after I ran the update-command for the packages. As far as I can remember, `RegisterQD` was not among the updated packages. Neither did I update my `julia` installation (still version `1.0.5`).

Minimal (non-)working sample

```julia
julia> using RegisterQD
julia> pseudofixed=rand(10,10)
julia> tfm, mm = qd_rigid(pseudofixed, pseudofixed, (5,5), 0.1, print_interval=typemax(Int))

```

On a machine that was not updated, the last command generates the expected result  
`(AffineMap([1.0 -0.0; 0.0 1.0], [0.0, 0.0]), 0.0)`

However, the updated machine first throws a deprecation warning and then an error

```julia
julia> tfm, mm = qd_rigid(pseudofixed, pseudofixed, (5,5), 0.1, print_interval=typemax(Int))
┌ Warning: `CenterIndexedArray(::Type{T}, dims) where T` is deprecated, use `CenterIndexedArray{T}(undef, dims...)` instead.
│ caller = Type at RegisterCore.jl:360 [inlined]
└ @ Core ~/.julia/packages/RegisterCore/0P3yV/src/RegisterCore.jl:360
ERROR: UndefVarError: shiftrange not defined
Stacktrace:
 [1] #mismatch#5(::Symbol, ::Function, ::Type{Float64}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,OffsetArrays.OffsetArray{Float64,2,Array{Float64,2}},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::Tuple{Int64,Int64}) at /home/moses/.julia/packages/RegisterMismatch/kKJng/src/RegisterMismatch.jl:120
 [2] #mismatch at ./subarray.jl:0 [inlined]
 [3] #mismatch#1 at /home/moses/.julia/packages/RegisterMismatchCommon/Q3oNz/src/RegisterMismatchCommon.jl:15 [inlined]
 [4] #mismatch at ./none:0 [inlined]
 [5] #best_shift#1(::Symbol, ::CoordinateTransformations.IdentityTransformation, ::Function, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,OffsetArrays.OffsetArray{Float64,2,Array{Float64,2}},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::Tuple{Int64,Int64}, ::Float64) at /home/moses/.julia/packages/RegisterQD/ZBH17/src/util.jl:29
 [6] #best_shift at ./none:0 [inlined]
 [7] #rigid_mm_fast#14(::CoordinateTransformations.IdentityTransformation, ::Function, ::Array{Float64,1}, ::Tuple{Int64,Int64}, ::Array{Float64,2}, ::Array{Float64,2}, ::Float64, ::LinearAlgebra.UniformScaling{Bool}) at /home/moses/.julia/packages/RegisterQD/ZBH17/src/rigid.jl:74
 [8] #rigid_mm_fast at ./none:0 [inlined]
 [9] f at /home/moses/.julia/packages/RegisterQD/ZBH17/src/rigid.jl:92 [inlined]
 [10] split!(::QuadDIRECT.Box{Float64,1}, ::getfield(RegisterQD, Symbol("#f#17")){LinearAlgebra.UniformScaling{Bool},CoordinateTransformations.IdentityTransformation,Float64,Array{Float64,2},Array{Float64,2},Tuple{Int64,Int64}}, ::Array{Float64,1}, ::Int64, ::Array{Float64,1}, ::Float64, ::Float64, ::Float64, ::Float64) at /home/moses/.julia/packages/QuadDIRECT/uCBvD/src/algorithm.jl:48
 [11] _init(::Function, ::Array{Float64,1}, ::Tuple{Array{Float64,1}}, ::Array{Float64,1}, ::Array{Float64,1}) at /home/moses/.julia/packages/QuadDIRECT/uCBvD/src/algorithm.jl:31
 [12] init at /home/moses/.julia/packages/QuadDIRECT/uCBvD/src/algorithm.jl:20 [inlined]
 [13] #analyze#29(::Base.Iterators.Pairs{Symbol,Any,NTuple{5,Symbol},NamedTuple{(:minwidth, :print_interval, :maxevals, :atol, :rtol),Tuple{Array{Float64,1},Int64,Float64,Int64,Float64}}}, ::Function, ::Function, ::Tuple{Array{Float64,1}}, ::Array{Float64,1}, ::Array{Float64,1}) at /home/moses/.julia/packages/QuadDIRECT/uCBvD/src/algorithm.jl:515
 [14] (::getfield(QuadDIRECT, Symbol("#kw##analyze")))(::NamedTuple{(:minwidth, :print_interval, :maxevals, :atol, :rtol),Tuple{Array{Float64,1},Int64,Float64,Int64,Float64}}, ::typeof(QuadDIRECT.analyze), ::Function, ::Tuple{Array{Float64,1}}, ::Array{Float64,1}, ::Array{Float64,1}) at ./none:0
 [15] #_analyze#7(::Base.Iterators.Pairs{Symbol,Any,NTuple{5,Symbol},NamedTuple{(:minwidth, :print_interval, :maxevals, :atol, :rtol),Tuple{Array{Float64,1},Int64,Float64,Int64,Float64}}}, ::Function, ::Function, ::Array{Float64,1}, ::Array{Float64,1}) at /home/moses/.julia/packages/RegisterQD/ZBH17/src/util.jl:207
 [16] (::getfield(RegisterQD, Symbol("#kw##_analyze")))(::NamedTuple{(:minwidth, :print_interval, :maxevals, :atol, :rtol),Tuple{Array{Float64,1},Int64,Float64,Int64,Float64}}, ::typeof(RegisterQD._analyze), ::Function, ::Array{Float64,1}, ::Array{Float64,1}) at ./none:0
 [17] #qd_rigid_coarse#16(::LinearAlgebra.UniformScaling{Bool}, ::CoordinateTransformations.IdentityTransformation, ::Float64, ::Base.Iterators.Pairs{Symbol,Int64,Tuple{Symbol},NamedTuple{(:print_interval,),Tuple{Int64}}}, ::Function, ::Array{Float64,2}, ::Array{Float64,2}, ::Tuple{Int64,Int64}, ::Array{Float64,1}, ::Array{Float64,1}) at /home/moses/.julia/packages/RegisterQD/ZBH17/src/rigid.jl:95
 [18] #qd_rigid_coarse at ./none:0 [inlined]
 [19] #qd_rigid#20(::LinearAlgebra.UniformScaling{Bool}, ::Array{Float64,1}, ::Float64, ::CoordinateTransformations.IdentityTransformation, ::Int64, ::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::Function, ::Array{Float64,2}, ::Array{Float64,2}, ::Tuple{Int64,Int64}, ::Float64) at /home/moses/.julia/packages/RegisterQD/ZBH17/src/rigid.jl:189
 [20] (::getfield(RegisterQD, Symbol("#kw##qd_rigid")))(::NamedTuple{(:print_interval,),Tuple{Int64}}, ::typeof(qd_rigid), ::Array{Float64,2}, ::Array{Float64,2}, ::Tuple{Int64,Int64}, ::Float64) at ./none:0
 [21] top-level scope at none:0

```

As I did not update `julia` itself, I do not understand that I get a deprecation. As far as I can see, RegisterQD has not seen any updates recently, either.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [March 24, 2020, 10:32pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/33 "2020-03-24T22:32:25Z")

</div>

Thanks for the report, this turned out to be quite the mess. As you presumably know, RegisterQD uses [HolyLabRegistry](https://github.com/HolyLab/HolyLabRegistry) in addition to General, as it requires a number of other lab packages. This turned out to be a problem stemming from the bad old days before we got @GunnarFarneback’s wonderful [LocalRegistry](https://github.com/GunnarFarneback/LocalRegistry.jl), when our releases were crafted by editing HolyLabRegistry manually. When I switched to LocalRegistry, a mistake I’d made months prior led me to inadvertently overwriting version 0.2.1 of [RegisterMismatchCommon](https://github.com/HolyLab/RegisterMismatchCommon.jl).

I believe I’ve untangled this successfully, both for older releases and the latest. You said you’re using Julia 1.0.5; the more recent dependencies won’t work on Julia 1.0, but I believe I sorted out all the retrospective bounds. I have tested installation from a “clean” package depot on both Julia 1.4 and Julia 1.0, and it all seems to work. Please try updating your packages and let me know if it works. (If you get Pkg errors try removing and then re-adding packages.)

---

<div class="post-metadata">

**Author:** ![Moses](https://avatars.discourse-cdn.com/v4/letter/m/278dde/32.png) [@Moses](https://discourse.julialang.org/u/Moses)\
**Post date:** [March 25, 2020, 6:49am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/34 "2020-03-25T06:49:05Z")

</div>

Thank you for the fast solution. It works again.

Yes, I am still on version 1.0.5, because this is currently marked as LTS.

---

<div class="post-metadata">

**Author:** ![tbenst](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbenst/32/3029_2.png) [@tbenst](https://discourse.julialang.org/u/tbenst)\
**Post date:** [September 28, 2020, 9:03am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/35 "2020-09-28T09:03:21Z")

</div>

@tim.holy thanks for this really cool package! I’d like to use RegisterQD to rigidly transform my data to a common brain atlas, however the pixel units are modestly different (0.88um vs 1.00um). Right now I use CMTK using commands described in [Figure S2](https://pubmed.ncbi.nlm.nih.gov/29103613/) to perform a rigid followed by non-rigid step, but I’m attracted to the global optimality that your algorithm strives for as well as easy integration with Julia code.

I am using AxisArrays with properly specified units but I _think_ RegisterQD assumes units are consistent between fixed and “moving”? What’s the recommended approach here–resample the data so axes have same units? I’m planning on just using `imresize` unless you would recommend a different way.

Thank you!

Edit: I think things are working properly with `qd_affine` although need to test on higher quality data ;). One thing I’m confused by is what the second value in the returned tuple is, so for example for this return value:  
`(AffineMap([1.2079968439617563 -0.0601021283777167; -0.062482595377332216 1.2539712136955676], [-160.24406067251462, -428.64275616465136]), 0.8871258679764823)`, I understand that the first 2x2 matrix is rotation / stretch, the second 2 vector is transaltion, but what is the third number (0.88)?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 28, 2020, 10:01pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/36 "2020-09-28T22:01:59Z")

</div>

You shouldn’t need to resample, the package is already set up to handle that. Just set the `SD` argument to `qd_rigid`. You can probably use `ps = pixelspacing(img); SD = ps./maximum(ps)` to get a numerical value free of physical units.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 29, 2020, 7:46am UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/37 "2020-09-29T07:46:11Z")

</div>

> [@tbenst](#):
>
> but what is the third number (0.88)

That’s the `mm` (mismatch) that is described at some places and not others (oops) in the docs. It’s essentially the average square difference, considering only the “inbounds” voxels (i.e., only those voxels for which the fixed and aligned moving images overlap).

---

<div class="post-metadata">

**Author:** ![tbenst](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbenst/32/3029_2.png) [@tbenst](https://discourse.julialang.org/u/tbenst)\
**Post date:** [September 29, 2020, 12:05pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/38 "2020-09-29T12:05:43Z")

</div>

Thanks! For SD, should I use the pixel spacing from “fixed” or “moving”? Ie the atlas is (0.78um, 0.78um, 2um) and my data is (1um, 1um, 1um)

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 29, 2020, 12:49pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/39 "2020-09-29T12:49:38Z")

</div>

Oh, sorry, I misunderstood; I thought they were the same but not isotropic. (The definition of a “rigid transformation” needs to change if space is not sampled isotropically.) I’m afraid you’ll have to resample to make them agree with one another, but they don’t have to be isotropic.

---

<div class="post-metadata">

**Author:** ![bourquard](https://avatars.discourse-cdn.com/v4/letter/b/57b2e6/32.png) [@bourquard](https://discourse.julialang.org/u/bourquard)\
**Post date:** [October 14, 2020, 11:13pm UTC](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244/40 "2020-10-14T23:13:02Z")

</div>

@tim.holy `ANTsRegistration.jl` is a great package, thanks for your contribution. I would have the following question: while `imgw = register(fixed, moving, pipeline; kwargs...)` allows to obtain the registered image as output, is there a simple way to get the corresponding geometrical-transformation information (which would be akin to the `imregtform` function in Matlab)? In case this is relevant to a potential solution, I am specifically interested in the simplest case of rigid registration (more specifically translation). Any help would be very much appreciated.

[Previous page](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244.md?page=1)

[Next page](https://discourse.julialang.org/t/how-can-i-register-align-shifted-image-stacks/29244.md?page=3)
