1
+ # ' Perform label image registration.
2
+ # '
3
+ # ' Perform pairwise registration using fixed and moving sets of label
4
+ # ' images (and, optionally, sets of corresponding intensity images).
5
+ # '
6
+ # ' @param fixedLabelImages A single (or set of) fixed label image(s).
7
+ # ' @param movingLabelImages A single (or set of) moving label image(s).
8
+ # ' @param fixedIntensityImages Optional---a single (or set of) fixed
9
+ # ' intensity image(s).
10
+ # ' @param movingIntensityImages Optional---a single (or set of) moving
11
+ # ' intensity image(s).
12
+ # ' @param fixedMask Defines region for similarity metric calculation
13
+ # ' in the space of the fixed image.
14
+ # ' @param movingMask Defines region for similarity metric calculation
15
+ # ' in the space of the moving image.
16
+ # ' @param typeOfLinearTransform Use label images with the centers of
17
+ # ' mass to a calculate linear transform of type \code{'rigid'},
18
+ # ' \code{'similarity'}, \code{'affine'}.
19
+ # ' @param typeOfTransform Only works with deformable-only transforms,
20
+ # ' specifically the family of \code{antsRegistrationSyN*[so]} or
21
+ # ' \code{antsRegistrationSyN*[bo]} transforms. See 'type_of_transform'
22
+ # ' in \code{antsRegistration}.
23
+ # ' @param labelImageWeighting Float or vector of floats giving the relative
24
+ # ' weighting for the label images.
25
+ # ' @param outputPrefix String definining the output prefix for the filenames
26
+ # ' of the output transform files.
27
+ # ' @param randomSeed Definition for deformable registration.
28
+ # ' @param verbose Print progress to the screen.
29
+ # ' @return outputs a list containing:
30
+ # ' \itemize{
31
+ # ' \item{fwdtransforms: }{Transforms to move from moving to fixed image.}
32
+ # ' \item{invtransforms: }{Transforms to move from fixed to moving image.}
33
+ # ' }
34
+ # ' Output of 1 indicates failure.
35
+ # ' @author Tustison NJ
36
+ # ' @examples
37
+ # ' \dontrun{
38
+ # ' r16 <- antsImageRead( getANTsRData( "r16" ) )
39
+ # ' r16Seg1 <- thresholdImage( r16, "Kmeans", 3 ) - 1
40
+ # ' r16Seg2 <- thresholdImage( r16, "Kmeans", 5 ) - 1
41
+ # ' r64 <- antsImageRead( getANTsRData( "r64" ) )
42
+ # ' r64Seg1 <- thresholdImage( r64, "Kmeans", 3 ) - 1
43
+ # ' r64Seg2 <- thresholdImage( r64, "Kmeans", 5 ) - 1
44
+ # ' reg <- labelImageRegistration( list( r16Seg1, r16Seg2 ),
45
+ # ' list( r64Seg1, r64Seg2 ),
46
+ # ' fixedIntensityImages = r16,
47
+ # ' movingIntensityImages = r64,
48
+ # ' typeOfLinearTransform = 'affine',
49
+ # ' typeOfTransform = 'antsRegistrationSyNQuick[bo]',
50
+ # ' labelImageWeighting = c( 1.0, 2.0 ),
51
+ # ' verbose = TRUE )
52
+ # ' }
53
+ # '
54
+ # ' @export labelImageRegistration
55
+ labelImageRegistration <- function ( fixedLabelImages , movingLabelImages ,
56
+ fixedIntensityImages = NULL , movingIntensityImages = NULL ,
57
+ fixedMask = NULL , movingMask = NULL ,
58
+ typeOfLinearTransform = ' affine' ,
59
+ typeOfTransform = ' antsRegistrationSyNQuick[so]' ,
60
+ labelImageWeighting = 1.0 ,
61
+ outputPrefix = ' ' ,
62
+ randomSeed = NULL ,
63
+ verbose = FALSE )
64
+ {
65
+ # Preform validation check on the input
66
+
67
+ if ( ANTsR :: is.antsImage( fixedLabelImages ) )
68
+ {
69
+ fixedLabelImages <- list ( antsImageClone( fixedLabelImages ) )
70
+ }
71
+ if ( ANTsR :: is.antsImage( movingLabelImages ) )
72
+ {
73
+ movingLabelImages <- list ( antsImageClone( movingLabelImages ) )
74
+ }
75
+
76
+ if ( length( fixedLabelImages ) != length( movingLabelImages ) )
77
+ {
78
+ stop( " The number of fixed and moving label images do not match." )
79
+ }
80
+
81
+ if ( ! is.null( fixedIntensityImages ) || ! is.null( movingIntensityImages ) )
82
+ {
83
+ if ( ANTsR :: is.antsImage( fixedIntensityImages ) )
84
+ {
85
+ fixedIntensityImages <- list ( antsImageClone( fixedIntensityImages ) )
86
+ }
87
+ if ( ANTsR :: is.antsImage( movingIntensityImages ) )
88
+ {
89
+ movingIntensityImages <- list ( antsImageClone( movingIntensityImages ) )
90
+ }
91
+ if ( length( fixedIntensityImages ) != length( movingIntensityImages ) )
92
+ {
93
+ stop( " The number of fixed and moving intensity images do not match." )
94
+ }
95
+ }
96
+
97
+ labelImageWeights <- labelImageWeighting
98
+ if ( is.numeric( labelImageWeighting ) && ! is.vector( labelImageWeighting ) )
99
+ {
100
+ labelImageWeights <- rep( labelImageWeighting , length( fixedLabelImages ) )
101
+ } else {
102
+ if ( length( fixedLabelImages ) != length( labelImageWeights ) )
103
+ {
104
+ stop( paste( " The length of labelImageWeights must" ,
105
+ " match the number of label image pairs." ) )
106
+ }
107
+ }
108
+
109
+ imageDimension <- fixedLabelImages [[1 ]]@ dimension
110
+
111
+ if ( outputPrefix == ' ' || is.null( outputPrefix ) || length( outputPrefix ) == 0 )
112
+ {
113
+ outputPrefix <- tempfile()
114
+ }
115
+
116
+ allowableLinearTransforms <- c( ' rigid' , ' similarity' , ' affine' )
117
+ if ( ! typeOfLinearTransform %in% allowableLinearTransforms )
118
+ {
119
+ stop( " Unrecognized linear transform." )
120
+ }
121
+
122
+ doDeformable <- FALSE
123
+ if ( ! is.null( typeOfTransform ) || length( typeOfTransform ) > 0 )
124
+ {
125
+ doDeformable <- TRUE
126
+ }
127
+
128
+ commonLabelIds <- list ()
129
+ totalNumberOfLabels <- 0
130
+ for ( i in seq.int( length( fixedLabelImages ) ) )
131
+ {
132
+ fixedLabelGeoms <- labelGeometryMeasures( fixedLabelImages [[i ]] )
133
+ fixedLabelIds <- fixedLabelGeoms $ Label
134
+ movingLabelGeoms <- labelGeometryMeasures( movingLabelImages [[i ]] )
135
+ movingLabelIds <- movingLabelGeoms $ Label
136
+ commonLabelIds [[i ]] <- intersect( fixedLabelIds , movingLabelIds )
137
+ totalNumberOfLabels = totalNumberOfLabels + length( commonLabelIds [[i ]] )
138
+ if ( verbose )
139
+ {
140
+ message( paste0( " Common label ids for image pair " , i , " : " , commonLabelIds [i ] ) )
141
+ }
142
+ if ( length( commonLabelIds [[i ]]) == 0 )
143
+ {
144
+ stop( paste0( " No common labels for image pair " , i ) )
145
+ }
146
+ }
147
+
148
+ if ( verbose )
149
+ {
150
+ message( " Total number of labels: " , totalNumberOfLabels )
151
+ }
152
+
153
+ # #############################
154
+ #
155
+ # Linear transform
156
+ #
157
+ # #############################
158
+
159
+ linearXfrm <- NULL
160
+ if ( ! is.null( typeOfLinearTransform ) )
161
+ {
162
+ if ( verbose )
163
+ {
164
+ message( " \n\n Computing linear transform.\n " )
165
+ }
166
+
167
+ if ( totalNumberOfLabels < 3 )
168
+ {
169
+ stop( " Number of labels must be >= 3." )
170
+ }
171
+
172
+ fixedCentersOfMass <- array ( data = 0 , c( totalNumberOfLabels , imageDimension ) )
173
+ movingCentersOfMass <- array ( data = 0 , c( totalNumberOfLabels , imageDimension ) )
174
+
175
+ deformableMultivariateExtras <- list ()
176
+
177
+ count <- 1
178
+ for ( i in seq.int( length( commonLabelIds ) ) )
179
+ {
180
+ for ( j in seq.int( length( commonLabelIds [[i ]] ) ) )
181
+ {
182
+ label <- commonLabelIds [[i ]][j ]
183
+ if ( verbose )
184
+ {
185
+ message( paste0( " Finding centers of mass for image pair " , i , " , label " , label ) )
186
+ }
187
+ fixedSingleLabelImage <- thresholdImage( fixedLabelImages [[i ]], label , label , 1 , 0 )
188
+ fixedCentersOfMass [count ,] <- getCenterOfMass( fixedSingleLabelImage )
189
+ movingSingleLabelImage <- thresholdImage( movingLabelImages [[i ]], label , label , 1 , 0 )
190
+ movingCentersOfMass [count ,] <- getCenterOfMass( movingSingleLabelImage )
191
+ count <- count + 1
192
+ if ( doDeformable )
193
+ {
194
+ deformableMultivariateExtras [[i ]] <- list ( " MSQ" , fixedSingleLabelImage ,
195
+ movingSingleLabelImage ,
196
+ labelImageWeights [i ], 0 )
197
+ }
198
+ }
199
+ }
200
+
201
+ linearXfrm <- fitTransformToPairedPoints( movingCentersOfMass ,
202
+ fixedCentersOfMass ,
203
+ transformType = typeOfLinearTransform ,
204
+ verbose = verbose )
205
+
206
+ linearXfrmFile <- paste0( outputPrefix , " 0GenericAffine.mat" )
207
+ writeAntsrTransform( linearXfrm , linearXfrmFile )
208
+ }
209
+
210
+ # #############################
211
+ #
212
+ # Deformable transform
213
+ #
214
+ # #############################
215
+
216
+ if ( doDeformable )
217
+ {
218
+ if ( verbose )
219
+ {
220
+ message( " \n\n Computing deformable transform using images.\n " )
221
+ }
222
+
223
+ doQuick <- FALSE
224
+ doRepro <- FALSE
225
+
226
+ if ( grepl( " Quick" , typeOfTransform ) )
227
+ {
228
+ doQuick <- TRUE
229
+ } else {
230
+ doRepro <- TRUE
231
+ randomSeed <- 1
232
+ }
233
+
234
+ intensityMetricParameter <- NULL
235
+ splineDistance <- 26
236
+ if ( grepl( " \\ [" , typeOfTransform ) && grepl(" \\ ]" , typeOfTransform ) )
237
+ {
238
+ subtypeOfTransform <- strsplit( strsplit( typeOfTransform , " \\ [" )[[1 ]][2 ], " \\ ]" )[[1 ]][1 ]
239
+ if ( ! ( grepl( " bo" , subtypeOfTransform ) || grepl( " so" , subtypeOfTransform ) ) )
240
+ {
241
+ stop( " Only 'so' or 'bo' transforms are available." )
242
+ }
243
+ if ( grepl( " ," , subtypeOfTransform ) )
244
+ {
245
+ subtypeOfTransformArgs <- strsplit( subtypeOfTransform , " ," )[[1 ]]
246
+ subtypeOfTransform <- subtypeOfTransformArgs [1 ]
247
+ intensityMetricParameter <- subtypeOfTransformArgs [2 ]
248
+ if ( length( subtypeOfTransformArgs ) > 2 )
249
+ {
250
+ splineDistance <- subtypeOfTransformArgs [3 ]
251
+ }
252
+ }
253
+ }
254
+
255
+ synTransform <- " SyN[0.1,3,0]"
256
+ if ( subtypeOfTransform == " bo" )
257
+ {
258
+ synTransform <- paste0(" BSplineSyN[0.1," , splineDistance , " ,0,3]" )
259
+ }
260
+ synStage <- list ( " --transform" , synTransform )
261
+
262
+ if ( doQuick )
263
+ {
264
+ synConvergence <- " [100x70x50x0,1e-6,10]"
265
+ } else {
266
+ synConvergence <- " [100x70x50x20,1e-6,10]"
267
+ }
268
+ synShrinkFactors <- " 8x4x2x1"
269
+ synSmoothingSigmas <- " 3x2x1x0vox"
270
+
271
+ synStage <- lappend( synStage , list (
272
+ " --convergence" , synConvergence ,
273
+ " --shrink-factors" , synShrinkFactors ,
274
+ " --smoothing-sigmas" , synSmoothingSigmas
275
+ ) )
276
+
277
+ intensityMetric <- NULL
278
+ if ( ! is.null( fixedIntensityImages ) && ! is.null( movingIntensityImages ) )
279
+ {
280
+ if ( doQuick )
281
+ {
282
+ intensityMetric <- " MI"
283
+ if ( is.null( intensityMetricParameter ) )
284
+ {
285
+ intensityMetricParameter <- 32
286
+ }
287
+ }
288
+ if ( ! doQuick || doRepro )
289
+ {
290
+ intensityMetric <- " CC"
291
+ if ( is.null( intensityMetricParameter ) )
292
+ {
293
+ intensityMetricParameter <- 2
294
+ }
295
+ }
296
+ for ( i in seq.int( length( fixedIntensityImages ) ) )
297
+ {
298
+ synStage <- lappend( synStage , list (
299
+ " --metric" , paste0( intensityMetric , " [" ,
300
+ antsrGetPointerName( fixedIntensityImages [[i ]] ), " ," ,
301
+ antsrGetPointerName( movingIntensityImages [[i ]] ), " ," ,
302
+ " 1.0," , as.character( intensityMetricParameter ), " ]" ) ) )
303
+ }
304
+ }
305
+ for ( kk in seq.int( length( deformableMultivariateExtras ) ) )
306
+ {
307
+ synStage <- lappend( synStage , list (
308
+ " --metric" , paste0( " MSQ[" ,
309
+ antsrGetPointerName( deformableMultivariateExtras [[kk ]][[2 ]] ), " ," ,
310
+ antsrGetPointerName( deformableMultivariateExtras [[kk ]][[3 ]] ), " ," ,
311
+ as.character( deformableMultivariateExtras [[kk ]][[4 ]] ), " ,0.0]" ) ) )
312
+ }
313
+
314
+ args <- NULL
315
+ if ( is.null( linearXfrm ) )
316
+ {
317
+ args <- list (
318
+ " -d" , as.character( imageDimension ),
319
+ " -o" , outputPrefix )
320
+ } else {
321
+ args <- list (
322
+ " -d" , as.character( imageDimension ),
323
+ " -r" , linearXfrmFile ,
324
+ " -o" , outputPrefix )
325
+ }
326
+ args <- lappend( args , synStage )
327
+
328
+ fixedMaskString <- " NA"
329
+ if ( ! is.null( fixedMask ) )
330
+ {
331
+ fixedMaskBinary <- antsImageClone( thresholdImage( fixedMask , 0 , 0 , 0 , 1 ), " unsigned char" )
332
+ fixedMaskString <- antsrGetPointerName( fixedMaskBinary )
333
+ }
334
+
335
+ movingMaskString <- " NA"
336
+ if ( ! is.null( movingMask ) )
337
+ {
338
+ movingMaskBinary <- antsImageClone( thresholdImage( movingMask , 0 , 0 , 0 , 1 ), " unsigned char" )
339
+ movingMaskString <- antsrGetPointerName( movingMaskBinary )
340
+ }
341
+
342
+ maskOption <- paste0( " [" , fixedMaskString , " ," , movingMaskString , " ]" )
343
+ args <- lappend( args , list ( " -x" , maskOption ) )
344
+
345
+ args <- lappend( args , list ( " --float" , " 1" ) )
346
+
347
+ if ( ! is.null( randomSeed ) )
348
+ {
349
+ args <- lappend( " --random-seed" , randomSeed )
350
+ }
351
+
352
+ if ( verbose )
353
+ {
354
+ args <- lappend( args , list ( " -v" , " 1" ) )
355
+ cat( " antsRegistration" , paste( unlist( args ) ), " \n " )
356
+ }
357
+ args <- .int_antsProcessArguments( c( args ) )
358
+ ANTsRCore :: AntsRegistration( args )
359
+ }
360
+
361
+ allXfrms <- Sys.glob( paste0( outputPrefix , " *" , " [0-9]*" ) )
362
+
363
+ findInverseWarps <- grep( " [0-9]InverseWarp.nii.gz" , allXfrms )
364
+ findForwardWarps <- grep( " [0-9]Warp.nii.gz" , allXfrms )
365
+
366
+ if ( length( findInverseWarps ) > 0 )
367
+ {
368
+ fwdtransforms <- c( allXfrms [findForwardWarps [1 ]], linearXfrmFile )
369
+ invtransforms <- c( linearXfrmFile , allXfrms [findInverseWarps [1 ]] )
370
+ } else {
371
+ fwdtransforms <- c( linearXfrmFile )
372
+ invtransforms <- c( linearXfrmFile )
373
+ }
374
+
375
+ if ( verbose )
376
+ {
377
+ message( " \n\n Resulting transforms" )
378
+ if ( length( findInverseWarps ) > 0 )
379
+ {
380
+ message( paste0( " fwdtransforms: [" , fwdtransforms [1 ], " , " , fwdtransforms [2 ], " ]" ) )
381
+ message( paste0( " invtransforms: [" , invtransforms [1 ], " , " , invtransforms [2 ], " ]" ) )
382
+ } else {
383
+ message( paste0( " fwdtransforms: [" , fwdtransforms [1 ], " ]" ) )
384
+ message( paste0( " invtransforms: [" , invtransforms [1 ], " ]" ) )
385
+ }
386
+ }
387
+
388
+ return ( list ( fwdtransforms = fwdtransforms ,
389
+ invtransforms = invtransforms ) )
390
+ }
0 commit comments