File: syn_registration_2d.py

package info (click to toggle)
dipy 1.11.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 17,144 kB
  • sloc: python: 92,240; makefile: 272; pascal: 183; sh: 162; ansic: 106
file content (269 lines) | stat: -rw-r--r-- 8,663 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
"""
==========================================
Symmetric Diffeomorphic Registration in 2D
==========================================
This example explains how to register 2D images using the Symmetric
Normalization (SyN) algorithm proposed by :footcite:t:`Avants2008` (also
implemented in the ANTs software :footcite:p:`Avants2009`)

We will perform the classic Circle-To-C experiment for diffeomorphic
registration
"""

import numpy as np

import dipy.align.imwarp as imwarp
from dipy.align.imwarp import SymmetricDiffeomorphicRegistration
from dipy.align.metrics import CCMetric, SSDMetric
from dipy.data import get_fnames
from dipy.io.image import load_nifti_data
from dipy.segment.mask import median_otsu
from dipy.viz import regtools

fname_moving = get_fnames(name="reg_o")
fname_static = get_fnames(name="reg_c")

moving = np.load(fname_moving)
static = np.load(fname_static)

###############################################################################
# To visually check the overlap of the static image with the transformed moving
# image, we can plot them on top of each other with different channels to see
# where the differences are located

regtools.overlay_images(
    static,
    moving,
    title0="Static",
    title_mid="Overlay",
    title1="Moving",
    fname="input_images.png",
)

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Input images.
#
#
#
# We want to find an invertible map that transforms the moving image (circle)
# into the static image (the C letter).
#
# The first decision we need to make is what similarity metric is appropriate
# for our problem. In this example we are using two binary images, so the Sum
# of Squared Differences (SSD) is a good choice.

dim = static.ndim
metric = SSDMetric(dim)

###############################################################################
# Now we define an instance of the registration class. The SyN algorithm uses
# a multi-resolution approach by building a Gaussian Pyramid. We instruct the
# registration instance to perform at most $[n_0, n_1, ..., n_k]$ iterations
# at each level of the pyramid. The 0-th level corresponds to the finest
# resolution.

level_iters = [200, 100, 50, 25]

sdr = SymmetricDiffeomorphicRegistration(metric, level_iters=level_iters, inv_iter=50)

###############################################################################
# Now we execute the optimization, which returns a DiffeomorphicMap object,
# that can be used to register images back and forth between the static and
# moving domains

mapping = sdr.optimize(static, moving)

###############################################################################
# It is a good idea to visualize the resulting deformation map to make sure
# the result is reasonable (at least, visually)

regtools.plot_2d_diffeomorphic_map(mapping, delta=10, fname="diffeomorphic_map.png")

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Deformed lattice under the resulting diffeomorphic map.
#
#
#
# Now let's warp the moving image and see if it gets similar to the static
# image

warped_moving = mapping.transform(moving, interpolation="linear")
regtools.overlay_images(
    static,
    warped_moving,
    title0="Static",
    title_mid="Overlay",
    title1="Warped moving",
    fname="direct_warp_result.png",
)

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Moving image transformed under the (direct) transformation in green on top
# of the static image (in red).
#
#
#
# And we can also apply the inverse mapping to verify that the warped static
# image is similar to the moving image

warped_static = mapping.transform_inverse(static, interpolation="linear")
regtools.overlay_images(
    warped_static,
    moving,
    title0="Warped static",
    title_mid="Overlay",
    title1="Moving",
    fname="inverse_warp_result.png",
)

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Static image transformed under the (inverse) transformation in red on top
# of the moving image (in green).
#
#
#
# Now let's register a couple of slices from a b0 image using the Cross
# Correlation metric. Also, let's inspect the evolution of the registration.
# To do this we will define a function that will be called by the registration
# object at each stage of the optimization process. We will draw the current
# warped images after finishing each resolution.


def callback_CC(sdr, status):
    # Status indicates at which stage of the optimization we currently are
    # For now, we will only react at the end of each resolution of the scale
    # space
    if status == imwarp.RegistrationStages.SCALE_END:
        # get the current images from the metric
        wmoving = sdr.metric.moving_image
        wstatic = sdr.metric.static_image
        # draw the images on top of each other with different colors
        regtools.overlay_images(
            wmoving,
            wstatic,
            title0="Warped moving",
            title_mid="Overlay",
            title1="Warped static",
        )


###############################################################################
# Now we are ready to configure and run the registration. First load the data

t1_name, b0_name = get_fnames(name="syn_data")
data = load_nifti_data(b0_name)

###############################################################################
# We first remove the skull from the b0 volume

b0_mask, mask = median_otsu(data, median_radius=4, numpass=4)

###############################################################################
# And select two slices to try the 2D registration

static = b0_mask[:, :, 40]
moving = b0_mask[:, :, 38]

###############################################################################
# After loading the data, we instantiate the Cross-Correlation metric. The
# metric receives three parameters: the dimension of the input images, the
# standard deviation of the Gaussian Kernel to be used to regularize the
# gradient and the radius of the window to be used for evaluating the local
# normalized cross correlation.

sigma_diff = 3.0
radius = 4
metric = CCMetric(2, sigma_diff=sigma_diff, radius=radius)

###############################################################################
# Let's use a scale space of 3 levels

level_iters = [100, 50, 25]
sdr = SymmetricDiffeomorphicRegistration(metric, level_iters=level_iters)
sdr.callback = callback_CC

###############################################################################
# And execute the optimization

mapping = sdr.optimize(static, moving)

warped = mapping.transform(moving)

###############################################################################
# We can see the effect of the warping by switching between the images before
# and after registration

regtools.overlay_images(
    static,
    moving,
    title0="Static",
    title_mid="Overlay",
    title1="Moving",
    fname="t1_slices_input.png",
)

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Input images.

regtools.overlay_images(
    static,
    warped,
    title0="Static",
    title_mid="Overlay",
    title1="Warped moving",
    fname="t1_slices_res.png",
)

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Moving image transformed under the (direct) transformation in green on top
# of the static image (in red).
#
#
#
# And we can apply the inverse warping too

inv_warped = mapping.transform_inverse(static)
regtools.overlay_images(
    inv_warped,
    moving,
    title0="Warped static",
    title_mid="Overlay",
    title1="moving",
    fname="t1_slices_res2.png",
)

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Static image transformed under the (inverse) transformation in red on top
# of the moving image (in green).
#
#
#
# Finally, let's see the deformation

regtools.plot_2d_diffeomorphic_map(mapping, delta=5, fname="diffeomorphic_map_b0s.png")

###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Deformed lattice under the resulting diffeomorphic map.
#
#
# References
# ----------
#
# .. footbibliography::
#