You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This package aligns a time series of 3D MRI images with similar contrast, following the seminal paper by [Friston et al.](https://doi.org/10.1002/hbm.460030303) It minimizes the squared difference between the images in a given mask. The package was heavily inspired by [SPM's](https://www.fil.ion.ucl.ac.uk/spm/)`spm_realign` function. The principal advantage over `spm_realign` is speed. Additionally, we implemented a *consensus* estimation, which aligns all time frames pairwise and uses [iteratively reweighted least squares](https://en.wikipedia.org/wiki/Iteratively_reweighted_least_squares) to calculate a consensus between all estimates. Compared to a single reference time frame, the consensus approach is less sensitive to the image quality of the reference frame. Compared with the *mean* time frame as a reference, it avoids difficulties in mapping to a blurred reference.
10
-
11
-
## Quick Tutorial
9
+
MRIRealign.jl performs rigid-body (6-DOF) motion correction for 4-D MRI
10
+
time-series data. It estimates three rotation angles and three
11
+
translations per volume by minimizing the sum of squared intensity
12
+
differences, then reslices (resamples) the volumes to undo the estimated
13
+
motion.
14
+
15
+
The algorithm follows the seminal paper by
16
+
[Friston et al.](https://doi.org/10.1002/hbm.460030303) and was heavily
17
+
inspired by [SPM's](https://www.fil.ion.ucl.ac.uk/spm/)`spm_realign`
18
+
function. Key differences from SPM include:
19
+
20
+
***Speed** — a Gauss–Newton trust-region optimizer with exact analytic
21
+
Jacobians of the rotation matrix converges in fewer iterations than
22
+
SPM's re-estimation loop.
23
+
***Consensus estimation** — all time frames are aligned pairwise and a
24
+
robust weighted consensus is computed via
25
+
[iteratively reweighted least squares](https://en.wikipedia.org/wiki/Iteratively_reweighted_least_squares)
26
+
with geodesic rotation distance on SO(3). This is less sensitive to
27
+
the image quality of any single reference frame and avoids
28
+
difficulties in mapping to a blurred temporal mean.
29
+
30
+
## Quick Start
12
31
13
32
On Unix systems, Julia can be installed with
14
-
```Bash
33
+
```bash
15
34
curl -fsSL https://install.julialang.org | sh
16
35
```
17
36
18
37
and on Windows systems with
19
38
```
20
39
winget install --name Julia --id 9NJNWW8PVKMN -e -s msstore
21
40
```
22
-
More detailed installation instructions can be found [here](https://julialang.org/install/).
41
+
More detailed installation instructions can be found
42
+
[here](https://julialang.org/install/).
23
43
24
44
Thereafter, you can start Julia from the command line with
25
-
```Bash
45
+
```bash
26
46
julia
27
47
```
28
48
29
-
This section assumes that you have a folder at the path `/path_to_files/` with NIfTI files of the format `mask.nii` and `somename_1.nii`, `somename_2.nii`, ... . Our package does not include loading functions, allowing users to load data from [NIfTI](https://github.com/JuliaNeuroscience/NIfTI.jl), [DICOM](https://github.com/JuliaHealth/DICOM.jl), [Matlab](https://github.com/JuliaIO/MAT.jl), [HDF5](https://github.com/JuliaIO/HDF5.jl) files etc.
49
+
### Loading data
50
+
51
+
This tutorial assumes that you have a folder at the path
52
+
`/path_to_files/` with NIfTI files of the format `mask.nii` and
53
+
`somename_1.nii`, `somename_2.nii`, … . MRIRealign.jl does not include
[HDF5](https://github.com/JuliaIO/HDF5.jl) files, etc.
30
59
31
-
The first time, the packages need to be installed with the package manager:
60
+
Install the packages once:
32
61
33
-
```@Julia
62
+
```julia
34
63
using Pkg
35
64
Pkg.add("MRIRealign")
36
65
Pkg.add("NIfTI")
37
66
```
38
67
39
-
Thereafter, we can use them:
68
+
Then load them:
40
69
41
-
```@Julia
70
+
```julia
42
71
using MRIRealign
43
72
using NIfTI
44
73
```
45
74
46
-
We can change the directory
47
-
```@Julia
75
+
Change to the data directory:
76
+
77
+
```julia
48
78
cd("/path_to_files/")
49
79
```
50
80
51
-
and, optionally, load a mask and convert it to a binary mask
52
-
```@Julia
81
+
Optionally, load a mask and convert it to a `BitArray`:
82
+
83
+
```julia
53
84
mask =round.(Bool, niread("mask.nii"))
54
85
```
55
-
Note that the `.` after round indicates a point-wise operation.
56
86
57
-
We can create a list of file names in the current folder, except for `mask.nii`, and sort them in natural order, i.e., 1, 2, 3, ... instead of the ASCII order 1, 10, 100, 101, ... .
87
+
Create a sorted list of volume file names (natural numeric order):
58
88
59
-
```@Julia
89
+
```julia
60
90
files =filter(f ->isfile(f) && f !="mask.nii", readdir())
61
91
files =sort(files, by = file ->parse(Int, match(r"\d+", file).match))
62
92
```
63
93
64
-
Using the size of the mask, where `size(mask)...` returns the three dimensions separately, we can allocate an array and load all time frames into it:
94
+
Allocate a 4-D array and read all volumes into it:
Now we are all set to call the `realign!` function, which will overwrite `img` with the aligned volumes and return the motion parameters, i.e., 3 rotation and 3 translation parameters in this order:
75
-
```@Julia
104
+
### Estimating and applying motion correction
105
+
106
+
Call `realign!`, which overwrites `img` with the aligned volumes and
107
+
returns the motion parameters — a `(6, t)` matrix where each column is
108
+
`[rx, ry, rz, tx, ty, tz]` (rotations in radians, translations in
109
+
voxels):
110
+
111
+
```julia
76
112
params =realign!(img; mask=mask)
77
113
```
78
114
79
-
We can write the aligned images back to the NIfTI files:
80
-
```@Julia
115
+
Write the aligned images back to NIfTI files:
116
+
117
+
```julia
81
118
for t ineachindex(files)
82
119
ni =niread(files[t])
83
120
ni.raw .= img[:,:,:,t]
84
121
niwrite(files[t], ni)
85
122
end
86
123
```
87
124
88
-
Note that this tutorial assumes that the headers of all NIfTI files are identical and replaces the raw data with interpolated data. For changing the NIfTI header instead, we can call `params = realign!(img; mask=mask, realign=false)` and write `params` to the NIfTI header. For more information, refer to [the NIfTI.jl documentation](https://github.com/JuliaNeuroscience/NIfTI.jl).
125
+
### Estimate-only workflow
126
+
127
+
To estimate motion parameters without modifying the images:
128
+
129
+
```julia
130
+
params =realign!(img; mask=mask, realign=false)
131
+
```
132
+
133
+
The returned `params` can later be applied with the two-argument form:
134
+
135
+
```julia
136
+
realign!(img, params)
137
+
```
138
+
139
+
> **Note:** This tutorial assumes that the NIfTI headers of all files are
140
+
> identical and replaces the raw data with interpolated data. To update
141
+
> the NIfTI header instead (preserving the original voxel data), use
142
+
> `realign=false` and write the parameters into the header. See
This package aligns a time series of 3D MRI images with similar contrast, following the seminal paper by [Friston et al.](https://doi.org/10.1002/hbm.460030303) It minimizes the squared difference between the images in a given mask. The package was heavily inspired by [SPM's](https://www.fil.ion.ucl.ac.uk/spm/)`spm_realign` function. The principal advantage over `spm_realign` is speed. Additionally, we implemented a *consensus* estimation, which aligns all time frames pairwise and uses [iteratively reweighted least squares](https://en.wikipedia.org/wiki/Iteratively_reweighted_least_squares) to calculate a consensus between all estimates. Compared to a single reference time frame, the consensus approach is less sensitive to the image quality of the reference frame. Compared with the *mean* time frame as a reference, it avoids difficulties in mapping to a blurred reference.
8
-
9
-
## Quick Tutorial
7
+
MRIRealign.jl performs rigid-body (6-DOF) motion correction for 4-D MRI
8
+
time-series data. It estimates three rotation angles and three
9
+
translations per volume by minimizing the sum of squared intensity
10
+
differences, then reslices (resamples) the volumes to undo the estimated
11
+
motion.
12
+
13
+
The algorithm follows the seminal paper by
14
+
[Friston et al.](https://doi.org/10.1002/hbm.460030303) and was heavily
15
+
inspired by [SPM's](https://www.fil.ion.ucl.ac.uk/spm/)`spm_realign`
16
+
function. Key differences from SPM include:
17
+
18
+
***Speed** — a Gauss–Newton trust-region optimizer with exact analytic
19
+
Jacobians of the rotation matrix converges in fewer iterations than
20
+
SPM's re-estimation loop.
21
+
***Consensus estimation** — all time frames are aligned pairwise and a
22
+
robust weighted consensus is computed via
23
+
[iteratively reweighted least squares](https://en.wikipedia.org/wiki/Iteratively_reweighted_least_squares)
24
+
with geodesic rotation distance on SO(3). This is less sensitive to
25
+
the image quality of any single reference frame and avoids
26
+
difficulties in mapping to a blurred temporal mean.
27
+
28
+
## Quick Start
10
29
11
30
On Unix systems, Julia can be installed with
12
-
```Bash
31
+
```bash
13
32
curl -fsSL https://install.julialang.org | sh
14
33
```
15
34
16
35
and on Windows systems with
17
36
```
18
37
winget install --name Julia --id 9NJNWW8PVKMN -e -s msstore
19
38
```
20
-
More detailed installation instructions can be found [here](https://julialang.org/install/).
39
+
More detailed installation instructions can be found
40
+
[here](https://julialang.org/install/).
21
41
22
42
Thereafter, you can start Julia from the command line with
23
-
```Bash
43
+
```bash
24
44
julia
25
45
```
26
46
27
-
This section assumes that you have a folder at the path `/path_to_files/` with NIfTI files of the format `mask.nii` and `somename_1.nii`, `somename_2.nii`, ... . Our package does not include loading functions, allowing users to load data from [NIfTI](https://github.com/JuliaNeuroscience/NIfTI.jl), [DICOM](https://github.com/JuliaHealth/DICOM.jl), [Matlab](https://github.com/JuliaIO/MAT.jl), [HDF5](https://github.com/JuliaIO/HDF5.jl) files etc.
47
+
### Loading data
48
+
49
+
This tutorial assumes that you have a folder at the path
50
+
`/path_to_files/` with NIfTI files of the format `mask.nii` and
51
+
`somename_1.nii`, `somename_2.nii`, … . MRIRealign.jl does not include
[HDF5](https://github.com/JuliaIO/HDF5.jl) files, etc.
28
57
29
-
The first time, the packages need to be installed with the package manager:
58
+
Install the packages once:
30
59
31
-
```@Julia
60
+
```julia
32
61
using Pkg
33
62
Pkg.add("MRIRealign")
34
63
Pkg.add("NIfTI")
35
64
```
36
65
37
-
Thereafter, we can use them:
66
+
Then load them:
38
67
39
-
```@Julia
68
+
```julia
40
69
using MRIRealign
41
70
using NIfTI
42
71
```
43
72
44
-
We can change the directory
45
-
```@Julia
73
+
Change to the data directory:
74
+
75
+
```julia
46
76
cd("/path_to_files/")
47
77
```
48
78
49
-
and, optionally, load a mask and convert it to a binary mask
50
-
```@Julia
79
+
Optionally, load a mask and convert it to a `BitArray`:
80
+
81
+
```julia
51
82
mask =round.(Bool, niread("mask.nii"))
52
83
```
53
-
Note that the `.` after round indicates a point-wise operation.
54
84
55
-
We can create a list of file names in the current folder, except for `mask.nii`, and sort them in natural order, i.e., 1, 2, 3, ... instead of the ASCII order 1, 10, 100, 101, ... .
85
+
Create a sorted list of volume file names (natural numeric order):
56
86
57
-
```@Julia
87
+
```julia
58
88
files =filter(f ->isfile(f) && f !="mask.nii", readdir())
59
89
files =sort(files, by = file ->parse(Int, match(r"\d+", file).match))
60
90
```
61
91
62
-
Using the size of the mask, where `size(mask)...` returns the three dimensions separately, we can allocate an array and load all time frames into it:
92
+
Allocate a 4-D array and read all volumes into it:
Now we are all set to call the `realign!` function, which will overwrite `img` with the aligned volumes and return the motion parameters, i.e., 3 rotation and 3 translation parameters in this order:
73
-
```@Julia
102
+
### Estimating and applying motion correction
103
+
104
+
Call [`realign!`](@ref), which overwrites `img` with the aligned volumes
105
+
and returns the motion parameters — a `(6, t)` matrix where each column
106
+
is `[rx, ry, rz, tx, ty, tz]` (rotations in radians, translations in
107
+
voxels):
108
+
109
+
```julia
74
110
params =realign!(img; mask=mask)
75
111
```
76
112
77
-
We can write the aligned images back to the NIfTI files:
78
-
```@Julia
113
+
Write the aligned images back to NIfTI files:
114
+
115
+
```julia
79
116
for t ineachindex(files)
80
117
ni =niread(files[t])
81
118
ni.raw .= img[:,:,:,t]
82
119
niwrite(files[t], ni)
83
120
end
84
121
```
85
122
86
-
Note that this tutorial assumes that the headers of all NIfTI files are identical and replaces the raw data with interpolated data. For changing the NIfTI header instead, we can call `params = realign!(img; mask=mask, realign=false)` and write `params` to the NIfTI header. For more information, refer to [the NIfTI.jl documentation](https://github.com/JuliaNeuroscience/NIfTI.jl).
123
+
### Estimate-only workflow
124
+
125
+
To estimate motion parameters without modifying the images:
126
+
127
+
```julia
128
+
params =realign!(img; mask=mask, realign=false)
129
+
```
130
+
131
+
The returned `params` can later be applied with the two-argument form:
132
+
133
+
```julia
134
+
realign!(img, params)
135
+
```
136
+
137
+
### Reference modes
138
+
139
+
```julia
140
+
# Robust consensus across all pairwise alignments (default, slowest)
141
+
params =realign!(img; ref_mode=:consensus)
142
+
143
+
# Align to the temporal mean (fast, may be blurred)
144
+
params =realign!(img; ref_mode=:mean)
145
+
146
+
# Align to a specific time frame (fast, quality depends on that frame)
147
+
params =realign!(img; ref_mode=1)
148
+
```
149
+
150
+
### Smoothing
151
+
152
+
For noisy data, applying Gaussian smoothing before estimation can
153
+
improve robustness. The `fwhm` keyword accepts a 3-tuple of
154
+
full-width-at-half-maximum values in voxel units:
155
+
156
+
```julia
157
+
params =realign!(img; fwhm=(5.0, 5.0, 5.0))
158
+
```
159
+
160
+
!!! note
161
+
The default `fwhm=nothing` (no smoothing) differs from SPM's default
162
+
of approximately 5 mm. For noisy data, setting `fwhm` explicitly is
163
+
recommended.
164
+
165
+
!!! note
166
+
This tutorial assumes that the NIfTI headers of all files are
167
+
identical and replaces the raw data with interpolated data. To
168
+
update the NIfTI header instead (preserving the original voxel
169
+
data), use `realign=false` and write the parameters into the header.
170
+
See [the NIfTI.jl documentation](https://github.com/JuliaNeuroscience/NIfTI.jl)
0 commit comments