1 /*----------------------------------------------------------------------------*/
2 /**** TO DO (someday, maybe):
3 -matini
4 ****/
5
6 /****** N.B.: What used to be 'target' is now 'source' to users,
7 but remains as 'target' in the code and comments. ******/
8
9 /****** N.B.: See the note about coordinates at the end of this file! ******/
10
11 /*
12 [PT: Nov 5, 2018] Don't know why this is only happening *now*, but
13 am moving the lpa/lpc cost functions into the main list under '-cost
14 ccc' in the help; they are no longer classified as "experimental"
15 */
16
17 /*----------------------------------------------------------------------------*/
18 #include "mrilib.h"
19 #include "r_new_resam_dset.h"
20 #include <time.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 /*** OpenMP specialized header includes & defines go here ***/
25
26 #ifdef USE_OMP
27 #include <omp.h>
28 # if 0
29 # ifdef USING_MCW_MALLOC
30 # include "mcw_malloc.c"
31 # endif
32 #endif
33 #endif
34
35 #undef USE_OLD_BLOK_DEFAULTS /* 21 Jul 2021 */
36
37 #define MAXPAR 999 /* max number of parameters to optimize */
38 #define PARC_FIX 1
39 #define PARC_INI 2
40 #define PARC_RAN 3
41 typedef struct { int np,code; float vb,vt ; } param_opt ;
42
43 /* codes for the 4 types of transformations,
44 each of which add 3 parameters to the mix */
45
46 #define WARP_SHIFT 1 /* shift only */
47 #define WARP_ROTATE 2 /* shift + rotate */
48 #define WARP_SCALE 3 /* + xyz scaling */
49 #define WARP_AFFINE 4 /* + shears */
50
51 /* for -1Dapply options */
52
53 #define APPLY_PARAM 1 /* 23 Jul 2007 */
54 #define APPLY_AFF12 2
55
56 /****************************************************************************/
57 /******* All the -nwarp stuff is inside ALLOW_NWARP #ifdef-s.
58 The goal is to make it easier to see and later remove this code ****/
59 #ifdef ALLOW_NWARP /*********************************************************/
60 /*** parameter counts for the obsolescent -nwarp option ***/
61
62 #define NPBIL 39 /* plus 4 */
63 #define WARP_BILINEAR 661
64 #define APPLY_BILIN 3
65
66 #define NPCUB 60 /* 3*(1+3+6+10) */
67 #define WARP_CUBIC 663
68 #define APPLY_CUBIC 4
69
70 #define NPQUINT 168 /* 3*(1+3+6+10+15+21) */
71 #define WARP_QUINT 664
72 #define APPLY_QUINT 5
73
74 #define NPHEPT 360 /* 3*(1+3+6+10+15+21+28+36) */
75 #define WARP_HEPT 665
76 #define APPLY_HEPT 6
77
78 #define NPNONI 660 /* 3*(1+3+6+10+15+21+28+36+45+55) */
79 #define WARP_NONI 666
80 #define APPLY_NONI 7
81
82 #define NONLINEAR_IS_POLY(aa) ( (aa) >= WARP_CUBIC && (aa) <= WARP_NONI )
83
84 #define NONLINEAR_APPLY(aa) ( (aa) >= APPLY_BILIN )
85
86 #endif /* ALLOW_NWARP */ /**************************************************/
87
88 /****/
89
90 /* -twobest defaults */
91
92 #define DEFAULT_TBEST 5
93 #define DEFAULT_TBEST_LPA 17 /* 27 May 2021 */
94
95 /* fractions for the lpc+ and lpa+ addons */
96
97 #define DEFAULT_MICHO_LPC_MI 0.2
98 #define DEFAULT_MICHO_LPC_NMI 0.2
99 #define DEFAULT_MICHO_LPC_CRA 0.4
100 #define DEFAULT_MICHO_LPC_HEL 0.4
101 #define DEFAULT_MICHO_LPC_OV 0.4
102
103 #define DEFAULT_MICHO_LPA_MI 0.0 /* 27 May 2021 */
104 #define DEFAULT_MICHO_LPA_NMI 0.2
105 #define DEFAULT_MICHO_LPA_CRA 0.4
106 #define DEFAULT_MICHO_LPA_HEL 0.4
107 #define DEFAULT_MICHO_LPA_OV 0.4 /* 28 Sep 2021: bring this back */
108
109 // [PT: 28 Sep 2021] lpb == lpa, but lpb+ does NOT contain ov
110 // + leaving off doing more with this, at the moment
111 #define DEFAULT_MICHO_LPB_MI 0.0
112 #define DEFAULT_MICHO_LPB_NMI 0.2
113 #define DEFAULT_MICHO_LPB_CRA 0.4
114 #define DEFAULT_MICHO_LPB_HEL 0.4
115 #define DEFAULT_MICHO_LPB_OV 0.0
116
117 /****/
118
119 /* making the weight volume from the base dataset */
120
121 static float wt_medsmooth = 2.25f ; /* for mri_weightize() */
122 static float wt_gausmooth = 4.50f ;
123 MRI_IMAGE * mri_weightize( MRI_IMAGE *, int, int, float,float ); /* prototype */
124
125 static int doing_2D = 0 ; /* 28 Apr 2020: 2D registration */
126 static int verb = 1 ; /* somewhat on by default */
127
128 /* for checking how far 2 sets of parameters are apart from each other */
129
130 float param_dist( GA_setup *stp , float *aa , float *bb ) ; /* prototype */
131
132 void AL_setup_warp_coords( int,int,int,int ,
133 int *, float *, mat44,
134 int *, float *, mat44 ) ; /* prototype */
135
136 MRI_IMAGE * mri_identity_params(void); /* prototype */
137
138 /* since I no longer like mcw_malloc, MEMORY_CHECK is down the memory hole */
139
140 #undef MEMORY_CHECK
141 #ifdef USING_MCW_MALLOC
142 # define MEMORY_CHECK(mm) \
143 do{ if( verb > 5 ) mcw_malloc_dump() ; \
144 if( verb > 1 ){ \
145 long long nb = mcw_malloc_total() ; \
146 if( nb > 0 ) INFO_message("Memory usage now = %s (%s): %s" , \
147 commaized_integer_string(nb) , \
148 approximate_number_string((double)nb) , (mm) ) ; \
149 } \
150 } while(0)
151 #else
152 # define MEMORY_CHECK(mm) /*nada*/
153 #endif
154
155 #define ALLOW_METH_CHECK /* for the -check option: 03 Apr 2008 */
156
157 /*----------------------------------------------------------------------------*/
158 /*** Stuff that defines the method codes and name ***/
159
160 #undef NMETH
161 #define NMETH GA_MATCH_METHNUM_SCALAR /* cf. mrilib.h */
162
163 static int meth_visible[NMETH] = /* 1 = show in -help; 0 = don't show */
164 { 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 } ;
165 /* ls sp mi crM nmi je hel crA crU lss lpc lpa lpc+ lpa+ */
166
167 static int meth_noweight[NMETH] = /* 1 = don't allow weights, just masks */
168 { 0 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
169 /* ls sp mi crM nmi je hel crA crU lss lpc lpa lpc+ lpa+ */
170
171 static int visible_noweights ;
172
173 static char *meth_shortname[NMETH] = /* short names for terse cryptic users */
174 { "ls" , "sp" , "mi" , "crM", "nmi", "je" , "hel",
175 "crA", "crU", "lss", "lpc", "lpa", "lpc+" , "lpa+" } ;
176
177 static char *meth_longname[NMETH] = /* long names for prolix users */
178 { "leastsq" , "spearman" ,
179 "mutualinfo" , "corratio_mul" ,
180 "norm_mutualinfo" , "jointentropy" ,
181 "hellinger" ,
182 "corratio_add" , "corratio_uns" , "signedPcor" ,
183 "localPcorSigned" , "localPcorAbs" , "localPcor+Others" ,
184 "localPcorAbs+Others" } ;
185
186 static char *meth_username[NMETH] = /* descriptive names */
187 { "Least Squares [Pearson Correlation]" ,
188 "Spearman [rank] Correlation" , /* hidden */
189 "Mutual Information [H(b)+H(s)-H(b,s)]" ,
190 "Correlation Ratio (Symmetrized*)" ,
191 "Normalized MI [H(b,s)/(H(b)+H(s))]" ,
192 "Joint Entropy [H(b,s)]" , /* hidden */
193 "Hellinger metric" ,
194 "Correlation Ratio (Symmetrized+)" ,
195 "Correlation Ratio (Unsym)" ,
196 "Signed Pearson Correlation" , /* hidden */
197 "Local Pearson Correlation Signed" , /* hidden */
198 "Local Pearson Correlation Abs" , /* hidden */
199 "Local Pearson Signed + Others" , /* hidden */
200 "Local Pearson Abs + Others" } ; /* hidden */
201
202 static char *meth_costfunctional[NMETH] = /* describe cost functional */
203 { "1 - abs(Pearson correlation coefficient)" ,
204 "1 - abs(Spearman correlation coefficient)" ,
205 "- Mutual Information = H(base,source)-H(base)-H(source)" ,
206 "1 - abs[ CR(base,source) * CR(source,base) ]" ,
207 "1/Normalized MI = H(base,source)/[H(base)+H(source)]" ,
208 "H(base,source) = joint entropy of image pair" ,
209 "- Hellinger distance(base,source)" ,
210 "1 - abs[ CR(base,source) + CR(source,base) ]" ,
211 "CR(source,base) = Var(source|base) / Var(source)" ,
212 "Pearson correlation coefficient between image pair" ,
213 "nonlinear average of Pearson cc over local neighborhoods" ,
214 "1 - abs(lpc)" ,
215 "lpc + hel + mi + nmi + crA + overlap" ,
216 "lpa + hel + nmi + crA + overlap"
217 } ;
218
219 /* check if method code implies use of BLOKs */
220
221 #undef METH_USES_BLOKS
222 #define METH_USES_BLOKS(mmm) ( mmm == GA_MATCH_PEARSON_LOCALS || \
223 mmm == GA_MATCH_LPC_MICHO_SCALAR || \
224 mmm == GA_MATCH_LPA_MICHO_SCALAR || \
225 mmm == GA_MATCH_PEARSON_LOCALA )
226
227 #undef METH_IS_LPA
228 #define METH_IS_LPA(mmm) ( mmm == GA_MATCH_LPA_MICHO_SCALAR || \
229 mmm == GA_MATCH_PEARSON_LOCALA )
230
231 #undef METH_IS_LPC
232 #define METH_IS_LPC(mmm) ( mmm == GA_MATCH_LPC_MICHO_SCALAR || \
233 mmm == GA_MATCH_PEARSON_LOCALS )
234
235 /* shortcut for the k-th output parameter in the alignment struct */
236
237 #define OUTVAL(k) stup.wfunc_param[k].val_out
238
239 /* In the stuff below,
240 stup.wfunc_param[i].fixed == 2 means parameter [i] cannot be unfixed
241 stup.wfunc_param[i].fixed == 1 means it is 'temporarily fixed'
242 stup.wfunc_param[i].fixed == 0 means it is 'free' -- to be optimized */
243
244 #ifdef ALLOW_NWARP /*********************************************************/
245 /*---------------------------------------------------------------------------*/
246 /* For the bilinear warp method -- this is obsolete IMHO [RWC] */
247
248 #define SETUP_BILINEAR_PARAMS \
249 do{ char str[16] ; \
250 stup.wfunc_numpar = NPBIL+4 ; \
251 stup.wfunc = mri_genalign_bilinear ; \
252 stup.wfunc_param = (GA_param *)realloc( (void *)stup.wfunc_param, \
253 (NPBIL+4)*sizeof(GA_param) ) ; \
254 for( jj=12 ; jj < NPBIL ; jj++ ){ \
255 sprintf(str,"blin%02d",jj+1) ; \
256 DEFPAR( jj,str, -nwarp_parmax,nwarp_parmax , 0.0f,0.0f,0.0f ) ; \
257 stup.wfunc_param[jj].fixed = 1 ; \
258 } \
259 DEFPAR(NPBIL ,"xcen" ,-1.0e9,1.0e9 , 0.0f,0.0f,0.0f ) ; \
260 DEFPAR(NPBIL+1,"ycen" ,-1.0e9,1.0e9 , 0.0f,0.0f,0.0f ) ; \
261 DEFPAR(NPBIL+2,"zcen" ,-1.0e9,1.0e9 , 0.0f,0.0f,0.0f ) ; \
262 DEFPAR(NPBIL+3,"ddfac", 0.0f ,1.0e9 , 1.0f,0.0f,0.0f ) ; \
263 stup.wfunc_param[NPBIL ].fixed = 2 ; \
264 stup.wfunc_param[NPBIL+1].fixed = 2 ; \
265 stup.wfunc_param[NPBIL+2].fixed = 2 ; \
266 stup.wfunc_param[NPBIL+3].fixed = 2 ; \
267 } while(0)
268
269 /*---------------------------------------------------------------------------*/
270
BILINEAR_diag_norm(GA_setup stup)271 static float BILINEAR_diag_norm(GA_setup stup)
272 {
273 float sum ;
274 sum = fabsf(OUTVAL(12))+fabsf(OUTVAL(13))+fabsf(OUTVAL(14)) ;
275 sum += fabsf(OUTVAL(24))+fabsf(OUTVAL(25))+fabsf(OUTVAL(26)) ;
276 sum += fabsf(OUTVAL(36))+fabsf(OUTVAL(37))+fabsf(OUTVAL(38)) ;
277 return (sum/9.0f) ;
278 }
279
BILINEAR_offdiag_norm(GA_setup stup)280 static float BILINEAR_offdiag_norm(GA_setup stup)
281 {
282 float dmag ;
283 dmag = fabsf(OUTVAL(15))+fabsf(OUTVAL(16))+fabsf(OUTVAL(17)) ;
284 dmag += fabsf(OUTVAL(18))+fabsf(OUTVAL(19))+fabsf(OUTVAL(20)) ;
285 dmag += fabsf(OUTVAL(21))+fabsf(OUTVAL(22))+fabsf(OUTVAL(23)) ;
286 dmag += fabsf(OUTVAL(27))+fabsf(OUTVAL(28))+fabsf(OUTVAL(29)) ;
287 dmag += fabsf(OUTVAL(30))+fabsf(OUTVAL(31))+fabsf(OUTVAL(32)) ;
288 dmag += fabsf(OUTVAL(33))+fabsf(OUTVAL(34))+fabsf(OUTVAL(35)) ;
289 return (dmag/18.0f) ;
290 }
291
292 /*---------------------------------------------------------------------------*/
293 /* for the polynomial warp setup - also obsolete [RWC] */
294
295 #define SETUP_POLYNO_PARAMS(nnl,ran,nam) \
296 do{ char str[32] , *spt , xyz[3] = { 'x', 'y', 'z' } ; \
297 stup.wfunc_numpar = 16+(nnl) ; \
298 stup.wfunc = NULL ; \
299 stup.wfunc_param = (GA_param *)realloc( (void *)stup.wfunc_param, \
300 (16+(nnl))*sizeof(GA_param) ); \
301 for( jj=12 ; jj < 12+(nnl) ; jj++ ){ \
302 spt = GA_polywarp_funcname( (jj-12)/3 ) ; \
303 if( spt != NULL ) sprintf(str,"%s:%s:%c" ,(nam),spt ,xyz[jj%3]) ; \
304 else sprintf(str,"%s:%03d:%c",(nam),jj+1,xyz[jj%3]) ; \
305 DEFPAR( jj,str, -(ran),(ran) , 0.0f,0.0f,0.0f ) ; \
306 stup.wfunc_param[jj].fixed = 1 ; \
307 } \
308 DEFPAR(12+(nnl),"xcen" ,-1.0e9,1.0e9 , 0.0f,0.0f,0.0f ) ; \
309 DEFPAR(13+(nnl),"ycen" ,-1.0e9,1.0e9 , 0.0f,0.0f,0.0f ) ; \
310 DEFPAR(14+(nnl),"zcen" ,-1.0e9,1.0e9 , 0.0f,0.0f,0.0f ) ; \
311 DEFPAR(15+(nnl),"xxfac", 0.0f ,1.0e9 , 1.0f,0.0f,0.0f ) ; \
312 stup.wfunc_param[12+(nnl)].fixed = 2 ; \
313 stup.wfunc_param[13+(nnl)].fixed = 2 ; \
314 stup.wfunc_param[14+(nnl)].fixed = 2 ; \
315 stup.wfunc_param[15+(nnl)].fixed = 2 ; \
316 } while(0)
317
318 #define SETUP_CUBIC_PARAMS do{ SETUP_POLYNO_PARAMS(48,nwarp_parmax,"cubic"); \
319 stup.wfunc = mri_genalign_cubic ; } while(0)
320
321 #define SETUP_QUINT_PARAMS do{ SETUP_POLYNO_PARAMS(156,nwarp_parmax,"quint"); \
322 stup.wfunc = mri_genalign_quintic ; } while(0)
323
324 #define SETUP_HEPT_PARAMS do{ SETUP_POLYNO_PARAMS(348,nwarp_parmax,"heptic"); \
325 stup.wfunc = mri_genalign_heptic ; } while(0)
326
327 #define SETUP_NONI_PARAMS do{ SETUP_POLYNO_PARAMS(648,nwarp_parmax,"nonic"); \
328 stup.wfunc = mri_genalign_nonic ; } while(0)
329
330 /*---------- Macros for freezing motions in particular directions ----------*/
331
332 /* cc = 1,2,3 for x,y,z directions:
333 permanently freeze parameters that cause motion in that direction */
334
335 #define FREEZE_POLYNO_PARAMS_MOT(cc) \
336 do{ int pp ; \
337 for( pp=12 ; pp < stup.wfunc_numpar ; pp++ ){ \
338 if( 1+pp%3 == (cc) ) stup.wfunc_param[pp].fixed = 2 ; \
339 } \
340 } while(0)
341 /* cc = 1,2,3 for x,y,z directions:
342 freeze parameters whose basis funcs are dependent on that coordinate */
343
344 #define FREEZE_POLYNO_PARAMS_DEP(cc) \
345 do{ int pp , qq , cm=(1 << ((cc)-1)) ; \
346 for( pp=12 ; pp < stup.wfunc_numpar ; pp++ ){ \
347 qq = GA_polywarp_coordcode( (pp-12)/3 ) ; \
348 if( qq & cm ) stup.wfunc_param[pp].fixed = 2 ; \
349 } \
350 } while(0)
351
352 /* overall parameter freeze box, based on user options */
353
354 #define FREEZE_POLYNO_PARAMS \
355 do{ if( nwarp_fixmotX ) FREEZE_POLYNO_PARAMS_MOT(1) ; \
356 if( nwarp_fixmotY ) FREEZE_POLYNO_PARAMS_MOT(2) ; \
357 if( nwarp_fixmotZ ) FREEZE_POLYNO_PARAMS_MOT(3) ; \
358 if( nwarp_fixdepX ) FREEZE_POLYNO_PARAMS_DEP(1) ; \
359 if( nwarp_fixdepY ) FREEZE_POLYNO_PARAMS_DEP(2) ; \
360 if( nwarp_fixdepZ ) FREEZE_POLYNO_PARAMS_DEP(3) ; } while(0)
361
362 #endif /* ALLOW_NWARP */ /**************************************************/
363
364 /*----------- count free params into variable 'nf' -----------*/
365
366 #define COUNT_FREE_PARAMS(nf) \
367 do{ int jj ; \
368 if( verb > 4 ) fprintf(stderr,"++ Free params:") ; \
369 for( (nf)=jj=0 ; jj < stup.wfunc_numpar ; jj++ ){ \
370 if( !stup.wfunc_param[jj].fixed ){ \
371 (nf)++ ; if( verb > 4 ) fprintf(stderr," %d",jj) ; \
372 } \
373 } \
374 if( verb > 4 ) fprintf(stderr,"\n") ; \
375 } while(0)
376
377 /*--------- Macro to save Pearson map (for use in main) [27 Jan 2021] --------*/
378
379 #define SAVE_PEARSON_MAP(sprefix,val_xxx) \
380 do{ MRI_IMAGE *pim ; \
381 PAR_CPY(val_xxx) ; /* copy output parameters to allpar[] */ \
382 pim = mri_genalign_map_pearson_local(&stup,allpar) ; /* get 3D map */ \
383 if( pim != NULL ){ \
384 THD_3dim_dataset *pset ; \
385 pset = THD_volume_to_dataset( dset_base , pim , /* convert to dataset */ \
386 (sprefix) , pad_xm,pad_xp, \
387 pad_ym,pad_yp,pad_zm,pad_zp ) ; \
388 mri_free(pim) ; \
389 if( pset != NULL ){ /* save dataset */ \
390 DSET_write(pset) ; WROTE_DSET(pset) ; DSET_delete(pset) ; \
391 } else { \
392 ERROR_message("Failed to create -PearSave dataset %s :(",(sprefix)) ; \
393 } \
394 } else { \
395 ERROR_message("Failed to create -PearSave volume %s :(",(sprefix)) ; \
396 } \
397 } while(0)
398
399 /*---------------------------------------------------------------------------*/
400 /* Given a string, find the cost functional method code that goes with it. */
401
meth_name_to_code(char * nam)402 int meth_name_to_code( char *nam ) /* 15 Dec 2010 */
403 {
404 int ii ;
405 if( nam == NULL || *nam == '\0' ) return 0 ;
406 for( ii=0 ; ii < NMETH ; ii++ ){
407 if( strcasecmp (nam,meth_shortname[ii]) == 0 ) return (ii+1) ;
408 if( strncasecmp(nam,meth_longname[ii],7) == 0 ) return (ii+1) ;
409 }
410 return 0 ;
411 }
412
413 /*---------------------------------------------------------------------------*/
414 /* Given an interpolation code, find the name that goes with it. */
415
INTERP_methname(int iii)416 char * INTERP_methname( int iii )
417 {
418 switch( iii ){
419 default: return "UNKNOWN" ;
420 case MRI_NN: return "NN" ;
421 case MRI_LINEAR: return "linear" ;
422 case MRI_CUBIC: return "cubic" ;
423 case MRI_QUINTIC: return "quintic" ;
424 case MRI_HEPTIC: return "heptic" ;
425 case MRI_WSINC5: return "wsinc5" ;
426 }
427 return "MYSTERIOUS" ; /* unreachable */
428 }
429
430 /*---------------------------- And vice-versa ------------------------------*/
431
INTERP_code(char * name)432 int INTERP_code( char *name )
433 {
434 if( name == NULL ) return -1 ;
435 if( strcmp(name,"NN") == 0 || strncmp(name,"nearest",5) == 0 )
436 return MRI_NN ;
437 else
438 if( strncmp(name,"linear",3) == 0 || strncmp(name,"trilinear",5) == 0 )
439 return MRI_LINEAR ;
440 else
441 if( strncmp(name,"cubic",3) == 0 || strncmp(name,"tricubic",5) == 0 )
442 return MRI_CUBIC ;
443 else
444 if( strncmp(name,"quintic",3)==0 || strncmp(name,"triquintic",5)==0 )
445 return MRI_QUINTIC ;
446 else
447 if( strncasecmp(name,"WSINC",5)==0 )
448 return MRI_WSINC5 ;
449
450 return -1 ;
451 }
452
453 /*---------------------------------------------------------------------------*/
454
Allin_Help(void)455 void Allin_Help(void) /* moved here 15 Mar 2021 */
456 {
457 int ii ;
458
459 visible_noweights = 0 ;
460 for( ii=0 ; ii < NMETH ; ii++ )
461 if( meth_visible[ii] && meth_noweight[ii] ) visible_noweights++ ;
462
463 printf("\n"
464 "Usage: 3dAllineate [options] sourcedataset\n"
465 "\n"
466 "--------------------------------------------------------------------------\n"
467 " Program to align one dataset (the 'source') to a 'base'\n"
468 " dataset, using an affine (matrix) transformation of space.\n"
469 "--------------------------------------------------------------------------\n"
470 "\n"
471 "--------------------------------------------------------------------------\n"
472 " ***** Please check your results visually, or at some point *****\n"
473 " ***** in time you will have bad results and not know it :-( *****\n"
474 " ***** *****\n"
475 " ***** No method for 3D image alignment, however tested it *****\n"
476 " ***** was, can be relied upon 100%% of the time, and anyone *****\n"
477 " ***** who tells you otherwise is a madman or is a liar!!!! *****\n"
478 " ***** *****\n"
479 " ***** In particular, if you are aligning two datasets with *****\n"
480 " ***** significantly different spatial coverage (e.g., *****\n"
481 " ***** -source = whole head T1w and -base = MNI template), *****\n"
482 " ***** the be careful to check the results. In such a case, *****\n"
483 " ***** using '-twobest MAX' should increase the chance of *****\n"
484 " ***** getting a good alignment (at the cost of CPU time). *****\n"
485 " ***** *****\n"
486 " ***** Furthermore, don't EVER think that \"I have so much *****\n"
487 " ***** data that a few errors will not matter\"!!!! *****\n"
488 "--------------------------------------------------------------------------\n"
489 "\n"
490 "* Options (lots of them!) are available to control:\n"
491 " ++ How the matching between the source and the base is computed\n"
492 " (i.e., the 'cost functional' measuring image mismatch).\n"
493 " ++ How the resliced source is interpolated to the base space.\n"
494 " ++ The complexity of the spatial transformation ('warp') used.\n"
495 " ++ And many many technical options to control the process in detail,\n"
496 " if you know what you are doing (or just like to fool around).\n"
497 "\n"
498 "* This program is a generalization of and improvement on the older\n"
499 " software 3dWarpDrive.\n"
500 "\n"
501 "* For nonlinear transformations, see progam 3dQwarp.\n"
502 "\n"
503 "* 3dAllineate can also be used to apply a pre-computed matrix to a dataset\n"
504 " to produce the transformed output. In this mode of operation, it just\n"
505 " skips the alignment process, whose function is to compute the matrix,\n"
506 " and instead it reads the matrix in, computes the output dataset,\n"
507 " writes it out, and stops.\n"
508 "\n"
509 "* If you are curious about the stepwise process used, see the section below\n"
510 " titled 'SUMMARY of the Default Allineation Process'.\n"
511 "\n"
512 "=====----------------------------------------------------------------------\n"
513 "NOTES: For most 3D image registration purposes, we now recommend that you\n"
514 "===== use Daniel Glen's script align_epi_anat.py (which, despite its name,\n"
515 " can do many more registration problems than EPI-to-T1-weighted).\n"
516 " -->> In particular, using 3dAllineate with the 'lpc' cost functional\n"
517 " (to align EPI and T1-weighted volumes) requires using a '-weight'\n"
518 " volume to get good results, and the align_epi_anat.py script will\n"
519 " automagically generate such a weight dataset that works well for\n"
520 " EPI-to-structural alignment.\n"
521 " -->> This script can also be used for other alignment purposes, such\n"
522 " as T1-weighted alignment between field strengths using the\n"
523 " '-lpa' cost functional. Investigate align_epi_anat.py to\n"
524 " see if it will do what you need -- you might make your life\n"
525 " a little easier and nicer and happier and more tranquil.\n"
526 " -->> Also, if/when you ask for registration help on the AFNI\n"
527 " message board, we'll probably start by recommending that you\n"
528 " try align_epi_anat.py if you haven't already done so.\n"
529 " -->> For aligning EPI and T1-weighted volumes, we have found that\n"
530 " using a flip angle of 50-60 degrees for the EPI works better than\n"
531 " a flip angle of 90 degrees. The reason is that there is more\n"
532 " internal contrast in the EPI data when the flip angle is smaller,\n"
533 " so the registration has some image structure to work with. With\n"
534 " the 90 degree flip angle, there is so little internal contrast in\n"
535 " the EPI dataset that the alignment process ends up being just\n"
536 " trying to match brain outlines -- which doesn't always give accurate\n"
537 " results: see http://dx.doi.org/10.1016/j.neuroimage.2008.09.037\n"
538 " -->> Although the total MRI signal is reduced at a smaller flip angle,\n"
539 " there is little or no loss in FMRI/BOLD information, since the bulk\n"
540 " of the time series 'noise' is from physiological fluctuation signals,\n"
541 " which are also reduced by the lower flip angle -- for more details,\n"
542 " see http://dx.doi.org/10.1016/j.neuroimage.2010.11.020\n"
543 "---------------------------------------------------------------------------\n"
544 " **** New (Summer 2013) program 3dQwarp is available to do nonlinear ****\n"
545 " *** alignment between a base and source dataset, including the use ***\n"
546 " ** of 3dAllineate for the preliminary affine alignment. If you are **\n"
547 " * interested, see the output of '3dQwarp -help' for the details. *\n"
548 "---------------------------------------------------------------------------\n"
549 "\n"
550 "COMMAND LINE OPTIONS:\n"
551 "====================\n"
552 " -base bbb = Set the base dataset to be the #0 sub-brick of 'bbb'.\n"
553 " If no -base option is given, then the base volume is\n"
554 " taken to be the #0 sub-brick of the source dataset.\n"
555 " (Base must be stored as floats, shorts, or bytes.)\n"
556 " ** -base is not needed if you are just applying a given\n"
557 " transformation to the -source dataset to produce\n"
558 " the output, using -1Dmatrix_apply or -1Dparam_apply\n"
559 " ** Unless you use the -master option, the aligned\n"
560 " output dataset will be stored on the same 3D grid\n"
561 " as the -base dataset.\n"
562 "\n"
563 " -source ttt = Read the source dataset from 'ttt'. If no -source\n"
564 " *OR* (or -input) option is given, then the source dataset\n"
565 " -input ttt is the last argument on the command line.\n"
566 " (Source must be stored as floats, shorts, or bytes.)\n"
567 " ** This is the dataset to be transformed, to match the\n"
568 " -base dataset, or directly with one of the options\n"
569 " -1Dmatrix_apply or -1Dparam_apply\n"
570 " ** 3dAllineate can register 2D datasets (single slice),\n"
571 " but both the base and source must be 2D -- you cannot\n"
572 " use this program to register a 2D slice into a 3D volume!\n"
573 " -- However, the 'lpc' and 'lpa' cost functionals do not\n"
574 " work properly with 2D images, as they are designed\n"
575 " around local 3D neighborhoods and that code has not\n"
576 " been patched to work with 2D neighborhoods :(\n"
577 " -- You can input .jpg files as 2D 'datasets', register\n"
578 " them with 3dAllineate, and write the result back out\n"
579 " using a prefix that ends in '.jpg'; HOWEVER, the color\n"
580 " information will not be used in the registration, as\n"
581 " this program was written to deal with monochrome medical\n"
582 " datasets. At the end, if the source was RGB (color), then\n"
583 " the output will be also be RGB, and then a color .jpg\n"
584 " can be output.\n"
585 " -- The above remarks also apply to aligning 3D RGB datasets:\n"
586 " it will be done using only the 3D volumes converted to\n"
587 " grayscale, but the final output will be the source\n"
588 " RGB dataset transformed to the (hopefully) aligned grid.\n"
589 " * However, I've never tested aligning 3D color datasets;\n"
590 " you can be the first one ever!\n"
591 " ** See the script @2dwarper.Allin for an example of using\n"
592 " 3dAllineate to do slice-by-slice nonlinear warping to\n"
593 " align 3D volumes distorted by time-dependent magnetic\n"
594 " field inhomogeneities.\n"
595 "\n"
596 " ** NOTA BENE: The base and source dataset do NOT have to be defined **\n"
597 " ** [that's] on the same 3D grids; the alignment process uses the **\n"
598 " ** [Latin ] coordinate systems defined in the dataset headers to **\n"
599 " ** [ for ] make the match between spatial locations, rather than **\n"
600 " ** [ NOTE ] matching the 2 datasets on a voxel-by-voxel basis **\n"
601 " ** [ WELL ] (as 3dvolreg and 3dWarpDrive do). **\n"
602 " ** -->> However, this coordinate-based matching requires that **\n"
603 " ** image volumes be defined on roughly the same patch of **\n"
604 " ** of (x,y,z) space, in order to find a decent starting **\n"
605 " ** point for the transformation. You might need to use **\n"
606 " ** the script @Align_Centers to do this, if the 3D **\n"
607 " ** spaces occupied by the images do not overlap much. **\n"
608 " ** -->> Or the '-cmass' option to this program might be **\n"
609 " ** sufficient to solve this problem, maybe, with luck. **\n"
610 " ** (Another reason why you should use align_epi_anat.py) **\n"
611 " ** -->> If the coordinate system in the dataset headers is **\n"
612 " ** WRONG, then 3dAllineate will probably not work well! **\n"
613 " ** And I say this because we have seen this in several **\n"
614 " ** datasets downloaded from online archives. **\n"
615 "\n"
616 " -prefix ppp = Output the resulting dataset to file 'ppp'. If this\n"
617 " *OR* option is NOT given, no dataset will be output! The\n"
618 " -out ppp transformation matrix to align the source to the base will\n"
619 " be estimated, but not applied. You can save the matrix\n"
620 " for later use using the '-1Dmatrix_save' option.\n"
621 " *N.B.: By default, the new dataset is computed on the grid of the\n"
622 " base dataset; see the '-master' and/or the '-mast_dxyz'\n"
623 " options to change this grid.\n"
624 " *N.B.: If 'ppp' is 'NULL', then no output dataset will be produced.\n"
625 " This option is for compatibility with 3dvolreg.\n"
626 "\n"
627 " -floatize = Write result dataset as floats. Internal calculations\n"
628 " -float are all done on float copies of the input datasets.\n"
629 " [Default=convert output dataset to data format of ]\n"
630 " [ source dataset; if the source dataset was ]\n"
631 " [ shorts with a scale factor, then the new ]\n"
632 " [ dataset will get a scale factor as well; ]\n"
633 " [ if the source dataset was shorts with no ]\n"
634 " [ scale factor, the result will be unscaled.]\n"
635 "\n"
636 " -1Dparam_save ff = Save the warp parameters in ASCII (.1D) format into\n"
637 " file 'ff' (1 row per sub-brick in source).\n"
638 " * A historical synonym for this option is '-1Dfile'.\n"
639 " * At the top of the saved 1D file is a #comment line\n"
640 " listing the names of the parameters; those parameters\n"
641 " that are fixed (e.g., via '-parfix') will be marked\n"
642 " by having their symbolic names end in the '$' character.\n"
643 " You can use '1dcat -nonfixed' to remove these columns\n"
644 " from the 1D file if you just want to further process the\n"
645 " varying parameters somehow (e.g., 1dsvd).\n"
646 " * However, the '-1Dparam_apply' option requires the\n"
647 " full list of parameters, including those that were\n"
648 " fixed, in order to work properly!\n"
649 "\n"
650 " -1Dparam_apply aa = Read warp parameters from file 'aa', apply them to \n"
651 " the source dataset, and produce a new dataset.\n"
652 " (Must also use the '-prefix' option for this to work! )\n"
653 " (In this mode of operation, there is no optimization of)\n"
654 " (the cost functional by changing the warp parameters; )\n"
655 " (previously computed parameters are applied directly. )\n"
656 /** " *N.B.: A historical synonym for this is '-1Dapply'.\n" **/
657 " *N.B.: If you use -1Dparam_apply, you may also want to use\n"
658 " -master to control the grid on which the new\n"
659 " dataset is written -- the base dataset from the\n"
660 " original 3dAllineate run would be a good possibility.\n"
661 " Otherwise, the new dataset will be written out on the\n"
662 " 3D grid coverage of the source dataset, and this\n"
663 " might result in clipping off part of the image.\n"
664 " *N.B.: Each row in the 'aa' file contains the parameters for\n"
665 " transforming one sub-brick in the source dataset.\n"
666 " If there are more sub-bricks in the source dataset\n"
667 " than there are rows in the 'aa' file, then the last\n"
668 " row is used repeatedly.\n"
669 " *N.B.: A trick to use 3dAllineate to resample a dataset to\n"
670 " a finer grid spacing:\n"
671 " 3dAllineate -input dataset+orig \\\n"
672 " -master template+orig \\\n"
673 " -prefix newdataset \\\n"
674 " -final wsinc5 \\\n"
675 " -1Dparam_apply '1D: 12@0'\\' \n"
676 " Here, the identity transformation is specified\n"
677 " by giving all 12 affine parameters as 0 (note\n"
678 " the extra \\' at the end of the '1D: 12@0' input!).\n"
679 " ** You can also use the word 'IDENTITY' in place of\n"
680 " '1D: 12@0'\\' (to indicate the identity transformation).\n"
681 " **N.B.: Some expert options for modifying how the wsinc5\n"
682 " method works are described far below, if you use\n"
683 " '-HELP' instead of '-help'.\n"
684 " ****N.B.: The interpolation method used to produce a dataset\n"
685 " is always given via the '-final' option, NOT via\n"
686 " '-interp'. If you forget this and use '-interp'\n"
687 " along with one of the 'apply' options, this program\n"
688 " will chastise you (gently) and change '-final'\n"
689 " to match what the '-interp' input.\n"
690 "\n"
691 " -1Dmatrix_save ff = Save the transformation matrix for each sub-brick into\n"
692 " file 'ff' (1 row per sub-brick in the source dataset).\n"
693 " If 'ff' does NOT end in '.1D', then the program will\n"
694 " append '.aff12.1D' to 'ff' to make the output filename.\n"
695 " *N.B.: This matrix is the coordinate transformation from base\n"
696 " to source DICOM coordinates. In other terms:\n"
697 " Xin = Xsource = M Xout = M Xbase\n"
698 " or\n"
699 " Xout = Xbase = inv(M) Xin = inv(M) Xsource\n"
700 " where Xin or Xsource is the 4x1 coordinates of a\n"
701 " location in the input volume. Xout is the \n"
702 " coordinate of that same location in the output volume.\n"
703 " Xbase is the coordinate of the corresponding location\n"
704 " in the base dataset. M is ff augmented by a 4th row of\n"
705 " [0 0 0 1], X. is an augmented column vector [x,y,z,1]'\n"
706 " To get the inverse matrix inv(M)\n"
707 " (source to base), use the cat_matvec program, as in\n"
708 " cat_matvec fred.aff12.1D -I\n"
709 "\n"
710 " -1Dmatrix_apply aa = Use the matrices in file 'aa' to define the spatial\n"
711 " transformations to be applied. Also see program\n"
712 " cat_matvec for ways to manipulate these matrix files.\n"
713 " *N.B.: You probably want to use either -base or -master\n"
714 " with either *_apply option, so that the coordinate\n"
715 " system that the matrix refers to is correctly loaded.\n"
716 " ** You can also use the word 'IDENTITY' in place of a\n"
717 " filename to indicate the identity transformation --\n"
718 " presumably for the purpose of resampling the source\n"
719 " dataset to a new grid.\n"
720 "\n"
721 " * The -1Dmatrix_* options can be used to save and re-use the transformation *\n"
722 " * matrices. In combination with the program cat_matvec, which can multiply *\n"
723 " * saved transformation matrices, you can also adjust these matrices to *\n"
724 " * other alignments. These matrices can also be combined with nonlinear *\n"
725 " * warps (from 3dQwarp) using programs 3dNwarpApply or 3dNwarpCat. *\n"
726 "\n"
727 " * The script 'align_epi_anat.py' uses 3dAllineate and 3dvolreg to align EPI *\n"
728 " * datasets to T1-weighted anatomical datasets, using saved matrices between *\n"
729 " * the two programs. This script is our currently recommended method for *\n"
730 " * doing such intra-subject alignments. *\n"
731 "\n"
732 " -cost ccc = Defines the 'cost' function that defines the matching\n"
733 " between the source and the base; 'ccc' is one of\n"
734 ) ;
735
736 for( ii=0 ; ii < NMETH ; ii++ )
737 if( meth_visible[ii] )
738 printf( " %-4s *OR* %-16s= %s\n" ,
739 meth_shortname[ii] , meth_longname[ii] , meth_username[ii] ) ;
740
741 printf(
742 " You can also specify the cost functional using an option\n"
743 " of the form '-mi' rather than '-cost mi', if you like\n"
744 " to keep things terse and cryptic (as I do).\n"
745 " [Default == '-hel' (for no good reason, but it sounds nice).]\n"
746 " **NB** See more below about lpa and lpc, which are typically\n"
747 " what we would recommend as first-choice cost functions\n"
748 " now:\n"
749 " lpa if you have similar contrast vols to align;\n"
750 " lpc if you have *non*similar contrast vols to align!\n"
751 "\n"
752 " -interp iii = Defines interpolation method to use during matching\n"
753 " process, where 'iii' is one of\n"
754 " NN *OR* nearestneighbour *OR nearestneighbor\n"
755 " linear *OR* trilinear\n"
756 " cubic *OR* tricubic\n"
757 " quintic *OR* triquintic\n"
758 " Using '-NN' instead of '-interp NN' is allowed (e.g.).\n"
759 " Note that using cubic or quintic interpolation during\n"
760 " the matching process will slow the program down a lot.\n"
761 " Use '-final' to affect the interpolation method used\n"
762 " to produce the output dataset, once the final registration\n"
763 " parameters are determined. [Default method == 'linear'.]\n"
764 " ** N.B.: Linear interpolation is used during the coarse\n"
765 " alignment pass; the selection here only affects\n"
766 " the interpolation method used during the second\n"
767 " (fine) alignment pass.\n"
768 " ** N.B.: '-interp' does NOT define the final method used\n"
769 " to produce the output dataset as warped from the\n"
770 " input dataset. If you want to do that, use '-final'.\n"
771 "\n"
772 " -final iii = Defines the interpolation mode used to create the\n"
773 " output dataset. [Default == 'cubic']\n"
774 " ** N.B.: If you are applying a transformation to an\n"
775 " integer-valued dataset (such as an atlas),\n"
776 " then you should use '-final NN' to avoid\n"
777 " interpolation of the integer labels.\n"
778 " ** N.B.: For '-final' ONLY, you can use 'wsinc5' to specify\n"
779 " that the final interpolation be done using a\n"
780 " weighted sinc interpolation method. This method\n"
781 " is so SLOW that you aren't allowed to use it for\n"
782 " the registration itself.\n"
783 " ++ wsinc5 interpolation is highly accurate and should\n"
784 " reduce the smoothing artifacts from lower\n"
785 " order interpolation methods (which are most\n"
786 " visible if you interpolate an EPI time series\n"
787 " to high resolution and then make an image of\n"
788 " the voxel-wise variance).\n"
789 " ++ On my Intel-based Mac, it takes about 2.5 s to do\n"
790 " wsinc5 interpolation, per 1 million voxels output.\n"
791 " For comparison, quintic interpolation takes about\n"
792 " 0.3 s per 1 million voxels: 8 times faster than wsinc5.\n"
793 " ++ The '5' refers to the width of the sinc interpolation\n"
794 " weights: plus/minus 5 grid points in each direction;\n"
795 " this is a tensor product interpolation, for speed.\n"
796 "\n"
797 "TECHNICAL OPTIONS (used for fine control of the program):\n"
798 "=================\n"
799 " -nmatch nnn = Use at most 'nnn' scattered points to match the\n"
800 " datasets. The smaller nnn is, the faster the matching\n"
801 " algorithm will run; however, accuracy may be bad if\n"
802 " nnn is too small. If you end the 'nnn' value with the\n"
803 " '%%' character, then that percentage of the base's\n"
804 " voxels will be used.\n"
805 " [Default == 47%% of voxels in the weight mask]\n"
806 "\n"
807 " -nopad = Do not use zero-padding on the base image.\n"
808 " (I cannot think of a good reason to use this option.)\n"
809 " [Default == zero-pad, if needed; -verb shows how much]\n"
810 "\n"
811 " -zclip = Replace negative values in the input datasets (source & base)\n"
812 " -noneg with zero. The intent is to clip off a small set of negative\n"
813 " values that may arise when using 3dresample (say) with\n"
814 " cubic interpolation.\n"
815 "\n"
816 " -conv mmm = Convergence test is set to 'mmm' millimeters.\n"
817 " This doesn't mean that the results will be accurate\n"
818 " to 'mmm' millimeters! It just means that the program\n"
819 " stops trying to improve the alignment when the optimizer\n"
820 " (NEWUOA) reports it has narrowed the search radius\n"
821 " down to this level.\n"
822 " * To set this value to the smallest allowable, use '-conv 0'.\n"
823 " * A coarser value for 'quick-and-dirty' alignment is 0.05.\n"
824 "\n"
825 " -verb = Print out verbose progress reports.\n"
826 " [Using '-VERB' will give even more prolix reports :]\n"
827 " -quiet = Don't print out verbose stuff. (But WHY?)\n"
828 "\n"
829 " -usetemp = Write intermediate stuff to disk, to economize on RAM.\n"
830 " Using this will slow the program down, but may make it\n"
831 " possible to register datasets that need lots of space.\n"
832 " **N.B.: Temporary files are written to the directory given\n"
833 " in environment variable TMPDIR, or in /tmp, or in ./\n"
834 " (preference in that order). If the program crashes,\n"
835 " these files are named TIM_somethingrandom, and you\n"
836 " may have to delete them manually. (TIM=Temporary IMage)\n"
837 " **N.B.: If the program fails with a 'malloc failure' type of\n"
838 " message, then try '-usetemp' (malloc=memory allocator).\n"
839 " * If the program just stops with a message 'killed', that\n"
840 " means the operating system (Unix/Linux) stopped the\n"
841 " program, which almost always is due to the system running\n"
842 " low on memory -- so it starts killing programs to save itself.\n"
843 #ifdef USING_MCW_MALLOC
844 " **N.B.: If you use '-verb', then memory usage is printed out\n"
845 " at various points along the way.\n"
846 #endif
847 "\n"
848 " -nousetemp = Don't use temporary workspace on disk [the default].\n"
849 "\n"
850 #ifdef ALLOW_METH_CHECK
851 " -check hhh = After cost functional optimization is done, start at the\n"
852 " final parameters and RE-optimize using the new cost\n"
853 " function 'hhh'. If the results are too different, a\n"
854 " warning message will be printed. However, the final\n"
855 " parameters from the original optimization will be\n"
856 " used to create the output dataset. Using '-check'\n"
857 " increases the CPU time, but can help you feel sure\n"
858 " that the alignment process did not go wild and crazy.\n"
859 " [Default == no check == don't worry, be happy!]\n"
860 " **N.B.: You can put more than one function after '-check', as in\n"
861 " -nmi -check mi hel crU crM\n"
862 " to register with Normalized Mutual Information, and\n"
863 " then check the results against 4 other cost functionals.\n"
864 " **N.B.: On the other hand, some cost functionals give better\n"
865 " results than others for specific problems, and so\n"
866 " a warning that 'mi' was significantly different than\n"
867 " 'hel' might not actually mean anything useful (e.g.).\n"
868 #if 0
869 " **N.B.: If you use '-CHECK' instead of '-check', AND there are\n"
870 " at least two extra check functions specified (in addition\n"
871 " to the primary cost functional), then the output parameter\n"
872 " set will be the median of all the final parameter sets\n"
873 " generated at this stage (including the primary set).\n"
874 " **** '-CHECK' is experimental and CPU intensive ****\n"
875 #endif
876 #endif
877 "\n"
878 " ** PARAMETERS THAT AFFECT THE COST OPTIMIZATION STRATEGY **\n"
879 "\n"
880 " -onepass = Use only the refining pass -- do not try a coarse\n"
881 " resolution pass first. Useful if you know that only\n"
882 " SMALL amounts of image alignment are needed.\n"
883 " [The default is to use both passes.]\n"
884 "\n"
885 " -twopass = Use a two pass alignment strategy, first searching for\n"
886 " a large rotation+shift and then refining the alignment.\n"
887 " [Two passes are used by default for the first sub-brick]\n"
888 " [in the source dataset, and then one pass for the others.]\n"
889 " ['-twopass' will do two passes for ALL source sub-bricks.]\n"
890 " *** The first (coarse) pass is relatively slow, as it tries\n"
891 " to search a large volume of parameter (rotations+shifts)\n"
892 " space for initial guesses at the alignment transformation.\n"
893 " * A lot of these initial guesses are kept and checked to\n"
894 " see which ones lead to good starting points for the\n"
895 " further refinement.\n"
896 " * The winners of this competition are then passed to the\n"
897 " '-twobest' (infra) successive optimization passes.\n"
898 " * The ultimate winner of THAT stage is what starts\n"
899 " the second (fine) pass alignment. Usually, this starting\n"
900 " point is so good that the fine pass optimization does\n"
901 " not provide a lot of improvement; that is, most of the\n"
902 " run time ends up in coarse pass with its multiple stages.\n"
903 " * All of these stages are intended to help the program avoid\n"
904 " stopping at a 'false' minimum in the cost functional.\n"
905 " They were added to the software as we gathered experience\n"
906 " with difficult 3D alignment problems. The combination of\n"
907 " multiple stages of partial optimization of multiple\n"
908 " parameter candidates makes the coarse pass slow, but also\n"
909 " makes it (usually) work well.\n"
910 "\n"
911 " -twoblur rr = Set the blurring radius for the first pass to 'rr'\n"
912 " millimeters. [Default == 11 mm]\n"
913 " **N.B.: You may want to change this from the default if\n"
914 " your voxels are unusually small or unusually large\n"
915 " (e.g., outside the range 1-4 mm along each axis).\n"
916 "\n"
917 " -twofirst = Use -twopass on the first image to be registered, and\n"
918 " then on all subsequent images from the source dataset,\n"
919 " use results from the first image's coarse pass to start\n"
920 " the fine pass.\n"
921 " (Useful when there may be large motions between the )\n"
922 " (source and the base, but only small motions within )\n"
923 " (the source dataset itself; since the coarse pass can )\n"
924 " (be slow, doing it only once makes sense in this case.)\n"
925 " **N.B.: [-twofirst is on by default; '-twopass' turns it off.]\n"
926 "\n"
927 " -twobest bb = In the coarse pass, use the best 'bb' set of initial\n"
928 " points to search for the starting point for the fine\n"
929 " pass. If bb==0, then no search is made for the best\n"
930 " starting point, and the identity transformation is\n"
931 " used as the starting point. [Default=%d; min=0 max=%d]\n"
932 " **N.B.: Setting bb=0 will make things run faster, but less reliably.\n"
933 " Setting bb = 'MAX' will make it be the max allowed value.\n"
934 "\n"
935 " -fineblur x = Set the blurring radius to use in the fine resolution\n"
936 " pass to 'x' mm. A small amount (1-2 mm?) of blurring at\n"
937 " the fine step may help with convergence, if there is\n"
938 " some problem, especially if the base volume is very noisy.\n"
939 " [Default == 0 mm = no blurring at the final alignment pass]\n"
940 "\n"
941 " **NOTES ON\n"
942 " **STRATEGY: * If you expect only small-ish (< 2 voxels?) image movement,\n"
943 " then using '-onepass' or '-twobest 0' makes sense.\n"
944 " * If you expect large-ish image movements, then do not\n"
945 " use '-onepass' or '-twobest 0'; the purpose of the\n"
946 " '-twobest' parameter is to search for large initial\n"
947 " rotations/shifts with which to start the coarse\n"
948 " optimization round.\n"
949 " * If you have multiple sub-bricks in the source dataset,\n"
950 " then the default '-twofirst' makes sense if you don't expect\n"
951 " large movements WITHIN the source, but expect large motions\n"
952 " between the source and base.\n"
953 " * '-twopass' re-starts the alignment process for each sub-brick\n"
954 " in the source dataset -- this option can be time consuming,\n"
955 " and is really intended to be used when you might expect large\n"
956 " movements between sub-bricks; for example, when the different\n"
957 " volumes are gathered on different days. For most purposes,\n"
958 " '-twofirst' (the default process) will be adequate and faster,\n"
959 " when operating on multi-volume source datasets.\n"
960
961 , DEFAULT_TBEST , PARAM_MAXTRIAL /* for -twobest */
962 ) ;
963
964 printf(
965 "\n"
966 " -cmass = Use the center-of-mass calculation to determin an initial shift\n"
967 " [This option is OFF by default]\n"
968 " can be given as cmass+a, cmass+xy, cmass+yz, cmass+xz\n"
969 " where +a means to try determine automatically in which\n"
970 " direction the data is partial by looking for a too large shift\n"
971 " If given in the form '-cmass+xy' (for example), means to\n"
972 " do the CoM calculation in the x- and y-directions, but\n"
973 " not the z-direction.\n"
974 " * MY OPINION: This option is REALLY useful in most cases.\n"
975 " However, if you only have partial coverage in\n"
976 " the -source dataset, you will need to use\n"
977 " one of the '+' additions to restrict the\n"
978 " use of the CoM limits.\n"
979 "\n"
980 " -nocmass = Don't use the center-of-mass calculation. [The default]\n"
981 " (You would not want to use the C-o-M calculation if the )\n"
982 " (source sub-bricks have very different spatial locations,)\n"
983 " (since the source C-o-M is calculated from all sub-bricks)\n"
984 "\n"
985 " **EXAMPLE: You have a limited coverage set of axial EPI slices you want to\n"
986 " register into a larger head volume (after 3dSkullStrip, of course).\n"
987 " In this case, '-cmass+xy' makes sense, allowing CoM adjustment\n"
988 " along the x = R-L and y = A-P directions, but not along the\n"
989 " z = I-S direction, since the EPI doesn't cover the whole brain\n"
990 " along that axis.\n"
991 ) ;
992
993 printf(
994 "\n"
995 " -autoweight = Compute a weight function using the 3dAutomask\n"
996 " algorithm plus some blurring of the base image.\n"
997 " **N.B.: '-autoweight+100' means to zero out all voxels\n"
998 " with values below 100 before computing the weight.\n"
999 " '-autoweight**1.5' means to compute the autoweight\n"
1000 " and then raise it to the 1.5-th power (e.g., to\n"
1001 " increase the weight of high-intensity regions).\n"
1002 " These two processing steps can be combined, as in\n"
1003 " '-autoweight+100**1.5'\n"
1004 " ** Note that '**' must be enclosed in quotes;\n"
1005 " otherwise, the shell will treat it as a wildcard\n"
1006 " and you will get an error message before 3dAllineate\n"
1007 " even starts!!\n"
1008 " ** UPDATE: one can now use '^' for power notation, to \n"
1009 " avoid needing to enclose the string in quotes.\n"
1010 ) ;
1011 if( visible_noweights ){
1012 printf(
1013 " **N.B.: Some cost functionals do not allow -autoweight, and\n"
1014 " will use -automask instead. A warning message\n"
1015 " will be printed if you run into this situation.\n"
1016 " If a clip level '+xxx' is appended to '-autoweight',\n"
1017 " then the conversion into '-automask' will NOT happen.\n"
1018 " Thus, using a small positive '+xxx' can be used trick\n"
1019 " -autoweight into working on any cost functional.\n"
1020 ) ;
1021 }
1022 printf(
1023 "\n"
1024 " -automask = Compute a mask function, which is like -autoweight,\n"
1025 " but the weight for a voxel is set to either 0 or 1.\n"
1026 " **N.B.: '-automask+3' means to compute the mask function, and\n"
1027 " then dilate it outwards by 3 voxels (e.g.).\n"
1028 " ** Note that '+' means something very different\n"
1029 " for '-automask' and '-autoweight'!!\n"
1030 "\n"
1031 " -autobox = Expand the -automask function to enclose a rectangular\n"
1032 " box that holds the irregular mask.\n"
1033 " **N.B.: This is the default mode of operation!\n"
1034 " For intra-modality registration, '-autoweight' may be better!\n"
1035 " * If the cost functional is 'ls', then '-autoweight' will be\n"
1036 " the default, instead of '-autobox'.\n"
1037 "\n"
1038 " -nomask = Don't compute the autoweight/mask; if -weight is not\n"
1039 " also used, then every voxel will be counted equally.\n"
1040 "\n"
1041 " -weight www = Set the weighting for each voxel in the base dataset;\n"
1042 " larger weights mean that voxel counts more in the cost\n"
1043 " function.\n"
1044 " **N.B.: The weight dataset must be defined on the same grid as\n"
1045 " the base dataset.\n"
1046 " **N.B.: Even if a method does not allow -autoweight, you CAN\n"
1047 " use a weight dataset that is not 0/1 valued. The\n"
1048 " risk is yours, of course (!*! as always in AFNI !*!).\n"
1049 "\n"
1050 " -wtprefix p = Write the weight volume to disk as a dataset with\n"
1051 " prefix name 'p'. Used with '-autoweight/mask', this option\n"
1052 " lets you see what voxels were important in the algorithm.\n"
1053 "\n"
1054 " -emask ee = This option lets you specify a mask of voxels to EXCLUDE from\n"
1055 " the analysis. The voxels where the dataset 'ee' is nonzero\n"
1056 " will not be included (i.e., their weights will be set to zero).\n"
1057 " * Like all the weight options, it applies in the base image\n"
1058 " coordinate system.\n"
1059 " ** Like all the weight options, it means nothing if you are using\n"
1060 " one of the 'apply' options.\n"
1061 ) ;
1062
1063 if( visible_noweights > 0 ){
1064 printf("\n"
1065 " Method Allows -autoweight\n"
1066 " ------ ------------------\n") ;
1067 for( ii=0 ; ii < NMETH ; ii++ )
1068 if( meth_visible[ii] )
1069 printf(" %-4s %s\n", meth_shortname[ii] ,
1070 meth_noweight[ii] ? "NO" : "YES" ) ;
1071 }
1072
1073 printf(
1074 "\n"
1075 " -source_mask sss = Mask the source (input) dataset, using 'sss'.\n"
1076 " -source_automask = Automatically mask the source dataset.\n"
1077 " [By default, all voxels in the source]\n"
1078 " [dataset are used in the matching. ]\n"
1079 " **N.B.: You can also use '-source_automask+3' to dilate\n"
1080 " the default source automask outward by 3 voxels.\n"
1081 ) ;
1082
1083 printf(
1084 "\n"
1085 " -warp xxx = Set the warp type to 'xxx', which is one of\n"
1086 " shift_only *OR* sho = 3 parameters\n"
1087 " shift_rotate *OR* shr = 6 parameters\n"
1088 " shift_rotate_scale *OR* srs = 9 parameters\n"
1089 " affine_general *OR* aff = 12 parameters\n"
1090 " [Default = affine_general, which includes image]\n"
1091 " [ shifts, rotations, scaling, and shearing]\n"
1092 " * MY OPINION: Shearing is usually unimportant, so\n"
1093 " you can omit it if you want: '-warp srs'.\n"
1094 " But it doesn't hurt to keep shearing,\n"
1095 " except for a little extra CPU time.\n"
1096 " On the other hand, scaling is often\n"
1097 " important, so should not be omitted.\n"
1098 "\n"
1099 " -warpfreeze = Freeze the non-rigid body parameters (those past #6)\n"
1100 " after doing the first sub-brick. Subsequent volumes\n"
1101 " will have the same spatial distortions as sub-brick #0,\n"
1102 " plus rigid body motions only.\n"
1103 " * MY OPINION: This option is almost useless.\n"
1104 "\n"
1105 " -replacebase = If the source has more than one sub-brick, and this\n"
1106 " option is turned on, then after the #0 sub-brick is\n"
1107 " aligned to the base, the aligned #0 sub-brick is used\n"
1108 " as the base image for subsequent source sub-bricks.\n"
1109 " * MY OPINION: This option is almost useless.\n"
1110 "\n"
1111 " -replacemeth m = After sub-brick #0 is aligned, switch to method 'm'\n"
1112 " for later sub-bricks. For use with '-replacebase'.\n"
1113 " * MY OPINION: This option is almost useless.\n"
1114 "\n"
1115 " -EPI = Treat the source dataset as being composed of warped\n"
1116 " EPI slices, and the base as comprising anatomically\n"
1117 " 'true' images. Only phase-encoding direction image\n"
1118 " shearing and scaling will be allowed with this option.\n"
1119 " **N.B.: For most people, the base dataset will be a 3dSkullStrip-ed\n"
1120 " T1-weighted anatomy (MPRAGE or SPGR). If you don't remove\n"
1121 " the skull first, the EPI images (which have little skull\n"
1122 " visible due to fat-suppression) might expand to fit EPI\n"
1123 " brain over T1-weighted skull.\n"
1124 " **N.B.: Usually, EPI datasets don't have as complete slice coverage\n"
1125 " of the brain as do T1-weighted datasets. If you don't use\n"
1126 " some option (like '-EPI') to suppress scaling in the slice-\n"
1127 " direction, the EPI dataset is likely to stretch the slice\n"
1128 " thickness to better 'match' the T1-weighted brain coverage.\n"
1129 #if 0
1130 " **N.B.: '-EPI' turns on '-warpfreeze -replacebase -replacemeth ls'.\n"
1131 " To disable '-replacemeth ls', use '-replacemeth 0' after '-EPI'.\n"
1132 #else
1133 " **N.B.: '-EPI' turns on '-warpfreeze -replacebase'.\n"
1134 #endif
1135 " You can use '-nowarpfreeze' and/or '-noreplacebase' AFTER the\n"
1136 " '-EPI' on the command line if you do not want these options used.\n"
1137 "\n"
1138 " ** OPTIONS to change search ranges for alignment parameters **\n"
1139 "\n"
1140 " -smallrange = Set all the parameter ranges to be smaller (about half) than\n"
1141 " the default ranges, which are rather large for many purposes.\n"
1142 " * Default angle range is plus/minus 30 degrees\n"
1143 " * Default shift range is plus/minus 32%% of grid size\n"
1144 " * Default scaling range is plus/minus 20%% of grid size\n"
1145 " * Default shearing range is plus/minus 0.1111\n"
1146 "\n"
1147 " -parfix n v = Fix parameter #n to be exactly at value 'v'.\n"
1148 "\n"
1149 " -parang n b t = Allow parameter #n to range only between 'b' and 't'.\n"
1150 " If not given, default ranges are used.\n"
1151 "\n"
1152 " -parini n v = Initialize parameter #n to value 'v', but then\n"
1153 " allow the algorithm to adjust it.\n"
1154 " **N.B.: Multiple '-par...' options can be used, to constrain\n"
1155 " multiple parameters.\n"
1156 " **N.B.: -parini has no effect if -twopass is used, since\n"
1157 " the -twopass algorithm carries out its own search\n"
1158 " for initial parameters.\n"
1159 "\n"
1160 " -maxrot dd = Allow maximum rotation of 'dd' degrees. Equivalent\n"
1161 " to '-parang 4 -dd dd -parang 5 -dd dd -parang 6 -dd dd'\n"
1162 " [Default=30 degrees]\n"
1163 "\n"
1164 " -maxshf dd = Allow maximum shift of 'dd' millimeters. Equivalent\n"
1165 " to '-parang 1 -dd dd -parang 2 -dd dd -parang 3 -dd dd'\n"
1166 " [Default=32%% of the size of the base image]\n"
1167 " **N.B.: This max shift setting is relative to the center-of-mass\n"
1168 " shift, if the '-cmass' option is used.\n"
1169 "\n"
1170 " -maxscl dd = Allow maximum scaling factor to be 'dd'. Equivalent\n"
1171 " to '-parang 7 1/dd dd -parang 8 1/dd dd -paran2 9 1/dd dd'\n"
1172 " [Default=1.4=image can go up or down 40%% in size]\n"
1173 "\n"
1174 " -maxshr dd = Allow maximum shearing factor to be 'dd'. Equivalent\n"
1175 " to '-parang 10 -dd dd -parang 11 -dd dd -parang 12 -dd dd'\n"
1176 " [Default=0.1111 for no good reason]\n"
1177 "\n"
1178 " NOTE: If the datasets being registered have only 1 slice, 3dAllineate\n"
1179 " will automatically fix the 6 out-of-plane motion parameters to\n"
1180 " their 'do nothing' values, so you don't have to specify '-parfix'.\n"
1181 #if 0
1182 "\n"
1183 " -matini mmm = Initialize 3x4 affine transformation matrix to 'mmm',\n"
1184 " which is either a .1D file or an expression in the\n"
1185 " syntax of program 1dmatcalc. Using this option is\n"
1186 " like using '-parini' on all affine matrix parameters.\n"
1187 #endif
1188 "\n"
1189 " -master mmm = Write the output dataset on the same grid as dataset\n"
1190 " 'mmm'. If this option is NOT given, the base dataset\n"
1191 " is the master.\n"
1192 " **N.B.: 3dAllineate transforms the source dataset to be 'similar'\n"
1193 " to the base image. Therefore, the coordinate system\n"
1194 " of the master dataset is interpreted as being in the\n"
1195 " reference system of the base image. It is thus vital\n"
1196 " that these finite 3D volumes overlap, or you will lose data!\n"
1197 " **N.B.: If 'mmm' is the string 'SOURCE', then the source dataset\n"
1198 " is used as the master for the output dataset grid.\n"
1199 " You can also use 'BASE', which is of course the default.\n"
1200 "\n"
1201 " -mast_dxyz del = Write the output dataset using grid spacings of\n"
1202 " *OR* 'del' mm. If this option is NOT given, then the\n"
1203 " -newgrid del grid spacings in the master dataset will be used.\n"
1204 " This option is useful when registering low resolution\n"
1205 " data (e.g., EPI time series) to high resolution\n"
1206 " datasets (e.g., MPRAGE) where you don't want to\n"
1207 " consume vast amounts of disk space interpolating\n"
1208 " the low resolution data to some artificially fine\n"
1209 " (and meaningless) spatial grid.\n"
1210 ) ;
1211
1212 printf(
1213 "\n"
1214 "----------------------------------------------\n"
1215 "DEFINITION OF AFFINE TRANSFORMATION PARAMETERS\n"
1216 "----------------------------------------------\n"
1217 "The 3x3 spatial transformation matrix is calculated as [S][D][U],\n"
1218 "where [S] is the shear matrix,\n"
1219 " [D] is the scaling matrix, and\n"
1220 " [U] is the rotation (proper orthogonal) matrix.\n"
1221 "Thes matrices are specified in DICOM-ordered (x=-R+L,y=-A+P,z=-I+S)\n"
1222 "coordinates as:\n"
1223 "\n"
1224 " [U] = [Rotate_y(param#6)] [Rotate_x(param#5)] [Rotate_z(param #4)]\n"
1225 " (angles are in degrees)\n"
1226 "\n"
1227 " [D] = diag( param#7 , param#8 , param#9 )\n"
1228 "\n"
1229 " [ 1 0 0 ] [ 1 param#10 param#11 ]\n"
1230 " [S] = [ param#10 1 0 ] OR [ 0 1 param#12 ]\n"
1231 " [ param#11 param#12 1 ] [ 0 0 1 ]\n"
1232 "\n"
1233 "The shift vector comprises parameters #1, #2, and #3.\n"
1234 "\n"
1235 "The goal of the program is to find the warp parameters such that\n"
1236 " I([x]_warped) 'is similar to' J([x]_in)\n"
1237 "as closely as possible in some sense of 'similar', where J(x) is the\n"
1238 "base image, and I(x) is the source image.\n"
1239 "\n"
1240 "Using '-parfix', you can specify that some of these parameters\n"
1241 "are fixed. For example, '-shift_rotate_scale' is equivalent\n"
1242 "'-affine_general -parfix 10 0 -parfix 11 0 -parfix 12 0'.\n"
1243 "Don't even think of using the '-parfix' option unless you grok\n"
1244 "this example!\n"
1245 "\n"
1246 "----------- Special Note for the '-EPI' Option's Coordinates -----------\n"
1247 "In this case, the parameters above are with reference to coordinates\n"
1248 " x = frequency encoding direction (by default, first axis of dataset)\n"
1249 " y = phase encoding direction (by default, second axis of dataset)\n"
1250 " z = slice encoding direction (by default, third axis of dataset)\n"
1251 "This option lets you freeze some of the warping parameters in ways that\n"
1252 "make physical sense, considering how echo-planar images are acquired.\n"
1253 "The x- and z-scaling parameters are disabled, and shears will only affect\n"
1254 "the y-axis. Thus, there will be only 9 free parameters when '-EPI' is\n"
1255 "used. If desired, you can use a '-parang' option to allow the scaling\n"
1256 "fixed parameters to vary (put these after the '-EPI' option):\n"
1257 " -parang 7 0.833 1.20 to allow x-scaling\n"
1258 " -parang 9 0.833 1.20 to allow z-scaling\n"
1259 "You could also fix some of the other parameters, if that makes sense\n"
1260 "in your situation; for example, to disable out-of-slice rotations:\n"
1261 " -parfix 5 0 -parfix 6 0\n"
1262 "and to disable out of slice translation:\n"
1263 " -parfix 3 0\n"
1264 "NOTE WELL: If you use '-EPI', then the output warp parameters (e.g., in\n"
1265 " '-1Dparam_save') apply to the (freq,phase,slice) xyz coordinates,\n"
1266 " NOT to the DICOM xyz coordinates, so equivalent transformations\n"
1267 " will be expressed with different sets of parameters entirely\n"
1268 " than if you don't use '-EPI'! This comment does NOT apply\n"
1269 " to the output of '-1Dmatrix_save', since that matrix is\n"
1270 " defined relative to the RAI (DICOM) spatial coordinates.\n"
1271 ) ;
1272
1273 printf(
1274 "\n"
1275 "*********** CHANGING THE ORDER OF MATRIX APPLICATION ***********\n"
1276 " {{{ There is no good reason to ever use these options! }}}\n"
1277 "\n"
1278 " -SDU or -SUD }= Set the order of the matrix multiplication\n"
1279 " -DSU or -DUS }= for the affine transformations:\n"
1280 " -USD or -UDS }= S = triangular shear (params #10-12)\n"
1281 " D = diagonal scaling matrix (params #7-9)\n"
1282 " U = rotation matrix (params #4-6)\n"
1283 " Default order is '-SDU', which means that\n"
1284 " the U matrix is applied first, then the\n"
1285 " D matrix, then the S matrix.\n"
1286 "\n"
1287 " -Supper }= Set the S matrix to be upper or lower\n"
1288 " -Slower }= triangular [Default=lower triangular]\n"
1289 " NOTE: There is no '-Lunch' option.\n"
1290 " There is no '-Faster' option.\n"
1291 "\n"
1292 " -ashift OR }= Apply the shift parameters (#1-3) after OR\n"
1293 " -bshift }= before the matrix transformation. [Default=after]\n"
1294 ) ;
1295
1296 printf(
1297 "\n"
1298 " ==================================================\n"
1299 " ===== RWCox - September 2006 - Live Long and Prosper =====\n"
1300 " ==================================================\n"
1301 "\n"
1302 " ********************************************************\n"
1303 " *** From Webster's Dictionary: Allineate == 'to align' ***\n"
1304 " ********************************************************\n"
1305 ) ;
1306
1307 /*......................................................................*/
1308
1309 if( 1 ){ /* this used to be only for "-HELP" */
1310 printf(
1311 "\n"
1312 "===========================================================================\n"
1313 " FORMERLY SECRET HIDDEN OPTIONS\n"
1314 "---------------------------------------------------------------------------\n"
1315 " ** N.B.: Most of these are experimental! [permanent beta] **\n"
1316 "===========================================================================\n"
1317 "\n"
1318 " -num_rtb n = At the beginning of the fine pass, the best set of results\n"
1319 " from the coarse pass are 'refined' a little by further\n"
1320 " optimization, before the single best one is chosen for\n"
1321 " for the final fine optimization.\n"
1322 " * This option sets the maximum number of cost functional\n"
1323 " evaluations to be used (for each set of parameters)\n"
1324 " in this step.\n"
1325 " * The default is 99; a larger value will take more CPU\n"
1326 " time but may give more robust results.\n"
1327 " * If you want to skip this step entirely, use '-num_rtb 0'.\n"
1328 " then, the best of the coarse pass results is taken\n"
1329 " straight to the final optimization passes.\n"
1330 " **N.B.: If you use '-VERB', you will see that one extra case\n"
1331 " is involved in this initial fine refinement step; that\n"
1332 " case is starting with the identity transformation, which\n"
1333 " helps insure against the chance that the coarse pass\n"
1334 " optimizations ran totally amok.\n"
1335 " * MY OPINION: This option is mostly useless - but not always!\n"
1336 " * Every step in the multi-step alignment process\n"
1337 " was added at some point to solve a difficult\n"
1338 " alignment problem.\n"
1339 " * Since you usually don't know if YOUR problem\n"
1340 " is difficult, you should not reduce the default\n"
1341 " process without good reason.\n"
1342 "\n"
1343 " -nocast = By default, parameter vectors that are too close to the\n"
1344 " best one are cast out at the end of the coarse pass\n"
1345 " refinement process. Use this option if you want to keep\n"
1346 " them all for the fine resolution pass.\n"
1347 " * MY OPINION: This option is nearly useless.\n"
1348 "\n"
1349 " -norefinal = Do NOT re-start the fine iteration step after it\n"
1350 " has converged. The default is to re-start it, which\n"
1351 " usually results in a small improvement to the result\n"
1352 " (at the cost of CPU time). This re-start step is an\n"
1353 " an attempt to avoid a local minimum trap. It is usually\n"
1354 " not necessary, but sometimes helps.\n"
1355 "\n"
1356 " -realaxes = Use the 'real' axes stored in the dataset headers, if they\n"
1357 " conflict with the default axes. [For Jedi AFNI Masters only!]\n"
1358 "\n"
1359 " -savehist sss = Save start and final 2D histograms as PGM\n"
1360 " files, with prefix 'sss' (cost: cr mi nmi hel).\n"
1361 " * if filename contains 'FF', floats is written\n"
1362 " * these are the weighted histograms!\n"
1363 " * -savehist will also save histogram files when\n"
1364 " the -allcost evaluations takes place\n"
1365 " * this option is mostly useless unless '-histbin' is\n"
1366 " also used\n"
1367 " * MY OPINION: This option is mostly for debugging.\n"
1368 #if 0
1369 " -seed iii = Set random number seed (for coarse startup search)\n"
1370 " to 'iii'.\n"
1371 " [Default==7654321; if iii==0, a unique value is used]\n"
1372 #endif
1373 " -median = Smooth with median filter instead of Gaussian blur.\n"
1374 " (Somewhat slower, and not obviously useful.)\n"
1375 " * MY OPINION: This option is nearly useless.\n"
1376 "\n"
1377 " -powell m a = Set the Powell NEWUOA dimensional parameters to\n"
1378 " 'm' and 'a' (cf. source code in powell_int.c).\n"
1379 " The number of points used for approximating the\n"
1380 " cost functional is m*N+a, where N is the number\n"
1381 " of parameters being optimized. The default values\n"
1382 " are m=2 and a=3. Larger values will probably slow\n"
1383 " the program down for no good reason. The smallest\n"
1384 " allowed values are 1.\n"
1385 " * MY OPINION: This option is nearly useless.\n"
1386 "\n"
1387 " -target ttt = Same as '-source ttt'. In the earliest versions,\n"
1388 " what I now call the 'source' dataset was called the\n"
1389 " 'target' dataset:\n"
1390 " Try to remember the kind of September (2006)\n"
1391 " When life was slow and oh so mellow\n"
1392 " Try to remember the kind of September\n"
1393 " When grass was green and source was target.\n"
1394 #if 0
1395 " -targijk = Align source xyz axes with ijk indexes, rather than\n"
1396 " using coordinates in target header.\n"
1397 #endif
1398 " -Xwarp =} Change the warp/matrix setup so that only the x-, y-, or z-\n"
1399 " -Ywarp =} axis is stretched & sheared. Useful for EPI, where 'X',\n"
1400 " -Zwarp =} 'Y', or 'Z' corresponds to the phase encoding direction.\n"
1401 " -FPS fps = Generalizes -EPI to arbitrary permutation of directions.\n"
1402 "\n"
1403 " -histpow pp = By default, the number of bins in the histogram used\n"
1404 " for calculating the Hellinger, Mutual Information, and\n"
1405 " Correlation Ratio statistics is n^(1/3), where n is\n"
1406 " the number of data points. You can change that exponent\n"
1407 " to 'pp' with this option.\n"
1408 " -histbin nn = Or you can just set the number of bins directly to 'nn'.\n"
1409 " -eqbin nn = Use equalized marginal histograms with 'nn' bins.\n"
1410 " -clbin nn = Use 'nn' equal-spaced bins except for the bot and top,\n"
1411 " which will be clipped (thus the 'cl'). If nn is 0, the\n"
1412 " program will pick the number of bins for you.\n"
1413 " **N.B.: '-clbin 0' is now the default [25 Jul 2007];\n"
1414 " if you want the old all-equal-spaced bins, use\n"
1415 " '-histbin 0'.\n"
1416 " **N.B.: '-clbin' only works when the datasets are\n"
1417 " non-negative; any negative voxels in either\n"
1418 " the input or source volumes will force a switch\n"
1419 " to all equal-spaced bins.\n"
1420 " * MY OPINION: The above histogram-altering options are useless.\n"
1421 "\n"
1422 " -wtmrad mm = Set autoweight/mask median filter radius to 'mm' voxels.\n"
1423 " -wtgrad gg = Set autoweight/mask Gaussian filter radius to 'gg' voxels.\n"
1424 " -nmsetup nn = Use 'nn' points for the setup matching [default=98756]\n"
1425 " -ignout = Ignore voxels outside the warped source dataset.\n"
1426 "\n"
1427 " -blok bbb = Blok definition for the 'lp?' (Local Pearson) cost\n"
1428 " functions: 'bbb' is one of\n"
1429 " 'BALL(r)' or 'CUBE(r)' or 'RHDD(r)' or 'TOHD(r)'\n"
1430 " corresponding to\n"
1431 " spheres or cubes or rhombic dodecahedra or\n"
1432 " truncated octahedra\n"
1433 " where 'r' is the size parameter in mm.\n"
1434 #ifdef USE_OLD_BLOK_DEFAULTS
1435 " [Default is 'RHDD(6.54321)' (rhombic dodecahedron)]\n"
1436 " * Changing the 'blok' definition/radius should only be\n"
1437 " needed if the resolution of the base dataset is\n"
1438 " radically different from the common 1 mm.\n"
1439 #else
1440 " [Default is 'TOHD(r)' = truncated octahedron]\n"
1441 " [with 'radius' r chosen to include about 500]\n"
1442 " [voxels in the base dataset 3D grid. ]\n"
1443 " * Changing the 'blok' definition/radius should only be\n"
1444 " needed in unusual situations, as when you are trying\n"
1445 " to have fun fun fun.\n"
1446 " * You can change the blok shape but leave the program\n"
1447 " to set the radius, using (say) 'RHDD(0)'.\n"
1448 " * The old default blok shape/size was 'RHDD(6.54321)',\n"
1449 " so if you want to maintain backward compatibility,\n"
1450 " you should use option '-blok \"RHDD(6.54321)\"'\n"
1451 #endif
1452 " * Only voxels in the weight mask will be used\n"
1453 " inside a blok.\n"
1454 " * HISTORICAL NOTES:\n"
1455 " * CUBE, RHDD, and TOHD are space filling polyhedra.\n"
1456 " That is, they are shapes that fit together without\n"
1457 " overlaps or gaps to fill up 3D space.\n"
1458 " * To even approximately fill space, BALLs must overlap,\n"
1459 " unlike the other blok shapes. Which means that BALL\n"
1460 " bloks will use some voxels more than once.\n"
1461 " * Kepler discovered/invented the RHDD (honeybees also did).\n"
1462 " * The TOHD is the 'most compact' or 'most ball-like'\n"
1463 " of the known convex space filling polyhedra.\n"
1464 #ifndef USE_OLD_BLOK_DEFAULTS
1465 " [Which is why TOHD is the default blok shape.]\n"
1466 #endif
1467 "\n"
1468 " -PearSave sss = Save the final local Pearson correlations into a dataset\n"
1469 " *OR* with prefix 'sss'. These are the correlations from\n"
1470 " -SavePear sss which the lpc and lpa cost functionals are calculated.\n"
1471 " * The values will be between -1 and 1 in each blok.\n"
1472 " See the 'Too Much Detail' section below for how\n"
1473 " these correlations are used to compute lpc and lpa.\n"
1474 " * Locations not used in the matching will get 0.\n"
1475 " ** Unless you use '-nmatch 100%%', there will be holes\n"
1476 " of 0s in the bloks, as not all voxels are used in\n"
1477 " the matching algorithm (speedup attempt).\n"
1478 " * All the matching points in a given blok will get\n"
1479 " the same value, which makes the resulting dataset\n"
1480 " look jauntily blocky, especially in color.\n"
1481 " * This saved dataset will be on the grid of the base\n"
1482 " dataset, and may be zero padded if the program\n"
1483 " chose to do so in it wisdom. This padding means\n"
1484 " that the voxels in this output dataset may not\n"
1485 " match one-to-one with the voxels in the base\n"
1486 " dataset; however, AFNI displays things using\n"
1487 " coordinates, so overlaying this dataset on the\n"
1488 " base dataset (say) should work OK.\n"
1489 " * If you really want this saved dataset to be on the\n"
1490 " grid as the base dataset, you'll have use\n"
1491 " 3dZeropad -master {Base Dataset} ....\n"
1492 " * Option '-PearSave' works even if you don't use the\n"
1493 " 'lpc' or 'lpa' cost functionals.\n"
1494 " * If you use this option combined with '-allcostX', then\n"
1495 " the local correlations will be saved from the INITIAL\n"
1496 " alignment parameters, rather than from the FINAL\n"
1497 " optimized parameters.\n"
1498 " (Of course, with '-allcostX', there IS no final result.)\n"
1499 " * This option does NOT work with '-allcost' or '-allcostX1D'.\n"
1500 "\n"
1501 " -allcost = Compute ALL available cost functionals and print them\n"
1502 " at various points in the optimization progress.\n"
1503 " -allcostX = Compute and print ALL available cost functionals for the\n"
1504 " un-warped inputs, and then quit.\n"
1505 " * This option is for testing purposes (AKA 'fun').\n"
1506 " -allcostX1D p q = Compute ALL available cost functionals for the set of\n"
1507 " parameters given in the 1D file 'p' (12 values per row),\n"
1508 " write them to the 1D file 'q', then exit. (For you, Zman)\n"
1509 " * N.B.: If -fineblur is used, that amount of smoothing\n"
1510 " will be applied prior to the -allcostX evaluations.\n"
1511 " The parameters are the rotation, shift, scale,\n"
1512 " and shear values, not the affine transformation\n"
1513 " matrix. An identity matrix could be provided as\n"
1514 " \"0 0 0 0 0 0 1 1 1 0 0 0\" for instance or by\n"
1515 " using the word \"IDENTITY\"\n"
1516 " * This option is for testing purposes (even more 'fun').\n"
1517 ) ;
1518
1519 printf("\n"
1520 "===========================================================================\n" );
1521 printf("\n"
1522 "Too Much Detail -- How Local Pearson Correlations Are Computed and Used\n"
1523 "-----------------------------------------------------------------------\n"
1524 " * The automask region of the base dataset is divided into a discrete\n"
1525 " set of 'bloks'. Usually there are several thousand bloks.\n"
1526 " * In each blok, the voxel values from the base and the source (after\n"
1527 " the alignment transformation is applied) are extracted and the\n"
1528 " correlation coefficient is computed -- either weighted or unweighted,\n"
1529 " depending on the options used in 3dAllineate (usually weighted).\n"
1530 " * Let p[i] = correlation coefficient in blok #i,\n"
1531 " w[i] = sum of weights used in blok #i, or = 1 if unweighted.\n"
1532 "** The values of p[i] are what get output via the '-PearSave' option.\n"
1533 " * Define pc[i] = arctanh(p[i]) = 0.5 * log( (1+p[i]) / (1-p[i]) )\n"
1534 " This expression is designed to 'stretch' out larger correlations,\n"
1535 " giving them more emphasis in psum below. The same reasoning\n"
1536 " is why pc[i]*abs(pc[i]) is used below, to make bigger correlations\n"
1537 " have a bigger impact in the final result.\n"
1538 " * psum = SUM_OVER_i { w[i]*pc[i]*abs(pc[i]) }\n"
1539 " wsum = SUM_OVER_i { w[i] }\n"
1540 " lpc = psum / wsum ==> negative correlations are good (smaller lpc)\n"
1541 " lpa = 1 - abs(lpc) ==> positive correlations are good (smaller lpa)\n"
1542 ) ;
1543
1544 printf("\n"
1545 "===========================================================================\n" );
1546 printf("\n"
1547 "Modifying '-final wsinc5' -- for the truly crazy people out there\n"
1548 "-----------------------------------------------------------------\n"
1549 " * The windowed (tapered) sinc function interpolation can be modified\n"
1550 " by several environment variables. This is expert-level stuff, and\n"
1551 " you should understand what you are doing if you use these options.\n"
1552 " The simplest way to use these would be on the command line, as in\n"
1553 " -DAFNI_WSINC5_RADIUS=9 -DAFNI_WSINC5_TAPERFUN=Hamming\n"
1554 "\n"
1555 " * AFNI_WSINC5_TAPERFUN lets you choose the taper function.\n"
1556 " The default taper function is the minimum sidelobe 3-term cosine:\n"
1557 " 0.4243801 + 0.4973406*cos(PI*x) + 0.0782793*cos(2*PI*x)\n"
1558 " If you set this environment variable to 'Hamming', then the\n"
1559 " minimum sidelobe 2-term cosine will be used instead:\n"
1560 " 0.53836 + 0.46164*cos(PI*x)\n"
1561 " Here, 'x' is between 0 and 1, where x=0 is the center of the\n"
1562 " interpolation mask and x=1 is the outer edge.\n"
1563 " ++ Unfortunately, the 3-term cosine doesn't have a catchy name; you can\n"
1564 " find it (and many other) taper functions described in the paper\n"
1565 " AH Nuttall, Some Windows with Very Good Sidelobe Behavior.\n"
1566 " IEEE Trans. ASSP, 29:84-91 (1981).\n"
1567 " In particular, see Fig.14 and Eq.36 in this paper.\n"
1568 "\n"
1569 " * AFNI_WSINC5_TAPERCUT lets you choose the start 'x' point for tapering:\n"
1570 " This value should be between 0 and 0.8; for example, 0 means to taper\n"
1571 " all the way from x=0 to x=1 (maximum tapering). The default value\n"
1572 " is 0. Setting TAPERCUT to 0.5 (say) means only to taper from x=0.5\n"
1573 " to x=1; thus, a larger value means that fewer points are tapered\n"
1574 " inside the interpolation mask.\n"
1575 "\n"
1576 " * AFNI_WSINC5_RADIUS lets you choose the radius of the tapering window\n"
1577 " (i.e., the interpolation mask region). This value is an integer\n"
1578 " between 3 and 21. The default value is 5 (which used to be the\n"
1579 " ONLY value, thus 'wsinc5'). RADIUS is measured in voxels, not mm.\n"
1580 "\n"
1581 " * AFNI_WSINC5_SPHERICAL lets you choose the shape of the mask region.\n"
1582 " If you set this value to 'Yes', then the interpolation mask will be\n"
1583 " spherical; otherwise, it defaults to cubical.\n"
1584 "\n"
1585 " * The Hamming taper function is a little faster than the 3-term function,\n"
1586 " but will have a little more Gibbs phenomenon.\n"
1587 " * A larger TAPERCUT will give a little more Gibbs phenomenon; compute\n"
1588 " speed won't change much with this parameter.\n"
1589 " * Compute time goes up with (at least) the 3rd power of the RADIUS; setting\n"
1590 " RADIUS to 21 will be VERY slow.\n"
1591 " * Visually, RADIUS=3 is similar to quintic interpolation. Increasing\n"
1592 " RADIUS makes the interpolated images look sharper and more well-\n"
1593 " defined. However, values of RADIUS greater than or equal to 7 appear\n"
1594 " (to Zhark's eagle eye) to be almost identical. If you really care,\n"
1595 " you'll have to experiment with this parameter yourself.\n"
1596 " * A spherical mask is also VERY slow, since the cubical mask allows\n"
1597 " evaluation as a tensor product. There is really no good reason\n"
1598 " to use a spherical mask; I only put it in for fun/experimental purposes.\n"
1599 "** For most users, there is NO reason to ever use these environment variables\n"
1600 " to modify wsinc5. You should only do this kind of thing if you have a\n"
1601 " good and articulable reason! (Or if you really like to screw around.)\n"
1602 "** The wsinc5 interpolation function is parallelized using OpenMP, which\n"
1603 " makes its usage moderately tolerable.\n"
1604 #ifndef USE_OMP
1605 " ++ However, this binary copy of AFNI is NOT compiled with OpenMP support.\n"
1606 " You should consider getting such binaries, as several AFNI program\n"
1607 " (including this one) will become significantly faster.\n"
1608 #endif
1609 ) ;
1610
1611 printf("\n"
1612 "===========================================================================\n" );
1613 printf("\n"
1614 "Hidden experimental cost functionals:\n"
1615 "-------------------------------------\n" ) ;
1616 for( ii=0 ; ii < NMETH ; ii++ )
1617 if( !meth_visible[ii] )
1618 printf( " %-4s *OR* %-16s= %s\n" ,
1619 meth_shortname[ii] , meth_longname[ii] , meth_username[ii] );
1620
1621 printf("\n"
1622 "Notes for the new [Feb 2010] lpc+ cost functional:\n"
1623 "--------------------------------------------------\n"
1624 " * The cost functional named 'lpc+' is a combination of several others:\n"
1625 " lpc + hel*%.1f + crA*%.1f + nmi*%.1f + mi*%.1f + ov*%.1f\n"
1626 " ++ 'hel', 'crA', 'nmi', and 'mi' are the histogram-based cost\n"
1627 " functionals also available as standalone options.\n"
1628 " ++ 'ov' is a measure of the overlap of the automasks of the base and\n"
1629 " source volumes; ov is not available as a standalone option.\n"
1630 "\n"
1631 " * The purpose of lpc+ is to avoid situations where the pure lpc cost\n"
1632 " goes wild; this especially happens if '-source_automask' isn't used.\n"
1633 " ++ Even with lpc+, you should use '-source_automask+2' (say) to be safe.\n"
1634 "\n"
1635 " * You can alter the weighting of the extra functionals by giving the\n"
1636 " option in the form (for example)\n"
1637 " '-lpc+hel*0.5+nmi*0+mi*0+crA*1.0+ov*0.5'\n"
1638 "\n"
1639 " * The quotes are needed to prevent the shell from wild-card expanding\n"
1640 " the '*' character.\n"
1641 " --> You can now use ':' in place of '*' to avoid this wildcard problem:\n"
1642 " -lpc+hel:0.5+nmi:0+mi:0+crA:1+ov:0.5+ZZ\n"
1643 "\n"
1644 " * Notice the weight factors FOLLOW the name of the extra functionals.\n"
1645 " ++ If you want a weight to be 0 or 1, you have to provide for that\n"
1646 " explicitly -- if you leave a weight off, then it will get its\n"
1647 " default value!\n"
1648 " ++ The order of the weight factor names is unimportant here:\n"
1649 " '-lpc+hel*0.5+nmi*0.8' == '-lpc+nmi*0.8+hel*0.5'\n"
1650 "\n"
1651 " * Only the 5 functionals listed (hel,crA,nmi,mi,ov) can be used in '-lpc+'.\n"
1652 "\n"
1653 " * In addition, if you want the initial alignments to be with '-lpc+' and\n"
1654 " then finish the Final alignment with pure '-lpc', you can indicate this\n"
1655 " by putting 'ZZ' somewhere in the option string, as in '-lpc+ZZ'.\n"
1656 " ***** '-cost lpc+ZZ' is very useful for aligning EPI to T1w volumes *****\n"
1657 "\n"
1658 " * [28 Nov 2018]\n"
1659 " All of the above now applies to the 'lpa+' cost functional,\n"
1660 " which can be used as a robust method for like-to-like alignment.\n"
1661 " For example, aligning 3T and 7T T1-weighted datasets from the same person.\n"
1662 " * [28 Sep 2021]\n"
1663 " However, the default multiplier constants for cost 'lpa+' are now\n"
1664 " different from the 'lpc+' multipliers -- to make 'lpa+' more\n"
1665 " robust. The new default for 'lpa+' is\n"
1666 " lpa + hel*%.1f + crA*%.1f + nmi*%.1f + mi*%.1f + ov*%.1f\n"
1667 " ***** '-cost lpa+ZZ' is very useful for T1w to T1w volumes (or any *****\n"
1668 " ***** similar-contrast datasets). *****\n"
1669 "\n"
1670 " *** Note that in trial runs, we have found that lpc+ZZ and lpa+ZZ are ***\n"
1671 " *** more robust than lpc+ and lpa+ -- which is why the '+ZZ' amendment ***\n"
1672 " *** was created. ***\n"
1673 , DEFAULT_MICHO_LPC_HEL, DEFAULT_MICHO_LPC_CRA, DEFAULT_MICHO_LPC_NMI, DEFAULT_MICHO_LPC_MI, DEFAULT_MICHO_LPC_OV
1674 , DEFAULT_MICHO_LPA_HEL, DEFAULT_MICHO_LPA_CRA, DEFAULT_MICHO_LPA_NMI, DEFAULT_MICHO_LPA_MI, DEFAULT_MICHO_LPA_OV
1675 ) ;
1676
1677 printf("\n"
1678 "Cost functional descriptions (for use with -allcost output):\n"
1679 "------------------------------------------------------------\n"
1680 ) ;
1681 for( ii=0 ; ii < NMETH ; ii++ )
1682 printf(" %-4s:: %s\n",
1683 meth_shortname[ii] , meth_costfunctional[ii] ) ;
1684
1685 printf("\n") ;
1686 printf(" * N.B.: Some cost functional values (as printed out above)\n"
1687 " are negated from their theoretical descriptions (e.g., 'hel')\n"
1688 " so that the best image alignment will be found when the cost\n"
1689 " is minimized. See the descriptions above and the references\n"
1690 " below for more details for each functional.\n");
1691 printf("\n") ;
1692 printf(" * MY OPINIONS:\n"
1693 " * Some of these cost functionals were implemented only for\n"
1694 " the purposes of fun and/or comparison and/or experimentation\n"
1695 " and/or special circumstances. These are\n"
1696 " sp je lss crM crA crM hel mi nmi\n"
1697 " * For many purposes, lpc+ZZ and lpa+ZZ are the most robust\n"
1698 " cost functionals, but usually the slowest to evaluate.\n"
1699 " * HOWEVER, just because some method is best MOST of the\n"
1700 " time does not mean it is best ALL of the time.\n"
1701 " Please check your results visually, or at some point\n"
1702 " in time you will have bad results and not know it!\n"
1703 " * For speed and for 'like-to-like' alignment, '-cost ls'\n"
1704 " can work well.\n") ;
1705 printf("\n") ;
1706 printf(" * For more information about the 'lpc' functional, see\n"
1707 " ZS Saad, DR Glen, G Chen, MS Beauchamp, R Desai, RW Cox.\n"
1708 " A new method for improving functional-to-structural\n"
1709 " MRI alignment using local Pearson correlation.\n"
1710 " NeuroImage 44: 839-848, 2009.\n"
1711 " http://dx.doi.org/10.1016/j.neuroimage.2008.09.037\n"
1712 " https://pubmed.ncbi.nlm.nih.gov/18976717\n"
1713 " The '-blok' option can be used to control the regions\n"
1714 " (size and shape) used to compute the local correlations.\n");
1715 printf(" *** Using the 'lpc' functional wisely requires the use of\n"
1716 " a proper weight volume. We HIGHLY recommend you use\n"
1717 " the align_epi_anat.py script if you want to use this\n"
1718 " cost functional! Otherwise, you are likely to get\n"
1719 " less than optimal results (and then swear at us unjustly).\n") ;
1720 printf("\n") ;
1721 printf(" * For more information about the 'cr' functionals, see\n"
1722 " http://en.wikipedia.org/wiki/Correlation_ratio\n"
1723 " Note that CR(x,y) is not the same as CR(y,x), which\n"
1724 " is why there are symmetrized versions of it available.\n") ;
1725 printf("\n") ;
1726 printf(" * For more information about the 'mi', 'nmi', and 'je'\n"
1727 " cost functionals, see\n"
1728 " http://en.wikipedia.org/wiki/Mutual_information\n"
1729 " http://en.wikipedia.org/wiki/Joint_entropy\n"
1730 " http://www.cs.jhu.edu/~cis/cista/746/papers/mutual_info_survey.pdf\n");
1731 printf("\n") ;
1732 printf(" * For more information about the 'hel' functional, see\n"
1733 " http://en.wikipedia.org/wiki/Hellinger_distance\n" ) ;
1734 printf("\n") ;
1735 printf(" * Some cost functionals (e.g., 'mi', 'cr', 'hel') are\n"
1736 " computed by creating a 2D joint histogram of the\n"
1737 " base and source image pair. Various options above\n"
1738 " (e.g., '-histbin', etc.) can be used to control the\n"
1739 " number of bins used in the histogram on each axis.\n"
1740 " (If you care to control the program in such detail!)\n" ) ;
1741 printf("\n") ;
1742 printf(" * Minimization of the chosen cost functional is done via\n"
1743 " the NEWUOA software, described in detail in\n"
1744 " MJD Powell. 'The NEWUOA software for unconstrained\n"
1745 " optimization without derivatives.' In: GD Pillo,\n"
1746 " M Roma (Eds), Large-Scale Nonlinear Optimization.\n"
1747 " Springer, 2006.\n"
1748 " http://www.damtp.cam.ac.uk/user/na/NA_papers/NA2004_08.pdf\n");
1749
1750 printf("\n"
1751 "===========================================================================\n"
1752 "\n"
1753 "SUMMARY of the Default Allineation Process\n"
1754 "------------------------------------------\n"
1755 "As mentioned earlier, each of these steps was added to deal with a problem\n"
1756 " that came up over the years. The resulting process is reasonably robust :),\n"
1757 " but then tends to be slow :(. If you use the '-verb' or '-VERB' option, you\n"
1758 " will get a lot of fun fun fun progress messages that show the results from\n"
1759 " this sequence of steps.\n"
1760 "\n"
1761 "Below, I refer to different scales of effort in the optimizations at each\n"
1762 " step. Easier/faster optimization is done using: matching with fewer points\n"
1763 " from the datasets; more smoothing of the base and source datasets; and by\n"
1764 " putting a smaller upper limit on the number of trials the optimizer is\n"
1765 " allowed to take. The Coarse phase starts with the easiest optimization,\n"
1766 " and increases the difficulty a little at each refinement. The Fine phase\n"
1767 " starts with the most difficult optimization setup: the most points for\n"
1768 " matching, little or no smoothing, and a large limit on the number of\n"
1769 " optimizer trials.\n"
1770 "\n"
1771 " 0. Preliminary Setup [Goal: create the basis for the following steps]\n"
1772 " a. Create the automask and/or autoweight from the '-base' dataset.\n"
1773 " The cost functional will only be computed from voxels inside the\n"
1774 " automask, and only a fraction of those voxels will actually be used\n"
1775 " for evaluating the cost functional (unless '-nmatch 100%%' is used).\n"
1776 " b. If the automask is 'too close' to the outside of the base 3D volume,\n"
1777 " zeropad the base dataset to avoid edge effects.\n"
1778 " c. Determine the 3D (x,y,z) shifts for the '-cmass' center-of-mass\n"
1779 " crude alignment, if ordered by the user.\n"
1780 " d. Set ranges of transformation parameters and which parameters are to\n"
1781 " be frozen at fixed values.\n"
1782 "\n"
1783 " 1. Coarse Phase [Goal: explore the vastness of 6-12D parameter space]\n"
1784 " a. The first step uses only the first 6 parameters (shifts + rotations),\n"
1785 " and evaluates thousands of potential starting points -- selected from\n"
1786 " a 6D grid in parameter space and also from random points in 6D\n"
1787 " parameter space. This step is fairly slow. The best 45 parameter\n"
1788 " sets (in the sense of the cost functional) are kept for the next step.\n"
1789 " b. Still using only the first 6 parameters, the best 45 sets of parameters\n"
1790 " undergo a little optimization. The best 6 parameter sets after this\n"
1791 " refinement are kept for the next step. (The number of sets chosen\n"
1792 " to go on to the next step can be set by the '-twobest' option.)\n"
1793 " The optimizations in this step use the blurring radius that is\n"
1794 " given by option '-twoblur', which defaults to 7.77 mm, and use\n"
1795 " relatively few points in each dataset for computing the cost functional.\n"
1796 " c. These 6 best parameter sets undergo further, more costly, optimization,\n"
1797 " now using all 12 parameters. This optimization runs in 3 passes, each\n"
1798 " more costly (less smoothing, more matching points) than the previous.\n"
1799 " (If 2 sets get too close in parameter space, 1 of them will be cast out\n"
1800 " -- this does not happen often.) Output parameter sets from the 3rd pass\n"
1801 " of successive refinement are inputs to the fine refinement phase.\n"
1802 "\n"
1803 " 2. Fine Phase [Goal: use more expensive optimization on good starting points]\n"
1804 " a. The 6 outputs from step 1c have the null parameter set (all 0, except\n"
1805 " for the '-cmass' shifts) appended. Then a small amount of optimization\n"
1806 " is applied to each of these 7 parameter sets ('-num_rtb'). The null\n"
1807 " parameter set is added here to insure against the possibility that the\n"
1808 " coarse optimizations 'ran away' to some unpleasant locations in the 12D\n"
1809 " parameter space. These optimizations use the full set of points specified\n"
1810 " by '-nmatch', and the smoothing specified by '-fineblur' (default = 0),\n"
1811 " but the number of functional evaluations is small, to make this step fast.\n"
1812 " b. The best (smallest cost) set from step 2a is chosen for the final\n"
1813 " optimization, which is run until the '-conv' limit is reached.\n"
1814 " These are the 'Finalish' parameters (shown using '-verb').\n"
1815 " c. The set of parameters from step 2b is used as the starting point\n"
1816 " for a new optimization, in an attempt to avoid a false minimum.\n"
1817 " The results of this optimization are the final parameter set.\n"
1818 "\n"
1819 " 3. The final set of parameters is used to produce the output volume,\n"
1820 " using the '-final' interpolation method.\n"
1821 "\n"
1822 "In practice, the output from the Coarse phase successive refinements is\n"
1823 "usually so good that the Fine phase runs quickly and makes only small\n"
1824 "adjustments. The quality resulting from the Coarse phase steps is mostly\n"
1825 "due, in my opinion, to the large number of initial trials (1ab), followed by\n"
1826 "by the successive refinements of several parameter sets (1c) to help usher\n"
1827 "'good' candidates to the starting line for the Fine phase.\n"
1828 "\n"
1829 "For some 'easy' registration problems -- such as T1w-to-T1w alignment, high\n"
1830 "quality images, a lot of overlap to start with -- the process can be sped\n"
1831 "up by reducing the number of steps. For example, '-num_rtb 0 -twobest 0'\n"
1832 "would eliminate step 2a and speed up step 1c. Even more extreme, '-onepass'\n"
1833 "could be used to skip all of the Coarse phase. But be careful out there!\n"
1834 "\n"
1835 "For 'hard' registration problems, cleverness is usually needed. Choice\n"
1836 "of cost functional matters. Preprocessing the datasets may be necessary.\n"
1837 "Using '-twobest %d' could help by providing more candidates for the\n"
1838 "Fine phase -- at the cost of CPU time. If you run into trouble -- which\n"
1839 "happens sooner or later -- try the AFNI Message Board -- and please\n"
1840 "give details, including the exact command line(s) you used.\n"
1841 , PARAM_MAXTRIAL
1842 ) ;
1843
1844 #if 0 /* No longer show help for -nwarp [29 Jan 2021] */
1845 printf("\n"
1846 "===========================================================================\n"
1847 "\n"
1848 " -nwarp type = Experimental nonlinear warping:\n"
1849 "\n"
1850 " ***** Note that these '-nwarp' options are superseded *****\n"
1851 " ***** by the AFNI program 3dQwarp, which does a more *****\n"
1852 " ***** accurate and more better job of nonlinear warping *****\n"
1853 " ********* I strongly recommend against using -nwarp!!! *********\n"
1854 " ********* [And I will not support or help you with it.] *********\n"
1855 " ******* ------ Zhark the Warper ------ July 2013 -------- *******\n"
1856 "\n"
1857 " * At present, the only 'type' is 'bilinear',\n"
1858 " as in 3dWarpDrive, with 39 parameters.\n"
1859 " * I plan to implement more complicated nonlinear\n"
1860 " warps in the future, someday .... [HAH!]\n"
1861 " * -nwarp can only be applied to a source dataset\n"
1862 " that has a single sub-brick!\n"
1863 " * -1Dparam_save and -1Dparam_apply work with\n"
1864 " bilinear warps; see the Notes for more information.\n"
1865 " ==>>*** Nov 2010: I have now added the following polynomial\n"
1866 " warps: 'cubic', 'quintic', 'heptic', 'nonic' (using\n"
1867 " 3rd, 5th, 7th, and 9th order Legendre polynomials); e.g.,\n"
1868 " -nwarp heptic\n"
1869 " * These are the nonlinear warps that I now am supporting.\n"
1870 " * Or you can call them 'poly3', 'poly5', 'poly7', and 'poly9',\n"
1871 " for simplicity and non-Hellenistic clarity.\n"
1872 " * These names are not case sensitive: 'nonic' == 'Nonic', etc.\n"
1873 " * Higher and higher order polynomials will take longer and longer\n"
1874 " to run!\n"
1875 " * If you wish to apply a nonlinear warp, you have to supply\n"
1876 " a parameter file with -1Dparam_apply and also specify the\n"
1877 " warp type with -nwarp. The number of parameters in the\n"
1878 " file (per line) must match the warp type:\n"
1879 " bilinear = 43 [for all nonlinear warps, the final]\n"
1880 " cubic = 64 [4 'parameters' are fixed values to]\n"
1881 " quintic = 172 [normalize the coordinates to -1..1]\n"
1882 " heptic = 364 [for the nonlinear warp functions. ]\n"
1883 " nonic = 664\n"
1884 " In all these cases, the first 12 parameters are the\n"
1885 " affine parameters (shifts, rotations, etc.), and the\n"
1886 " remaining parameters define the nonlinear part of the warp\n"
1887 " (polynomial coefficients); thus, the number of nonlinear\n"
1888 " parameters over which the optimization takes place is\n"
1889 " the number in the table above minus 16.\n"
1890 " * The actual polynomial functions used are products of\n"
1891 " Legendre polynomials, but the symbolic names used in\n"
1892 " the header line in the '-1Dparam_save' output just\n"
1893 " express the polynomial degree involved; for example,\n"
1894 " quint:x^2*z^3:z\n"
1895 " is the name given to the polynomial warp basis function\n"
1896 " whose highest power of x is 2, is independent of y, and\n"
1897 " whose highest power of z is 3; the 'quint' indicates that\n"
1898 " this was used in '-nwarp quintic'; the final ':z' signifies\n"
1899 " that this function was for deformations in the (DICOM)\n"
1900 " z-direction (+z == Superior).\n"
1901 " ==>>*** You can further control the form of the polynomial warps\n"
1902 " (but not the bilinear warp!) by restricting their degrees\n"
1903 " of freedom in 2 different ways.\n"
1904 " ++ You can remove the freedom to have the nonlinear\n"
1905 " deformation move along the DICOM x, y, and/or z axes.\n"
1906 " ++ You can remove the dependence of the nonlinear\n"
1907 " deformation on the DICOM x, y, and/or z coordinates.\n"
1908 " ++ To illustrate with the six second order polynomials:\n"
1909 " p2_xx(x,y,z) = x*x p2_xy(x,y,z) = x*y\n"
1910 " p2_xz(x,y,z) = x*z p2_yy(x,y,z) = y*y\n"
1911 " p2_yz(x,y,z) = y*z p2_zz(x,y,z) = z*z\n"
1912 " Unrestricted, there are 18 parameters associated with\n"
1913 " these polynomials, one for each direction of motion (x,y,z)\n"
1914 " * If you remove the freedom of the nonlinear warp to move\n"
1915 " data in the z-direction (say), then there would be 12\n"
1916 " parameters left.\n"
1917 " * If you instead remove the freedom of the nonlinear warp\n"
1918 " to depend on the z-coordinate, you would be left with\n"
1919 " 3 basis functions (p2_xz, p2_yz, and p2_zz would be\n"
1920 " eliminated), each of which would have x-motion, y-motion,\n"
1921 " and z-motion parameters, so there would be 9 parameters.\n"
1922 " ++ To fix motion along the x-direction, use the option\n"
1923 " '-nwarp_fixmotX' (and '-nwarp_fixmotY' and '-nwarp_fixmotZ).\n"
1924 " ++ To fix dependence of the polynomial warp on the x-coordinate,\n"
1925 " use the option '-nwarp_fixdepX' (et cetera).\n"
1926 " ++ These coordinate labels in the options (X Y Z) refer to the\n"
1927 " DICOM directions (X=R-L, Y=A-P, Z=I-S). If you would rather\n"
1928 " fix things along the dataset storage axes, you can use\n"
1929 " the symbols I J K to indicate the fastest to slowest varying\n"
1930 " array dimensions (e.g., '-nwarp_fixdepK').\n"
1931 " * Mixing up the X Y Z and I J K forms of parameter freezing\n"
1932 " (e.g., '-nwarp_fixmotX -nwarp_fixmotJ') may cause trouble!\n"
1933 " ++ If you input a 2D dataset (a single slice) to be registered\n"
1934 " with '-nwarp', the program automatically assumes '-nwarp_fixmotK'\n"
1935 " and '-nwarp_fixdepK' so there are no out-of-plane parameters\n"
1936 " or dependence. The number of nonlinear parameters is then:\n"
1937 " 2D: cubic = 14 ; quintic = 36 ; heptic = 66 ; nonic = 104.\n"
1938 " 3D: cubic = 48 ; quintic = 156 ; heptic = 348 ; nonic = 648.\n"
1939 " [ n-th order: 2D = (n+4)*(n-1) ; 3D = (n*n+7*n+18)*(n-1)/2 ]\n"
1940 " ++ Note that these '-nwarp_fix' options have no effect on the\n"
1941 " affine part of the warp -- if you want to constrain that as\n"
1942 " well, you'll have to use the '-parfix' option.\n"
1943 " * However, for 2D images, the affine part will automatically\n"
1944 " be restricted to in-plane (6 parameter) 'motions'.\n"
1945 " ++ If you save the warp parameters (with '-1Dparam_save') when\n"
1946 " doing 2D registration, all the parameters will be saved, even\n"
1947 " the large number of them that are fixed to zero. You can use\n"
1948 " '1dcat -nonfixed' to remove these columns from the 1D file if\n"
1949 " you want to further process the varying parameters (e.g., 1dsvd).\n"
1950 " **++ The mapping from I J K to X Y Z (DICOM coordinates), where the\n"
1951 " '-nwarp_fix' constraints are actually applied, is very simple:\n"
1952 " given the command to fix K (say), the coordinate X, or Y, or Z\n"
1953 " whose direction most closely aligns with the dataset K grid\n"
1954 " direction is chosen. Thus, for coronal images, K is in the A-P\n"
1955 " direction, so '-nwarp_fixmotK' is translated to '-nwarp_fixmotY'.\n"
1956 " * This simplicity means that using the '-nwarp_fix' commands on\n"
1957 " oblique datasets is problematic. Perhaps it would work in\n"
1958 " combination with the '-EPI' option, but that has not been tested.\n"
1959 "\n"
1960 "-nwarp NOTES:\n"
1961 "-------------\n"
1962 "* -nwarp is slow - reeeaaallll slow - use it with OpenMP!\n"
1963 "* Check the results to make sure the optimizer didn't run amok!\n"
1964 " (You should ALWAYS do this with any registration software.)\n"
1965 "* For the nonlinear warps, the largest coefficient allowed is\n"
1966 " set to 0.10 by default. If you wish to change this, use an\n"
1967 " option like '-nwarp_parmax 0.05' (to make the allowable amount\n"
1968 " of nonlinear deformation half the default).\n"
1969 " ++ N.B.: Increasing the maximum past 0.10 may give very bad results!!\n"
1970 "* If you use -1Dparam_save, then you can apply the nonlinear\n"
1971 " warp to another dataset using -1Dparam_apply in a later\n"
1972 " 3dAllineate run. To do so, use '-nwarp xxx' in both runs\n"
1973 " , so that the program knows what the extra parameters in\n"
1974 " the file are to be used for.\n"
1975 " ++ Bilinear: 43 values are saved in 1 row of the param file.\n"
1976 " ++ The first 12 are the affine parameters\n"
1977 " ++ The next 27 are the D1,D2,D3 matrix parameters (cf. infra).\n"
1978 " ++ The final 'extra' 4 values are used to specify\n"
1979 " the center of coordinates (vector Xc below), and a\n"
1980 " pre-computed scaling factor applied to parameters #13..39.\n"
1981 " ++ For polynomial warps, a similar format is used (mutatis mutandis).\n"
1982 "* The option '-nwarp_save sss' lets you save a 3D dataset of the\n"
1983 " the displacement field used to create the output dataset. This\n"
1984 " dataset can be used in program 3dNwarpApply to warp other datasets.\n"
1985 " ++ If the warp is symbolized by x -> w(x) [here, x is a DICOM 3-vector],\n"
1986 " then the '-nwarp_save' dataset contains w(x)-x; that is, it contains\n"
1987 " the warp displacement of each grid point from its grid location.\n"
1988 "\n"
1989 "* Bilinear warp formula:\n"
1990 " Xout = inv[ I + {D1 (Xin-Xc) | D2 (Xin-Xc) | D3 (Xin-Xc)} ] [ A Xin ]\n"
1991 " where Xin = input vector (base dataset coordinates)\n"
1992 " Xout = output vector (source dataset coordinates)\n"
1993 " Xc = center of coordinates used for nonlinearity\n"
1994 " (will be the center of the base dataset volume)\n"
1995 " A = matrix representing affine transformation (12 params)\n"
1996 " I = 3x3 identity matrix\n"
1997 " D1,D2,D3 = three 3x3 matrices (the 27 'new' parameters)\n"
1998 " * when all 27 parameters == 0, warp is purely affine\n"
1999 " {P|Q|R} = 3x3 matrix formed by adjoining the 3-vectors P,Q,R\n"
2000 " inv[...] = inverse 3x3 matrix of stuff inside '[...]'\n"
2001 "* The inverse of a bilinear transformation is another bilinear\n"
2002 " transformation. Someday, I may write a program that will let\n"
2003 " you compute that inverse transformation, so you can use it for\n"
2004 " some cunning and devious purpose.\n"
2005 "* If you expand the inv[...] part of the above formula in a 1st\n"
2006 " order Taylor series, you'll see that a bilinear warp is basically\n"
2007 " a quadratic warp, with the additional feature that its inverse\n"
2008 " is directly computable (unlike a pure quadratic warp).\n"
2009 "* 'bilinearD' means the matrices D1, D2, and D3 with be constrained\n"
2010 " to be diagonal (a total of 9 nonzero values), rather than full\n"
2011 " (a total of 27 nonzero values). This option is much faster.\n"
2012 "* Is '-nwarp bilinear' useful? Try it and tell me!\n"
2013 "* Unlike a bilinear warp, the polynomial warps cannot be exactly\n"
2014 " inverted. At some point, I'll write a program to compute an\n"
2015 " approximate inverse, if there is enough clamor for such a toy.\n"
2016 "\n"
2017 "===========================================================================\n"
2018 ) ;
2019 #endif
2020
2021 } else { /* now unreachable */
2022
2023 printf(
2024 "\n"
2025 " [[[ To see a plethora of advanced/experimental options, use '-HELP'. ]]]\n");
2026
2027 }
2028
2029 PRINT_AFNI_OMP_USAGE("3dAllineate",
2030 "* OpenMP may or may not speed up the program significantly. Limited\n"
2031 " tests show that it provides some benefit, particularly when using\n"
2032 " the more complicated interpolation methods (e.g., '-cubic' and/or\n"
2033 " '-final wsinc5'), for up to 3-4 CPU threads.\n"
2034 "* But the speedup is definitely not linear in the number of threads, alas.\n"
2035 " Probably because my parallelization efforts were pretty limited.\n"
2036 ) ;
2037
2038 PRINT_COMPILE_DATE ; exit(0);
2039 }
2040
2041 /*---------------------------------------------------------------------------*/
2042 /*============================ Ye Olde Main Programme =======================*/
2043 /* Note the GA_setup struct 'stup' below.
2044 This struct contains all the alignment setup information,
2045 and is passed to functions in mri_genalign.c to do lots of things,
2046 like evaluate the cost functional, optimize the cost, and so forth. */
2047 /*---------------------------------------------------------------------------*/
2048
main(int argc,char * argv[])2049 int main( int argc , char *argv[] )
2050 {
2051 GA_setup stup ; /* holds all the setup info */
2052 THD_3dim_dataset *dset_out=NULL ;
2053 MRI_IMAGE *im_base, *im_targ, *im_weig=NULL, *im_mask=NULL, *qim ;
2054 MRI_IMAGE *im_bset, *im_wset, *im_tmask=NULL ;
2055 int iarg , ii,jj,kk , nmask=0 , nfunc , rr , ntask , ntmask=0 , nnz ;
2056 int nx_base,ny_base,nz_base , nx_targ,ny_targ,nz_targ , nxy_base ;
2057 float dx_base,dy_base,dz_base , dx_targ,dy_targ,dz_targ , dxyz_top ;
2058 int nxyz_base[3] , nxyz_targ[3] , nxyz_dout[3] ;
2059 float dxyz_base[3] , dxyz_targ[3] , dxyz_dout[3] ;
2060 int nvox_base ;
2061 float v1,v2 , xxx_p,yyy_p,zzz_p,siz , xxx_m,yyy_m,zzz_m , xxx,yyy,zzz , xc,yc,zc ;
2062 float xxc,yyc,zzc ; int CMbad=0 ; /* 26 Feb 2020 */
2063 int pad_xm=0,pad_xp=0 , pad_ym=0,pad_yp=0 , pad_zm=0,pad_zp=0 ;
2064 int tfdone=0; /* stuff for -twofirst */
2065 float tfparm[PARAM_MAXTRIAL+2][MAXPAR]; /* +2 for some extra cases */
2066 float ffparm[PARAM_MAXTRIAL+2][MAXPAR]; /* not really used yet */
2067 float tfcost[PARAM_MAXTRIAL+2] ;
2068 int tfindx[PARAM_MAXTRIAL+2] ;
2069 int tfiorg[PARAM_MAXTRIAL+2] , ffiorg[PARAM_MAXTRIAL+2] ; /* 24 Jun 2021 */
2070 int tfi2bs[PARAM_MAXTRIAL+2] , ffi2bs[PARAM_MAXTRIAL+2] ;
2071 int skip_first=0 , didtwo , targ_kind, skipped=0 , nptwo=6 ;
2072 int targ_was_vector=0, targ_vector_kind=-1 ; MRI_IMAGE *im_targ_vector=NULL ;
2073 double ctim=0.0,dtim , rad , conv_rad ;
2074 float **parsave=NULL ;
2075 mat44 *matsave=NULL ;
2076 mat44 targ_cmat,base_cmat,base_cmat_inv,targ_cmat_inv,mast_cmat,mast_cmat_inv,
2077 qmat,wmat ;
2078 MRI_IMAGE *apply_im = NULL ;
2079 float *apply_far = NULL ;
2080 int apply_nx=0, apply_ny=0, apply_mode=0, nparam_free , diffblur=1 ;
2081 float cost, cost_ini ;
2082 mat44 cmat_bout , cmat_tout , aff12_xyz ;
2083 int nxout=0,nyout=0,nzout=0 ;
2084 float dxout,dyout,dzout ;
2085 floatvec *allcost ; /* 19 Sep 2007 */
2086 float allpar[MAXPAR] ;
2087 float xsize , ysize , zsize ; /* 06 May 2008: box size */
2088 float bfac ; /* 14 Oct 2008: brick factor */
2089 int twodim_code=0 , xx_code=0 , yy_code=0 , zz_code=0 ;
2090
2091 /*----- input parameters, to be filled in from the options -----*/
2092
2093 THD_3dim_dataset *dset_base = NULL ;
2094 THD_3dim_dataset *dset_targ = NULL ; /* target == source */
2095 THD_3dim_dataset *dset_mast = NULL ;
2096 THD_3dim_dataset *dset_weig = NULL ;
2097 int tb_mast = 0 ; /* for -master SOURCE/BASE */
2098 int auto_weight = 3 ; /* -autobbox == default */
2099 float auto_wclip = 0.0f ; /* 31 Jul 2007 */
2100 float auto_wpow = 1.0f ; /* 10 Sep 2007 */
2101 char *auto_string = "-autobox" ;
2102 int auto_dilation = 0 ; /* for -automask+N */
2103 int wtspecified = 0 ; /* 10 Sep 2007 (was weight specified?) */
2104 double dxyz_mast = 0.0 ; /* implemented 24 Jul 2007 */
2105 int meth_code = GA_MATCH_HELLINGER_SCALAR ;
2106 int sm_code = GA_SMOOTH_GAUSSIAN ;
2107 float sm_rad = 0.0f ;
2108 float fine_rad = 0.0f ;
2109 int floatize = 0 ; /* off by default */
2110 int twopass = 1 ; /* on by default */
2111 int twofirst = 1 ; /* on by default */
2112 int zeropad = 1 ; /* on by default */
2113 char *prefix = NULL ; /* off by default */
2114 char *wtprefix = NULL ; /* off by default */
2115 char *param_save_1D = NULL ; /* off by default */
2116 char *matrix_save_1D = NULL ; /* 23 Jul 2007 */
2117 char *apply_1D = NULL ; /* off by default */
2118 int interp_code = MRI_LINEAR ;
2119 int got_interp = 0 ;
2120 int npt_match = -47 ; /* 47%, that is */
2121 int final_interp = MRI_CUBIC ;
2122 int got_final = 0 ;
2123 int warp_code = WARP_AFFINE ;
2124 char warp_code_string[64] = "\0" ; /* 22 Feb 2010 */
2125 int warp_freeze = 0 ; /* off by default */
2126 int do_small = 0 ; /* 12 May 2020 */
2127 int nparopt = 0 ;
2128 MRI_IMAGE *matini = NULL ;
2129 int tbest = DEFAULT_TBEST; /* default=try best 5 */
2130 int num_rtb = 99 ; /* 28 Aug 2008 */
2131 int nocast = 0 ; /* 29 Aug 2008 */
2132 param_opt paropt[MAXPAR] ;
2133 float powell_mm = 0.0f ;
2134 float powell_aa = 0.0f ;
2135 float conv_mm = 0.001f ; /* millimeters */
2136 float nmask_frac = -1.0; /* use default for voxel fraction */
2137 int matorder = MATORDER_SDU ; /* matrix mult order */
2138 int smat = SMAT_LOWER ; /* shear matrix triangle */
2139 int dcode = DELTA_AFTER ; /* shift after */
2140 int meth_check_count = 0 ; /* don't do it */
2141 int meth_check[NMETH+1] ;
2142 int meth_median_replace = 0 ; /* don't do it */
2143 char *save_hist = NULL ; /* don't save it */
2144 long seed = 7654321 ; /* random? */
2145 int XYZ_warp = 0 ; /* off by default */
2146 double hist_pow = 0.0 ;
2147 int hist_nbin = 0 ;
2148 int epi_fe = -1 ; /* off by default */
2149 int epi_pe = -1 ;
2150 int epi_se = -1 ;
2151 int epi_targ = -1 ;
2152 int replace_base = 0 ; /* off by default */
2153 int replace_meth = 0 ; /* off by default */
2154 int usetemp = 0 ; /* off by default */
2155 int nmatch_setup = 98765 ;
2156 int ignout = 0 ; /* 28 Feb 2007 */
2157 int hist_mode = 0 ; /* 08 May 2007 */
2158 float hist_param = 0.0f ;
2159 int hist_setbyuser = 0 ;
2160 int do_cmass = 0 ; /* 30 Jul 2007 */
2161 int big_cmass = 0 ; /* 29 May 2021 */
2162 int do_refinal = 1 ; /* 14 Nov 2007 */
2163 int use_realaxes = 0 ; /* 10 Oct 2014 */
2164
2165 int auto_tdilation = 0 ; /* for -source_automask+N */
2166 int auto_tmask = 0 ;
2167 char *auto_tstring = NULL ;
2168 int fill_source_mask = 0 ;
2169
2170 #ifdef USE_OLD_BLOK_DEFAULTS
2171 int bloktype = GA_BLOK_RHDD ; /* 20 Aug 2007 */
2172 float blokrad = 6.54321f ;
2173 #else
2174 int bloktype = GA_BLOK_TOHD ; /* 21 Jul 2021 */
2175 float blokrad = -1.0f ;
2176 #endif
2177 int blokmin = 0 ;
2178
2179 int do_allcost = 0 ; /* 19 Sep 2007 */
2180 int do_save_pearson_map = 0 ; /* 25 Jan 2021 */
2181 char *save_pearson_prefix = NULL ;
2182
2183 MRI_IMAGE *allcostX1D = NULL ; /* 02 Sep 2008 */
2184 char *allcostX1D_outname = NULL ;
2185
2186 #ifdef ALLOW_NWARP /*********************************************************/
2187 int nwarp_pass = 0 ;
2188 int nwarp_type = WARP_CUBIC ;
2189 float nwarp_parmax = 0.10f ; /* 05 Jan 2011 */
2190 int nwarp_flags = 0 ; /* 29 Oct 2010 */
2191 int nwarp_itemax = 0 ;
2192 int nwarp_fixaff = 0 ; /* 26 Nov 2010 */
2193 int nwarp_fixmotX = 0 ; /* 07 Dec 2010 */
2194 int nwarp_fixdepX = 0 ;
2195 int nwarp_fixmotY = 0 ;
2196 int nwarp_fixdepY = 0 ;
2197 int nwarp_fixmotZ = 0 ;
2198 int nwarp_fixdepZ = 0 ;
2199 int nwarp_fixmotI = 0 ;
2200 int nwarp_fixdepI = 0 ;
2201 int nwarp_fixmotJ = 0 ;
2202 int nwarp_fixdepJ = 0 ;
2203 int nwarp_fixmotK = 0 ;
2204 int nwarp_fixdepK = 0 ;
2205 char *nwarp_save_prefix = NULL ; /* 10 Dec 2010 */
2206 int nwarp_meth_code = 0 ; /* 15 Dec 2010 */
2207 #else
2208 # define nwarp_pass 0 /* just in case it escapes somewhere */
2209 #endif /* ALLOW_NWARP */ /**************************************************/
2210
2211 int micho_zfinal = 0 ; /* 24 Feb 2010 */
2212 double micho_mi = DEFAULT_MICHO_LPC_MI ; /* -lpc+ stuff */
2213 double micho_nmi = DEFAULT_MICHO_LPC_NMI ;
2214 double micho_crA = DEFAULT_MICHO_LPC_CRA ;
2215 double micho_hel = DEFAULT_MICHO_LPC_HEL ;
2216 double micho_ov = DEFAULT_MICHO_LPC_OV ; /* 02 Mar 2010 */
2217 int micho_fallthru = 0 ; /* 19 Nov 2016 */
2218
2219 int do_zclip = 0 ; /* 29 Oct 2010 */
2220
2221 bytevec *emask = NULL ; /* 14 Feb 2013 */
2222
2223 int do_xflip_bset = 0 ; /* 18 Jun 2019 */
2224
2225 #undef ALLOW_UNIFIZE /* I decided this was a bad idea */
2226 #ifdef ALLOW_UNIFIZE
2227 int do_unifize_base = 0 ; /* 23 Dec 2016 */
2228 int do_unifize_targ = 0 ; /* not implemented */
2229 MRI_IMAGE *im_ubase = NULL ;
2230 MRI_IMAGE *im_utarg = NULL ;
2231 #endif
2232
2233 #define APPLYING ( apply_1D != NULL || apply_mode != 0 ) /* 13 Mar 2017 */
2234
2235 /**----------------------------------------------------------------------*/
2236 /**----------------- Help the pitifully ignorant user? -----------------**/
2237
2238 AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */
2239
2240 /*---- A couple thousand lines of help used to be here for some reason ----*/
2241
2242 if( argc < 2 || strcmp(argv[1],"-help")==0 ||
2243 strcmp(argv[1],"-HELP")==0 || strcmp(argv[1],"-POMOC")==0 ){
2244
2245 Allin_Help() ; exit(0) ;
2246 }
2247
2248 /**-------------------------------------------------------------------**/
2249 /**-------------------- bookkeeping and marketing --------------------**/
2250 /**-------------------------------------------------------------------**/
2251
2252 #if defined(USING_MCW_MALLOC) && !defined(USE_OMP)
2253 enable_mcw_malloc() ;
2254 #else
2255 # if 0
2256 if( AFNI_yesenv("ALLIN_DEBUG") ) enable_mcw_malloc() ;
2257 # endif
2258 #endif
2259
2260 mainENTRY("3dAllineate"); machdep();
2261 AFNI_logger("3dAllineate",argc,argv);
2262 PRINT_VERSION("3dAllineate"); AUTHOR("Zhark the Registrator");
2263 THD_check_AFNI_version("3dAllineate"); /* no longer does anything */
2264 (void)COX_clock_time() ;
2265
2266 /*-- initialize -final interp from environment? [25 Nov 2018] --*/
2267
2268 ii = INTERP_code(my_getenv("AFNI_3dAllineate_final")) ;
2269 if( ii >= 0 ){ final_interp = ii ; got_final = 1 ; }
2270
2271 /*********************************************************************/
2272 /**--- process command line options (1000+ lines of boring code) ---**/
2273 /*********************************************************************/
2274
2275 iarg = 1 ;
2276 while( iarg < argc && argv[iarg][0] == '-' ){
2277
2278 /*------*/
2279
2280 #ifdef ALLOW_UNIFIZE
2281 if( strcmp(argv[iarg],"-unifize_base") == 0 ){ /* 23 Dec 2016 */
2282 do_unifize_base++ ; iarg++ ; continue ;
2283 }
2284 # if 0
2285 if( strcmp(argv[iarg],"-unifize_source") == 0 ){ /* 23 Dec 2016 */
2286 do_unifize_targ++ ; iarg++ ; continue ;
2287 }
2288 # endif
2289 #endif
2290
2291 /*------*/
2292
2293 if( strcmp(argv[iarg],"-smallrange") == 0 ){ /* 12 May 2020 */
2294 do_small++ ; iarg++ ; continue ;
2295 }
2296
2297 /*------*/
2298
2299 if( strcmp(argv[iarg],"-realaxes") == 0 ){ /* 10 Oct 2014 */
2300 use_realaxes++ ; iarg++ ; continue ;
2301 }
2302
2303 /*------*/
2304
2305 if( strcmp(argv[iarg],"-xflipbase") == 0 ){ /* 18 Jun 2019 [SECRET] */
2306 do_xflip_bset = 1 ; iarg++ ; continue ;
2307 }
2308
2309 /*-----*/
2310
2311 if( strcmp(argv[iarg],"-zclip") == 0 || strcmp(argv[iarg],"-noneg") == 0 ){ /* 29 Oct 2010 */
2312 do_zclip++ ; iarg++ ; continue ;
2313 }
2314
2315 /*-----*/
2316
2317 #ifdef ALLOW_NWARP /*********************************************************/
2318 if( strncmp(argv[iarg],"-nwarp_save",11) == 0 ){ /* 10 Dec 2010 = SECRET */
2319 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2320 if( !THD_filename_ok(argv[iarg]) )
2321 ERROR_exit("badly formed filename: '%s' '%s' :-(",argv[iarg-1],argv[iarg]) ;
2322 if( strcmp(argv[iarg],"NULL") == 0 ) nwarp_save_prefix = NULL ;
2323 else nwarp_save_prefix = argv[iarg] ;
2324 iarg++ ; continue ;
2325 }
2326
2327 /*-----*/
2328
2329 if( strcmp(argv[iarg],"-nwarp_parmax") == 0 ){ /* 05 Jan 2011 = SECRET */
2330 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2331 nwarp_parmax = (float)strtod(argv[iarg],NULL) ;
2332 if( nwarp_parmax <= 0.0f || nwarp_parmax > 1.0f )
2333 ERROR_exit("Illegal value (%g) after '%s' :-(",nwarp_parmax,argv[iarg-1]) ;
2334 iarg++ ; continue ;
2335 }
2336
2337 /*-----*/
2338
2339 if( strncmp(argv[iarg],"-nwarp_fix",10) == 0 ){ /* 07 Dec 2010 = SECRET */
2340 char *aaa = argv[iarg]+10 , dcod ;
2341 if( strlen(aaa) < 4 ) ERROR_exit("don't understand option %s",argv[iarg]) ;
2342 dcod = toupper(aaa[3]) ;
2343 if( strncmp(aaa,"mot",3) == 0 ){ /* -nwarp_fixmot */
2344 switch( dcod ){
2345 case 'X': nwarp_fixmotX = 1 ; break ;
2346 case 'Y': nwarp_fixmotY = 1 ; break ;
2347 case 'Z': nwarp_fixmotZ = 1 ; break ;
2348 case 'I': nwarp_fixmotI = 1 ; break ;
2349 case 'J': nwarp_fixmotJ = 1 ; break ;
2350 case 'K': nwarp_fixmotK = 1 ; break ;
2351 default: ERROR_exit("can't decode option %s",argv[iarg]) ;
2352 }
2353 } else if( strncmp(aaa,"dep",3) == 0 ){ /* -nwarp_fixdep */
2354 switch( dcod ){
2355 case 'X': nwarp_fixdepX = 1 ; break ;
2356 case 'Y': nwarp_fixdepY = 1 ; break ;
2357 case 'Z': nwarp_fixdepZ = 1 ; break ;
2358 case 'I': nwarp_fixdepI = 1 ; break ;
2359 case 'J': nwarp_fixdepJ = 1 ; break ;
2360 case 'K': nwarp_fixdepK = 1 ; break ;
2361 default: ERROR_exit("can't decode option %s",argv[iarg]) ;
2362 }
2363 } else {
2364 ERROR_exit("don't know option %s",argv[iarg]) ;
2365 }
2366 iarg++ ; continue ;
2367 }
2368
2369 /*-----*/
2370
2371 if( strcasecmp(argv[iarg],"-nwarp_HERMITE") == 0 ){ /** 28 Mar 2013: SUPER-SECRET **/
2372 GA_setup_polywarp(GA_HERMITE) ; nwarp_parmax = 0.0444f ;
2373 iarg++ ; continue ;
2374 }
2375
2376 /*-----*/
2377
2378 if( strcmp(argv[iarg],"-nwarp") == 0 ){ /* 03 Apr 2008 = SECRET */
2379 nwarp_pass = 1 ; iarg++ ;
2380
2381 WARNING_message(" !! Use program 3dQwarp instead of 3dAllineate -nwarp !!" ) ;
2382 WARNING_message(" !! 3dAllineate -nwarp is obsolete and inferior :( !!" ) ;
2383 WARNING_message(" (( YOU HAVE BEEN WARNED -- here lurketh the dragons ))" ) ;
2384
2385 if( iarg >= argc ){
2386 ERROR_exit("need a warp type after '-nwarp' :-(") ;
2387 } else if( strncasecmp(argv[iarg],"bil",3) == 0 ){
2388 nwarp_type = WARP_BILINEAR ;
2389 if( strstr(argv[iarg],"D") != NULL ) nwarp_flags = 1 ; /* 29 Oct 2010 */
2390 } else if( strncasecmp(argv[iarg],"cub",3) == 0 ||
2391 strncasecmp(argv[iarg],"poly3",5) == 0 ){ /* 13 Nov 2010 */
2392 nwarp_type = WARP_CUBIC ;
2393 } else if( strncasecmp(argv[iarg],"qui",3) == 0 ||
2394 strncasecmp(argv[iarg],"poly5",5) == 0 ){ /* 15 Nov 2010 */
2395 nwarp_type = WARP_QUINT ;
2396 } else if( strncasecmp(argv[iarg],"hep",3) == 0 ||
2397 strncasecmp(argv[iarg],"poly7",5) == 0 ){ /* 15 Nov 2010 */
2398 nwarp_type = WARP_HEPT ;
2399 } else if( strncasecmp(argv[iarg],"non",3) == 0 ||
2400 strncasecmp(argv[iarg],"poly9",5) == 0 ){ /* 17 Nov 2010 */
2401 nwarp_type = WARP_NONI ;
2402 } else {
2403 ERROR_exit("unknown -nwarp type '%s' :-(",argv[iarg]) ;
2404 }
2405 nwarp_fixaff = ( strstr(argv[iarg],"FA") != NULL ) ;
2406 warp_code = WARP_AFFINE ; iarg++ ;
2407
2408 if( iarg < argc && isdigit(argv[iarg][0]) ){ /** really secret **/
2409 nwarp_itemax = (int)strtod(argv[iarg],NULL) ;
2410 iarg++ ;
2411 }
2412
2413 if( iarg < argc && isalpha(argv[iarg][0]) ){
2414 nwarp_meth_code = meth_name_to_code(argv[iarg]) ;
2415 if( nwarp_meth_code > 0 ) iarg++ ;
2416 }
2417
2418 /* change some other parameters from their defaults */
2419
2420 do_refinal = 0 ;
2421 continue ;
2422 }
2423 #endif /* ALLOW_NWARP */ /**************************************************/
2424
2425 /*-----*/
2426
2427 if( strcmp(argv[iarg],"-norefinal") == 0 ){ /* 14 Nov 2007 */
2428 do_refinal = 0 ; iarg++ ; continue ; /* SECRET OPTION */
2429 }
2430
2431 /*-----*/
2432
2433 if( strcmp(argv[iarg],"-allcost") == 0 ){ /* 19 Sep 2007 */
2434 do_allcost = 1 ; iarg++ ; continue ; /* SECRET OPTIONS */
2435 }
2436 if( strcmp(argv[iarg],"-allcostX") == 0 ){
2437 do_allcost = -1 ; iarg++ ; continue ;
2438 }
2439 if( strcmp(argv[iarg],"-allcostX1D") == 0 ){ /* 02 Sep 2008 */
2440 MRI_IMAGE *qim ;
2441 do_allcost = -2 ;
2442 if(strcmp(argv[++iarg],"IDENTITY") == 0)
2443 allcostX1D = mri_identity_params();
2444 else {
2445 qim = mri_read_1D( argv[iarg] ) ;
2446 if( qim == NULL )
2447 ERROR_exit("Can't read -allcostX1D '%s' :-(",argv[iarg]) ;
2448 allcostX1D = mri_transpose(qim) ; mri_free(qim) ;
2449 }
2450 if( allcostX1D->nx < 12 )
2451 ERROR_exit("-allcostX1D '%s' has only %d values per row :-(" ,
2452 argv[iarg] , allcostX1D->nx ) ;
2453 allcostX1D_outname = strdup(argv[++iarg]) ;
2454 ++iarg ; continue ;
2455 }
2456
2457 /*-----*/
2458
2459 if( strcasecmp(argv[iarg],"-PearSave") == 0 ||
2460 strcasecmp(argv[iarg],"-SavePear") == 0 ){ /* 25 Jan 2021 */
2461 static char *pppname = "PearSave.nii.gz" ;
2462 do_save_pearson_map = 1 ; iarg++ ;
2463 if( !THD_filename_ok(argv[iarg]) ){
2464 WARNING_message("Option '%s' has illegal prefix '%s' - replacing with '%s'",
2465 argv[iarg-1] , argv[iarg] , pppname ) ;
2466 save_pearson_prefix = strdup(pppname) ;
2467 } else {
2468 save_pearson_prefix = strdup(argv[iarg]) ;
2469 }
2470 ++iarg ; continue ;
2471 }
2472
2473 /*-----*/
2474
2475 if( strncmp(argv[iarg],"-cmass",6) == 0 ){
2476 if( argv[iarg][6] == '+' ){
2477 if (strchr(argv[iarg]+6,'a')) {
2478 do_cmass = -1; /* ZSS -- automatic determination */
2479 } else {
2480 do_cmass = (strchr(argv[iarg]+6,'x') != NULL) /* does string */
2481 + 2*(strchr(argv[iarg]+6,'y') != NULL) /* contain x, */
2482 + 4*(strchr(argv[iarg]+6,'z') != NULL) ; /* y, or z? */
2483 }
2484 if( do_cmass == 0 )
2485 ERROR_exit("Don't understand coordinates in '%s :-(",argv[iarg]) ;
2486 } else {
2487 do_cmass = 7 ; /* all coords */
2488 }
2489 if( verb ){
2490 char cstr[16]="undefined" ;
2491 if( do_cmass < 0 ) strcpy(cstr,"auto") ;
2492 else if( do_cmass == 0 ) strcpy(cstr,"none") ;
2493 else {
2494 strcpy(cstr,"+") ;
2495 if( do_cmass & 1 ) strcat(cstr,"x") ;
2496 if( do_cmass & 2 ) strcat(cstr,"y") ;
2497 if( do_cmass & 3 ) strcat(cstr,"z") ;
2498 }
2499 INFO_message("Option '%s' enables center-of-mass code = %d = %s",argv[iarg],do_cmass,cstr) ;
2500 }
2501 iarg++ ; continue ;
2502 }
2503
2504 if( strcmp(argv[iarg],"-nocmass") == 0 ){
2505 if( verb ) INFO_message("Option '%s' disables center-of-mass usage",argv[iarg]) ;
2506 do_cmass = 0 ; iarg++ ; continue ;
2507 }
2508
2509 /*-----*/
2510
2511 if( strcmp(argv[iarg],"-ignout") == 0 ){ /* SECRET OPTION */
2512 GA_set_outval(1.e+33); ignout = 1; iarg++; continue; /* 28 Feb 2007 */
2513 }
2514
2515 /*-----*/
2516
2517 if( strcmp(argv[iarg],"-matini") == 0 ){
2518 if( matini != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
2519 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2520 if( strncmp(argv[iarg],"1D:",3) == 0 ||
2521 (strchr(argv[iarg],' ') == NULL && argv[iarg][0] != '&') ){
2522 matini = mri_read_1D( argv[iarg] ) ;
2523 if( matini == NULL ) ERROR_exit("Can't read -matini file '%s' :-(",argv[iarg]);
2524 } else {
2525 matini = mri_matrix_evalrpn( argv[iarg] ) ;
2526 if( matini == NULL ) ERROR_exit("Can't evaluate -matini expression :-(");
2527 }
2528 if( matini->nx < 3 || matini->ny < 3 )
2529 ERROR_exit("-matini matrix has nx=%d and ny=%d (should be at least 3) :-(",
2530 matini->nx,matini->ny) ;
2531 else if( matini->nx > 3 || matini->ny > 4 )
2532 WARNING_message("-matini matrix has nx=%d and ny=%d (should be 3x4) :-(",
2533 matini->nx,matini->ny) ;
2534
2535 WARNING_message("-matini is not yet implemented! :-(") ;
2536 iarg++ ; continue ;
2537 }
2538
2539 /*-----*/
2540
2541 if( strcmp(argv[iarg],"-mast_dxyz") == 0 ||
2542 strcmp(argv[iarg],"-dxyz_mast") == 0 ||
2543 strcmp(argv[iarg],"-newgrid" ) == 0 ){
2544
2545 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2546 dxyz_mast = strtod(argv[iarg],NULL) ;
2547 if( dxyz_mast <= 0.0 )
2548 ERROR_exit("Illegal value '%s' after -mast_dxyz :-(",argv[iarg]) ;
2549 if( dxyz_mast <= 0.5 )
2550 WARNING_message("Small value %g after -mast_dxyz :-(",dxyz_mast) ;
2551 iarg++ ; continue ;
2552 }
2553
2554 /*-----*/
2555
2556 if( strcmp(argv[iarg],"-nmsetup") == 0 ){
2557 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2558 nmatch_setup = (int)strtod(argv[iarg],NULL) ;
2559 if( nmatch_setup < 9999 ) nmatch_setup = 23456 ;
2560 iarg++ ; continue ;
2561 }
2562
2563 /*-----*/
2564
2565 if( strncmp(argv[iarg],"-master",6) == 0 ){
2566 if( dset_mast != NULL || tb_mast )
2567 ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
2568 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2569 if( strcmp(argv[iarg],"SOURCE") == 0 ){ /* 19 Jul 2007 */
2570 tb_mast = 1 ;
2571 } else if( strcmp(argv[iarg],"BASE") == 0 ){
2572 tb_mast = 2 ;
2573 } else {
2574 dset_mast = THD_open_dataset( argv[iarg] ) ;
2575 if( dset_mast == NULL )
2576 ERROR_exit("can't open -master dataset '%s' :-(",argv[iarg]);
2577 }
2578 iarg++ ; continue ;
2579 }
2580
2581 /*-----*/
2582
2583 if( strcmp(argv[iarg],"-seed") == 0 ){ /* SECRET OPTION */
2584 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2585 seed = (long)strtod(argv[iarg],NULL) ; iarg++ ; continue ;
2586 }
2587
2588 /*-----*/
2589
2590 if( strcmp(argv[iarg],"-powell") == 0 ){ /* SECRET OPTION */
2591 if( ++iarg >= argc-1 ) ERROR_exit("no arguments after '%s' :-(",argv[iarg-1]) ;
2592 powell_mm = (float)strtod(argv[iarg++],NULL) ;
2593 powell_aa = (float)strtod(argv[iarg++],NULL) ;
2594 if( powell_mm < 1.0f ) powell_mm = 1.0f ;
2595 if( powell_aa < 1.0f ) powell_aa = 1.0f ;
2596 if( verb ) INFO_message("Set Powell iteration factors to m=%.1f a=%.1f",powell_mm,powell_aa) ;
2597 continue ;
2598 }
2599
2600 /*-----*/
2601
2602 if( strncmp(argv[iarg],"-weight_frac",11) == 0 ){
2603 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2604 nmask_frac = atof( argv[iarg] ) ;
2605 if( nmask_frac < 0.0f || nmask_frac > 1.0f )
2606 ERROR_exit("-weight_frac must be between 0.0 and 1.0 (have '%s') :-(",argv[iarg]);
2607 wtspecified = 1 ; iarg++ ; continue ;
2608 }
2609
2610 /*-----*/
2611
2612 if( strncmp(argv[iarg],"-weight",6) == 0 ){
2613 auto_weight = 0 ;
2614 if( dset_weig != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
2615 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2616 dset_weig = THD_open_dataset( argv[iarg] ) ;
2617 if( dset_weig == NULL ) ERROR_exit("can't open -weight dataset '%s' :-(",argv[iarg]);
2618 wtspecified = 1 ; iarg++ ; continue ;
2619 }
2620
2621 /*-----*/
2622
2623 if( strncmp(argv[iarg],"-autoweight",11) == 0 ){
2624 char *cpt ;
2625 int cpt_offset = 2; /* offset for exponent str [rickr 23 Apr 2019] */
2626 /* allow ** or ^ (to avoid shell protection) */
2627 if( dset_weig != NULL ) ERROR_exit("Can't use -autoweight AND -weight :-(") ;
2628 auto_weight = 1 ; auto_string = argv[iarg] ;
2629 cpt = strstr(auto_string,"+") ;
2630 if( cpt != NULL && *(cpt+1) != '\0' ) /* 31 Jul 2007 */
2631 auto_wclip = (float)strtod(cpt+1,NULL) ;
2632 cpt = strstr(auto_string,"**") ;
2633 if( cpt == NULL ) {
2634 /* allow ** or ^ (to avoid shell protection) [rickr 23 Apr 2019] */
2635 cpt = strstr(auto_string,"^") ;
2636 cpt_offset = 1;
2637 }
2638 if( cpt != NULL && *(cpt+cpt_offset) != '\0' ) /* 10 Sep 2007 */
2639 auto_wpow = (float)strtod(cpt+cpt_offset,NULL) ;
2640 wtspecified = 1 ; iarg++ ; continue ;
2641 }
2642
2643 if( strncmp(argv[iarg],"-automask",9) == 0 ){
2644 if( dset_weig != NULL ) ERROR_exit("Can't use -automask AND -weight :-(") ;
2645 auto_weight = 2 ; auto_string = argv[iarg] ;
2646 if( auto_string[9] == '+' && auto_string[10] != '\0' )
2647 auto_dilation = (int)strtod(auto_string+10,NULL) ;
2648 wtspecified = 1 ; iarg++ ; continue ;
2649 }
2650
2651 if( strncmp(argv[iarg],"-noauto",6) == 0 ||
2652 strncmp(argv[iarg],"-nomask",6) == 0 ){
2653 wtspecified = 1 ; auto_weight = 0 ; iarg++ ; continue ;
2654 }
2655
2656 if( strcmp(argv[iarg],"-autobox") == 0 ){
2657 wtspecified = 1 ; auto_weight = 3 ; auto_string = "-autobox" ;
2658 iarg++ ; continue ;
2659 }
2660
2661 /*-----*/
2662
2663 if( strcmp(argv[iarg],"-source_mask") == 0 ){ /* 07 Aug 2007 */
2664 byte *mmm ; THD_3dim_dataset *dset_tmask ;
2665 if( im_tmask != NULL )
2666 ERROR_exit("Can't use -source_mask twice :-(") ;
2667 if( auto_tmask )
2668 ERROR_exit("Can't use -source_mask AND -source_automask :-(") ;
2669 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2670 dset_tmask = THD_open_dataset( argv[iarg] ) ;
2671 if( dset_tmask == NULL )
2672 ERROR_exit("can't open -source_mask dataset '%s' :-(",argv[iarg]);
2673 mmm = THD_makemask( dset_tmask , 0 , 1.0f,-1.0f ) ;
2674 if( mmm == NULL )
2675 ERROR_exit("Can't use -source_mask '%s' for some reason :-(",argv[iarg]) ;
2676 im_tmask = mri_new_vol_empty(
2677 DSET_NX(dset_tmask),DSET_NY(dset_tmask),DSET_NZ(dset_tmask) ,
2678 MRI_byte ) ;
2679 DSET_delete(dset_tmask) ; /* ZSS: Moved here cause that's
2680 right and proper*/
2681 mri_fix_data_pointer( mmm , im_tmask ) ;
2682 ntmask = THD_countmask( im_tmask->nvox , mmm ) ;
2683 if( ntmask < 666 )
2684 ERROR_exit("Too few (%d) voxels in -source_mask :-(",ntmask) ;
2685 if( verb > 1 ) INFO_message("%d voxels in -source_mask",ntmask) ;
2686 iarg++ ; fill_source_mask = 1 ; continue ;
2687 }
2688
2689 if( strncmp(argv[iarg],"-source_automask",16) == 0 ){ /* 07 Aug 2007 */
2690 if( im_tmask != NULL )
2691 ERROR_exit("Can't use -source_automask AND -source_mask :-(") ;
2692 auto_tmask = 1 ; auto_tstring = argv[iarg] ;
2693 if( auto_tstring[16] == '+' && auto_string[17] != '\0' )
2694 auto_tdilation = (int)strtod(auto_tstring+17,NULL) ;
2695 iarg++ ; fill_source_mask = 1 ; continue ;
2696 }
2697
2698 /*-----*/
2699
2700 if( strncmp(argv[iarg],"-wtprefix",6) == 0 ){
2701 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2702 if( !THD_filename_ok(argv[iarg]) )
2703 ERROR_exit("badly formed filename: '%s' '%s' :-(",argv[iarg-1],argv[iarg]) ;
2704 wtprefix = argv[iarg] ; iarg++ ; continue ;
2705 }
2706
2707 /*-----*/
2708
2709 if( strcmp(argv[iarg],"-savehist") == 0 ){ /* SECRET OPTION */
2710 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2711 if( !THD_filename_ok(argv[iarg]) )
2712 ERROR_exit("badly formed filename: '%s' '%s' :-(",argv[iarg-1],argv[iarg]) ;
2713 save_hist = argv[iarg] ; iarg++ ; continue ;
2714 }
2715
2716 /*-----*/
2717
2718 if( strcmp(argv[iarg],"-histpow") == 0 ){ /* SECRET OPTION */
2719 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2720 hist_pow = strtod(argv[iarg],NULL) ;
2721 set_2Dhist_hpower(hist_pow) ;
2722 iarg++ ; continue ;
2723 }
2724
2725 if( strcmp(argv[iarg],"-histbin") == 0 ){ /* SECRET OPTION */
2726 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2727 hist_nbin = (int)strtod(argv[iarg],NULL) ;
2728 hist_mode = 0 ; hist_param = 0.0f ; hist_setbyuser = 1 ;
2729 set_2Dhist_hbin( hist_nbin ) ;
2730 iarg++ ; continue ;
2731 }
2732
2733 if( strcmp(argv[iarg],"-clbin") == 0 ){ /* SECRET OPTION - 08 May 2007 */
2734 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2735 hist_mode = GA_HIST_CLEQWD ;
2736 hist_param = (float)strtod(argv[iarg],NULL) ; hist_setbyuser = 1 ;
2737 iarg++ ; continue ;
2738 }
2739
2740 #if 0
2741 if( strcmp(argv[iarg],"-izz") == 0 ){ /* EXPERIMENTAL!! */
2742 THD_correlate_ignore_zerozero(1) ; iarg++ ; continue ;
2743 }
2744 #endif
2745
2746 if( strcmp(argv[iarg],"-eqbin") == 0 ){ /* SECRET OPTION - 08 May 2007 */
2747 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2748 hist_mode = GA_HIST_EQHIGH ;
2749 hist_param = (float)strtod(argv[iarg],NULL) ; hist_setbyuser = 1 ;
2750 if( hist_param < 3.0f || hist_param > 255.0f ){
2751 WARNING_message("'-eqbin %f' is illegal -- ignoring :-(",hist_param) ;
2752 hist_mode = 0 ; hist_param = 0.0f ; hist_setbyuser = 0 ;
2753 }
2754 iarg++ ; continue ;
2755 }
2756
2757 if( strcmp(argv[iarg],"-wtmrad") == 0 ){ /* SECRET OPTION */
2758 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2759 wt_medsmooth = (float)strtod(argv[iarg],NULL) ;
2760 iarg++ ; continue ;
2761 }
2762
2763 if( strcmp(argv[iarg],"-wtgrad") == 0 ){ /* SECRET OPTION */
2764 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2765 wt_gausmooth = (float)strtod(argv[iarg],NULL) ;
2766 iarg++ ; continue ;
2767 }
2768
2769 /*-----*/
2770
2771 if( strcmp(argv[iarg],"-verb") == 0 ){
2772 verb++ ; iarg++ ; continue ;
2773 }
2774 if( strcmp(argv[iarg],"-VERB") == 0 ){
2775 verb+=2 ; iarg++ ; continue ;
2776 }
2777 if( strcmp(argv[iarg],"-quiet") == 0 ){ /* 10 Oct 2006 */
2778 verb=0 ; iarg++ ; continue ;
2779 }
2780
2781 if( strcmp(argv[iarg],"-round") == 0 ){ /* 04 Jun 2021 [HIDDEN] */
2782 mri_genalign_round(1) ; iarg++ ; continue ;
2783 }
2784 if( strcmp(argv[iarg],"-noround") == 0 ){
2785 mri_genalign_round(0) ; iarg++ ; continue ;
2786 }
2787
2788 if( strcmp(argv[iarg],"-usetemp") == 0 ){ /* 20 Dec 2006 */
2789 usetemp = 1 ; iarg++ ; continue ;
2790 }
2791 if( strcmp(argv[iarg],"-nousetemp") == 0 ){
2792 usetemp = 0 ; iarg++ ; continue ;
2793 }
2794
2795 /*-----*/
2796
2797 if( strncmp(argv[iarg],"-floatize",6) == 0 ){
2798 floatize++ ; iarg++ ; continue ;
2799 }
2800
2801 /*-----*/
2802
2803 if( strncmp(argv[iarg],"-nopad",5) == 0 ){
2804 zeropad = 0 ; iarg++ ; continue ;
2805 }
2806
2807 /*----- Check the various cost options -----*/
2808
2809 jj = meth_name_to_code( argv[iarg]+1 ) ; /* check for match after the '-' */
2810 if( jj > 0 ){
2811 meth_code = jj ; iarg++ ; continue ; /* there was a match */
2812 }
2813
2814 /** -cost shortname *OR* -cost longname **/
2815
2816 if( strcmp(argv[iarg],"-cost") == 0 || strcmp(argv[iarg],"-meth") == 0 ){
2817 if( ++iarg >= argc ) ERROR_exit("no argument after '-cost' :-(") ;
2818
2819 jj = meth_name_to_code( argv[iarg] ) ;
2820 if( jj > 0 ){ meth_code = jj ; iarg++ ; continue ; }
2821
2822 /* fail here, UNLESS the method is 'lpc+something' */
2823
2824 if( ! (strlen(argv[iarg]) > 5 &&
2825 ( strncasecmp(argv[iarg],"lpc+",4) == 0
2826 || strncasecmp(argv[iarg],"lpa+",4) == 0 ) ) )
2827 ERROR_exit("Unknown code '%s' after -cost :-(",argv[iarg]) ;
2828
2829 /* fall through to lpc+ code */
2830
2831 micho_fallthru = 1 ;
2832 }
2833
2834 /* 24 Feb 2010: special option for -lpc+stuff or -lpa+stuff */
2835
2836 if( micho_fallthru ||
2837 (strlen(argv[iarg]) > 6 &&
2838 ( strncasecmp(argv[iarg],"-lpc+",5) == 0
2839 || strncasecmp(argv[iarg],"-lpa+",5) == 0 ) ) ){
2840 char *cpt ;
2841 if( strcasestr(argv[iarg],"lpc") != NULL ){
2842 meth_code = GA_MATCH_LPC_MICHO_SCALAR ;
2843 micho_mi = DEFAULT_MICHO_LPC_MI ;
2844 micho_nmi = DEFAULT_MICHO_LPC_NMI ;
2845 micho_crA = DEFAULT_MICHO_LPC_CRA ;
2846 micho_hel = DEFAULT_MICHO_LPC_HEL ;
2847 micho_ov = DEFAULT_MICHO_LPC_OV ;
2848 } else if( strcasestr(argv[iarg],"lpa") != NULL ){
2849 int do_old = ( strcasestr(argv[iarg],"+OLD") != NULL ) ;
2850 meth_code = GA_MATCH_LPA_MICHO_SCALAR ;
2851 micho_mi = (do_old) ? DEFAULT_MICHO_LPC_MI : DEFAULT_MICHO_LPA_MI ; /* 27 May 2021 */
2852 micho_nmi = (do_old) ? DEFAULT_MICHO_LPC_NMI : DEFAULT_MICHO_LPA_NMI ;
2853 micho_crA = (do_old) ? DEFAULT_MICHO_LPC_CRA : DEFAULT_MICHO_LPA_CRA ;
2854 micho_hel = (do_old) ? DEFAULT_MICHO_LPC_HEL : DEFAULT_MICHO_LPA_HEL ;
2855 micho_ov = (do_old) ? DEFAULT_MICHO_LPC_OV : DEFAULT_MICHO_LPA_OV ;
2856 } else {
2857 WARNING_message("How did this happen? argv[%d] = %s",iarg,argv[iarg]) ;
2858 meth_code = GA_MATCH_LPC_MICHO_SCALAR ;
2859 }
2860 micho_fallthru = 0 ;
2861 cpt = strcasestr(argv[iarg],"+hel*"); if( cpt != NULL ) micho_hel = strtod(cpt+5,NULL);
2862 cpt = strcasestr(argv[iarg],"+hel:"); if( cpt != NULL ) micho_hel = strtod(cpt+5,NULL);
2863 cpt = strcasestr(argv[iarg],"+mi*" ); if( cpt != NULL ) micho_mi = strtod(cpt+4,NULL);
2864 cpt = strcasestr(argv[iarg],"+mi:" ); if( cpt != NULL ) micho_mi = strtod(cpt+4,NULL);
2865 cpt = strcasestr(argv[iarg],"+nmi*"); if( cpt != NULL ) micho_nmi = strtod(cpt+5,NULL);
2866 cpt = strcasestr(argv[iarg],"+nmi:"); if( cpt != NULL ) micho_nmi = strtod(cpt+5,NULL);
2867 cpt = strcasestr(argv[iarg],"+crA*"); if( cpt != NULL ) micho_crA = strtod(cpt+5,NULL);
2868 cpt = strcasestr(argv[iarg],"+crA:"); if( cpt != NULL ) micho_crA = strtod(cpt+5,NULL);
2869 cpt = strcasestr(argv[iarg],"+ov*" ); if( cpt != NULL ) micho_ov = strtod(cpt+4,NULL);
2870 cpt = strcasestr(argv[iarg],"+ov:" ); if( cpt != NULL ) micho_ov = strtod(cpt+4,NULL);
2871 cpt = strcasestr(argv[iarg],"ZZ") ; micho_zfinal = (cpt != NULL) ;
2872
2873 INFO_message("%s parameters: hel=%.2f mi=%.2f nmi=%.2f crA=%.2f ov=%.2f %s",
2874 meth_shortname[meth_code-1] ,
2875 micho_hel , micho_mi , micho_nmi , micho_crA , micho_ov ,
2876 micho_zfinal ? "[to be zeroed at Final iteration]" : "\0" ) ;
2877 iarg++ ; continue ;
2878 }
2879
2880 #ifdef ALLOW_METH_CHECK
2881 /*----- -check costname -----*/
2882
2883 if( strncasecmp(argv[iarg],"-check",5) == 0 ){
2884 #if 0
2885 if( strncmp(argv[iarg],"-CHECK",5) == 0 ) meth_median_replace = 1 ; /* not good */
2886 #endif
2887 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2888
2889 for( ; iarg < argc && argv[iarg][0] != '-' ; iarg++ ){
2890 if( meth_check_count == NMETH ) continue ; /* malicious user? */
2891 jj = meth_name_to_code(argv[iarg]) ;
2892 if( jj > 0 ){ meth_check[meth_check_count++] = jj; continue; }
2893 WARNING_message("Unknown code '%s' after -check :-(",argv[iarg]) ;
2894 }
2895 continue ;
2896 }
2897 #else
2898 if( strncasecmp(argv[iarg],"-check",5) == 0 ){
2899 WARNING_message("option '%s' is no longer available",argv[iarg]) ;
2900 for( ; iarg < argc && argv[iarg][0] != '-' ; iarg++ ) ; /*nada*/
2901 continue ;
2902 }
2903 #endif
2904
2905 /*-----*/
2906
2907 if( strcmp(argv[iarg],"-emask") == 0 ){ /* 14 Feb 2013 */
2908 if( emask != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
2909 if( ++iarg >= argc ) ERROR_exit("no argument after '-emask' :-(") ;
2910 emask = THD_create_mask_from_string( argv[iarg] ) ;
2911 if( emask == NULL ) ERROR_exit("Can't create emask from '%s'",argv[iarg]) ;
2912 iarg++ ; continue ;
2913 }
2914
2915 /*-----*/
2916
2917 if( strcmp(argv[iarg],"-base") == 0 ){
2918 if( dset_base != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
2919 if( ++iarg >= argc ) ERROR_exit("no argument after '-base' :-(") ;
2920 dset_base = THD_open_dataset( argv[iarg] ) ;
2921 if( dset_base == NULL ) ERROR_exit("can't open -base dataset '%s' :-(",argv[iarg]);
2922 ii = (int)DSET_BRICK_TYPE(dset_base,0) ;
2923 if( ii != MRI_float && ii != MRI_short && ii != MRI_byte )
2924 #if 0
2925 ERROR_exit("base dataset %s has non-scalar data type '%s' :-(",
2926 DSET_BRIKNAME(dset_base) , MRI_TYPE_name[ii] ) ;
2927 #else
2928 WARNING_message("base dataset %s has non-scalar data type '%s' :-(",
2929 DSET_BRIKNAME(dset_base) , MRI_TYPE_name[ii] ) ;
2930 #endif
2931 iarg++ ; continue ;
2932 }
2933
2934 /*-----*/
2935
2936 if( strncmp(argv[iarg],"-source",6) == 0 ||
2937 strncmp(argv[iarg],"-input" ,5) == 0 ||
2938 strncmp(argv[iarg],"-target",7) == 0 ||
2939 strncmp(argv[iarg],"-src" ,4) == 0 ){
2940 if( dset_targ != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
2941 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2942 dset_targ = THD_open_dataset( argv[iarg] ) ;
2943 if( dset_targ == NULL )
2944 ERROR_exit("can't open -%s dataset '%s' :-(",argv[iarg-1],argv[iarg]);
2945 iarg++ ; continue ;
2946 }
2947
2948 /*-----*/
2949
2950 if( strncmp(argv[iarg],"-median",5) == 0 ){ /* SECRET OPTION */
2951 sm_code = GA_SMOOTH_MEDIAN ; iarg++ ; continue ;
2952 }
2953
2954 /*-----*/
2955
2956 if( strncmp(argv[iarg],"-twoblur",7) == 0 ){
2957 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2958 sm_rad = (float)strtod(argv[iarg],NULL) ; twopass = 1 ;
2959 if( sm_rad < 0.0f ) sm_rad = 0.0f ;
2960 iarg++ ; continue ;
2961 }
2962
2963 if( strncmp(argv[iarg],"-fineblur",8) == 0 ){
2964 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2965 fine_rad = (float)strtod(argv[iarg],NULL) ; iarg++ ; continue ;
2966 }
2967
2968 /*-----*/
2969
2970 if( strncmp(argv[iarg],"-twobest",7) == 0 ){
2971 static int first=1 ; int tbold=tbest ;
2972 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2973 tbest = (int)strtod(argv[iarg],NULL) ; twopass = 1 ;
2974 if( tbest < 0 ){
2975 WARNING_message("-twobest %d is illegal: replacing with 0",tbest) ;
2976 tbest = 0 ;
2977 } else if( strncasecmp(argv[iarg],"MAX",3) == 0 ){ /* 28 May 2021 */
2978 tbest = PARAM_MAXTRIAL ;
2979 if( verb > 1 )
2980 INFO_message("-twobest MAX ==> %d",PARAM_MAXTRIAL) ;
2981 } else if( tbest > PARAM_MAXTRIAL ){
2982 INFO_message("-twobest %d is too big: replaced with %d",tbest,PARAM_MAXTRIAL) ;
2983 tbest = PARAM_MAXTRIAL ;
2984 } else if( !first && tbold > tbest ){
2985 INFO_message("keeping older/larger -twobest value of %d",tbold) ;
2986 tbest = tbold ;
2987 }
2988 first = 0 ; iarg++ ; continue ;
2989 }
2990
2991 if( strncmp(argv[iarg],"-num_rtb",7) == 0 ){
2992 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
2993 num_rtb = (int)strtod(argv[iarg],NULL) ; twopass = 1 ;
2994 if( num_rtb <= 0 ) num_rtb = 0 ;
2995 else if( num_rtb < 66 ) num_rtb = 66 ;
2996 else if( num_rtb > 666 ) num_rtb = 666 ;
2997 iarg++ ; continue ;
2998 }
2999
3000 if( strncmp(argv[iarg],"-nocast",6) == 0 ){
3001 nocast = 1 ; iarg++ ; continue ;
3002 }
3003
3004 /*-----*/
3005
3006 if( strncmp(argv[iarg],"-onepass",6) == 0 ){
3007 twopass = twofirst = 0 ; iarg++ ; continue ;
3008 }
3009 if( strncmp(argv[iarg],"-twopass",6) == 0 ){
3010 twopass = 1 ; twofirst = 0 ; iarg++ ; continue ;
3011 }
3012 if( strncmp(argv[iarg],"-twofirst",6) == 0 ){
3013 twofirst = twopass = 1 ; iarg++ ; continue ;
3014 }
3015
3016 /*-----*/
3017
3018 if( strncmp(argv[iarg],"-output",5) == 0 || strncmp(argv[iarg],"-prefix",5) == 0 ){
3019 if( prefix != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]) ;
3020 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3021 if( !THD_filename_ok(argv[iarg]) )
3022 ERROR_exit("badly formed filename: '%s' '%s' :-(",argv[iarg-1],argv[iarg]) ;
3023 if( strcmp(argv[iarg],"NULL") == 0 ) prefix = NULL ;
3024 else prefix = argv[iarg] ;
3025 iarg++ ; continue ;
3026 }
3027
3028 /*-----*/
3029
3030 if( strncmp(argv[iarg],"-1Dfile",5) == 0 || strncmp(argv[iarg],"-1Dparam_save",12) == 0 ){
3031 if( param_save_1D != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]);
3032 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3033 if( !THD_filename_ok(argv[iarg]) )
3034 ERROR_exit("badly formed filename: %s '%s' :-(",argv[iarg-1],argv[iarg]) ;
3035 if( STRING_HAS_SUFFIX(argv[iarg],".1D") ){
3036 param_save_1D = argv[iarg] ;
3037 } else {
3038 param_save_1D = calloc(sizeof(char*),strlen(argv[iarg])+16) ;
3039 strcpy(param_save_1D,argv[iarg]) ; strcat(param_save_1D,".param.1D") ;
3040 }
3041 iarg++ ; continue ;
3042 }
3043
3044 /*-----*/
3045
3046 if( strncmp(argv[iarg],"-1Dmatrix_save",13) == 0 ){
3047 if( matrix_save_1D != NULL ) ERROR_exit("Can't have multiple %s options :-(",argv[iarg]);
3048 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3049 if( !THD_filename_ok(argv[iarg]) )
3050 ERROR_exit("badly formed filename: %s '%s' :-(",argv[iarg-1],argv[iarg]) ;
3051 if( STRING_HAS_SUFFIX(argv[iarg],".1D") ){
3052 matrix_save_1D = argv[iarg] ;
3053 } else {
3054 matrix_save_1D = calloc(sizeof(char*),strlen(argv[iarg])+16) ;
3055 strcpy(matrix_save_1D,argv[iarg]) ; strcat(matrix_save_1D,".aff12.1D") ;
3056 }
3057 iarg++ ; continue ;
3058 }
3059
3060 /*-----*/
3061
3062 #undef APL
3063 #define APL(i,j) apply_far[(i)+(j)*apply_nx] /* i=param index, j=row index */
3064
3065 if( strncmp(argv[iarg],"-1Dapply",5) == 0 ||
3066 strncmp(argv[iarg],"-1Dparam_apply",13) == 0 ){
3067 char *fname ;
3068
3069 if( APPLYING )
3070 ERROR_exit("Can't have multiple 'apply' options :-(") ;
3071 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3072 #if 0
3073 if( strncmp(argv[iarg],"1D:",3) != 0 && !THD_filename_ok(argv[iarg]) )
3074 ERROR_exit("badly formed filename: %s '%s' :-(",argv[iarg-1],argv[iarg]) ;
3075 #endif
3076 fname = argv[iarg] ;
3077 if( strcasecmp(fname,"IDENTITY")==0 ) fname = "1D: 12@0'" ;
3078 apply_1D = fname ; qim = mri_read_1D(apply_1D) ;
3079 if( qim == NULL ) ERROR_exit("Can't read %s '%s' :-(",argv[iarg-1],apply_1D) ;
3080 apply_im = mri_transpose(qim); mri_free(qim);
3081 apply_far = MRI_FLOAT_PTR(apply_im) ;
3082 apply_nx = apply_im->nx ; /* # of values per row */
3083 apply_ny = apply_im->ny ; /* number of rows */
3084 apply_mode = APPLY_PARAM ;
3085 iarg++ ; continue ;
3086 }
3087
3088 /*-----*/
3089
3090 if( strncmp(argv[iarg],"-1Dmatrix_apply",13) == 0 ){
3091 char *fname ;
3092 if( APPLYING )
3093 ERROR_exit("Can't have multiple 'apply' options :-(") ;
3094 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3095 #if 0
3096 if( !THD_filename_ok(argv[iarg]) )
3097 ERROR_exit("badly formed filename: %s '%s' :-(",argv[iarg-1],argv[iarg]) ;
3098 #endif
3099 fname = argv[iarg] ;
3100 if( strcasecmp(fname,"IDENTITY")==0 )
3101 fname = "1D: 1 0 0 0 0 1 0 0 0 0 1 0" ;
3102 apply_1D = fname ; qim = mri_read_1D(apply_1D) ;
3103 if( qim == NULL ) ERROR_exit("Can't read -1Dmatrix_apply '%s' :-(",apply_1D) ;
3104 apply_im = mri_transpose(qim); mri_free(qim);
3105 apply_far = MRI_FLOAT_PTR(apply_im) ;
3106 apply_nx = apply_im->nx ; /* # of values per row */
3107 apply_ny = apply_im->ny ; /* number of rows */
3108 apply_mode = APPLY_AFF12 ;
3109 if( apply_nx < 12 && apply_im->nvox == 12 ){ /* special case of a 3x4 array */
3110 apply_nx = 12 ; apply_ny = 1 ;
3111 INFO_message("-1Dmatrix_apply: converting input 3x4 array to 1 row of 12 numbers") ;
3112 }
3113 if( apply_nx < 12 )
3114 ERROR_exit("%d = Less than 12 numbers per row in -1Dmatrix_apply '%s' :-(" ,apply_nx,apply_1D) ;
3115 else if( apply_nx > 12 )
3116 WARNING_message("%d = More than 12 numbers per row in -1Dmatrix_apply '%s'",apply_ny,apply_1D) ;
3117 iarg++ ; continue ;
3118 }
3119
3120 /*-----*/
3121
3122 if( strcmp(argv[iarg],"-NN") == 0 || strncmp(argv[iarg],"-nearest",6) == 0 ){
3123 interp_code = MRI_NN ; iarg++ ; got_interp = 1 ;continue ;
3124 }
3125 if( strncmp(argv[iarg],"-linear",4)==0 || strncmp(argv[iarg],"-trilinear",6)==0 ){
3126 interp_code = MRI_LINEAR ; iarg++ ; got_interp = 1 ;continue ;
3127 }
3128 if( strncmp(argv[iarg],"-cubic",4)==0 || strncmp(argv[iarg],"-tricubic",6)==0 ){
3129 interp_code = MRI_CUBIC ; iarg++ ; got_interp = 1 ;continue ;
3130 }
3131 if( strncmp(argv[iarg],"-quintic",4)==0 || strncmp(argv[iarg],"-triquintic",6)==0 ){
3132 interp_code = MRI_QUINTIC ; iarg++ ; got_interp = 1 ;continue ;
3133 }
3134 #if 0
3135 if( strncasecmp(argv[iarg],"-WSINC") == 0 ){
3136 interp_code = MRI_WSINC5 ; iarg++ ; got_interp = 1 ;continue ;
3137 }
3138 #endif
3139 if( strncmp(argv[iarg],"-interp",5)==0 ){
3140 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3141 if( strcmp(argv[iarg],"NN")==0 || strncmp(argv[iarg],"nearest",5)==0 )
3142 interp_code = MRI_NN ;
3143 else
3144 if( strncmp(argv[iarg],"linear",3)==0 || strncmp(argv[iarg],"trilinear",5)==0 )
3145 interp_code = MRI_LINEAR ;
3146 else
3147 if( strncmp(argv[iarg],"cubic",3)==0 || strncmp(argv[iarg],"tricubic",5)==0 )
3148 interp_code = MRI_CUBIC ;
3149 else
3150 if( strncmp(argv[iarg],"quintic",3)==0 || strncmp(argv[iarg],"triquintic",5)==0 )
3151 interp_code = MRI_QUINTIC ;
3152 #if 0
3153 else
3154 if( strncasecmp(argv[iarg],"WSINC",5)==0 )
3155 interp_code = MRI_WSINC5 ;
3156 #endif
3157 else
3158 ERROR_exit("Unknown code '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ;
3159 iarg++ ; got_interp = 1 ;continue ;
3160 }
3161
3162 if( strncmp(argv[iarg],"-final",5) == 0 ){
3163 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3164 if( strcmp(argv[iarg],"NN") == 0 || strncmp(argv[iarg],"nearest",5) == 0 )
3165 final_interp = MRI_NN ;
3166 else
3167 if( strncmp(argv[iarg],"linear",3) == 0 || strncmp(argv[iarg],"trilinear",5) == 0 )
3168 final_interp = MRI_LINEAR ;
3169 else
3170 if( strncmp(argv[iarg],"cubic",3) == 0 || strncmp(argv[iarg],"tricubic",5) == 0 )
3171 final_interp = MRI_CUBIC ;
3172 else
3173 if( strncmp(argv[iarg],"quintic",3)==0 || strncmp(argv[iarg],"triquintic",5)==0 )
3174 final_interp = MRI_QUINTIC ;
3175 else
3176 if( strncasecmp(argv[iarg],"WSINC",5)==0 )
3177 final_interp = MRI_WSINC5 ;
3178 else
3179 ERROR_exit("Unknown code '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ;
3180 iarg++ ; got_final = 1 ;continue ;
3181 }
3182
3183 /*-----*/
3184
3185 if( strncmp(argv[iarg],"-converge",5) == 0 ){
3186 float vv ;
3187 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3188 vv = (float)strtod(argv[iarg],NULL) ;
3189 if( vv < 0.0001f ){ vv = 0.0001f; WARNING_message("%s: limited %s to 0.0001",argv[iarg-1],argv[iarg]); }
3190 else if( vv > 6.666f ){ vv = 6.666f ; WARNING_message("%s: limited %s to 6.666" ,argv[iarg-1],argv[iarg]); }
3191 conv_mm = vv ; iarg++ ; continue ;
3192 }
3193
3194 /*-----*/
3195
3196 if( strncmp(argv[iarg],"-nmatch",5) == 0 ){
3197 char *cpt ;
3198 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3199 npt_match = (int)strtod(argv[iarg],&cpt) ;
3200 if( npt_match <= 0 )
3201 ERROR_exit("Illegal value '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ;
3202 if( *cpt == '%' || npt_match <= 100 )
3203 npt_match = -npt_match ; /* signal for % */
3204 iarg++ ; continue ;
3205 }
3206
3207 /*-----*/
3208
3209 if( strcmp(argv[iarg],"-warp") == 0 ){
3210 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3211 if( strcmp(argv[iarg],"sho") ==0 || strcmp(argv[iarg],"shift_only") ==0 )
3212 warp_code = WARP_SHIFT ;
3213 else if( strcmp(argv[iarg],"shr")==0 || strcmp(argv[iarg],"shift_rotate") ==0 )
3214 warp_code = WARP_ROTATE ;
3215 else if( strcmp(argv[iarg],"srs")==0 || strcmp(argv[iarg],"shift_rotate_scale")==0 )
3216 warp_code = WARP_SCALE ;
3217 else if( strcmp(argv[iarg],"aff")==0 || strcmp(argv[iarg],"affine_general") ==0 )
3218 warp_code = WARP_AFFINE ;
3219 else
3220 ERROR_exit("Unknown code '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ;
3221 iarg++ ; continue ;
3222 }
3223 if( strcmp(argv[iarg],"-dof") == 0 ){
3224 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3225 ii = (int)strtod(argv[iarg],NULL) ;
3226 switch(ii){
3227 case 3: warp_code = WARP_SHIFT ; break ;
3228 case 6: warp_code = WARP_ROTATE ; break ;
3229 case 9: warp_code = WARP_SCALE ; break ;
3230 case 12: warp_code = WARP_AFFINE ; break ;
3231 default:
3232 ERROR_exit("Unknown value '%s' after '%s' :-(",argv[iarg],argv[iarg-1]) ;
3233 }
3234 iarg++ ; continue ;
3235 }
3236
3237 /*-----*/
3238
3239 if( strcmp(argv[iarg],"-parfix") == 0 ){
3240 if( ++iarg >= argc-1 ) ERROR_exit("need 2 arguments after '%s' :-(",argv[iarg-1]) ;
3241 if( nparopt >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3242 ii = (int)strtod(argv[iarg],NULL) ;
3243 if( ii <= 0 ) ERROR_exit("-parfix '%s' is illegal :-(",argv[iarg]) ;
3244 v1 = (float)strtod(argv[++iarg],NULL) ;
3245 paropt[nparopt].np = ii-1 ;
3246 paropt[nparopt].code = PARC_FIX ;
3247 paropt[nparopt].vb = v1 ;
3248 nparopt++ ; iarg++ ; continue ;
3249 }
3250
3251 /*-----*/
3252
3253 if( strcmp(argv[iarg],"-parang") == 0 ){
3254 if( ++iarg >= argc-2 ) ERROR_exit("need 3 arguments after '%s' :-(",argv[iarg-1]) ;
3255 if( nparopt >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3256 ii = (int)strtod(argv[iarg],NULL) ;
3257 if( ii <= 0 ) ERROR_exit("-parang '%s' is illegal :-(",argv[iarg]) ;
3258 v1 = (float)strtod(argv[++iarg],NULL) ;
3259 v2 = (float)strtod(argv[++iarg],NULL) ;
3260 if( v1 > v2 ) ERROR_exit("-parang %d '%s' '%s' is illegal :-(",
3261 ii,argv[iarg-1],argv[iarg] ) ;
3262 paropt[nparopt].np = ii-1 ;
3263 paropt[nparopt].code = PARC_RAN ;
3264 paropt[nparopt].vb = v1 ;
3265 paropt[nparopt].vt = v2 ;
3266 nparopt++ ; iarg++ ; continue ;
3267 }
3268
3269 /*-----*/
3270
3271 if( strcmp(argv[iarg],"-maxrot") == 0 ){
3272 float vv ;
3273 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3274 if( nparopt+2 >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3275 vv = (float)strtod(argv[iarg],NULL) ;
3276 if( vv <= 0.0f || vv > 90.0f ) ERROR_exit("-maxrot %f is illegal :-(",vv) ;
3277 paropt[nparopt].np = 3 ;
3278 paropt[nparopt].code = PARC_RAN ;
3279 paropt[nparopt].vb = -vv ;
3280 paropt[nparopt].vt = vv ; nparopt++ ;
3281 paropt[nparopt].np = 4 ;
3282 paropt[nparopt].code = PARC_RAN ;
3283 paropt[nparopt].vb = -vv ;
3284 paropt[nparopt].vt = vv ; nparopt++ ;
3285 paropt[nparopt].np = 5 ;
3286 paropt[nparopt].code = PARC_RAN ;
3287 paropt[nparopt].vb = -vv ;
3288 paropt[nparopt].vt = vv ; nparopt++ ; iarg++ ; continue ;
3289 }
3290
3291 /*-----*/
3292
3293 if( strcmp(argv[iarg],"-maxscl") == 0 ){
3294 float vv , vvi ; char *cpt ;
3295 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3296 if( nparopt+2 >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3297 vv = (float)strtod(argv[iarg],&cpt) ;
3298 if( *cpt == '%' ) vv = 1.0f + 0.01*vv ;
3299 if( vv == 1.0f || vv > 2.5f || vv < 0.4f )
3300 ERROR_exit("-maxscl %f is illegal :-(",vv) ;
3301 if( vv > 1.0f ){ vvi = 1.0f/vv; }
3302 else { vvi = vv ; vv = 1.0f/vvi ; }
3303 paropt[nparopt].np = 6 ;
3304 paropt[nparopt].code = PARC_RAN ;
3305 paropt[nparopt].vb = vvi ;
3306 paropt[nparopt].vt = vv ; nparopt++ ;
3307 paropt[nparopt].np = 7 ;
3308 paropt[nparopt].code = PARC_RAN ;
3309 paropt[nparopt].vb = vvi ;
3310 paropt[nparopt].vt = vv ; nparopt++ ;
3311 paropt[nparopt].np = 8 ;
3312 paropt[nparopt].code = PARC_RAN ;
3313 paropt[nparopt].vb = vvi ;
3314 paropt[nparopt].vt = vv ; nparopt++ ; iarg++ ; continue ;
3315 }
3316
3317 /*-----*/
3318
3319 if( strcmp(argv[iarg],"-maxshr") == 0 ){ /* 03 Dec 2010 */
3320 float vv ;
3321 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3322 if( nparopt+2 >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3323 vv = (float)strtod(argv[iarg],NULL) ;
3324 if( vv <= 0.0f || vv > 1.0f ) ERROR_exit("-maxshr %f is illegal :-(",vv) ;
3325 paropt[nparopt].np = 9 ;
3326 paropt[nparopt].code = PARC_RAN ;
3327 paropt[nparopt].vb = -vv ;
3328 paropt[nparopt].vt = vv ; nparopt++ ;
3329 paropt[nparopt].np = 10 ;
3330 paropt[nparopt].code = PARC_RAN ;
3331 paropt[nparopt].vb = -vv ;
3332 paropt[nparopt].vt = vv ; nparopt++ ;
3333 paropt[nparopt].np = 11 ;
3334 paropt[nparopt].code = PARC_RAN ;
3335 paropt[nparopt].vb = -vv ;
3336 paropt[nparopt].vt = vv ; nparopt++ ; iarg++ ; continue ;
3337 }
3338
3339 /*-----*/
3340
3341 if( strcmp(argv[iarg],"-maxshf") == 0 ){
3342 float vv ;
3343 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]) ;
3344 if( nparopt+2 >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3345 vv = (float)strtod(argv[iarg],NULL) ;
3346 if( vv <= 0.0f ) ERROR_exit("-maxshf %f is illegal :-(",vv) ;
3347 paropt[nparopt].np = 0 ;
3348 paropt[nparopt].code = PARC_RAN ;
3349 paropt[nparopt].vb = -vv ;
3350 paropt[nparopt].vt = vv ; nparopt++ ;
3351 paropt[nparopt].np = 1 ;
3352 paropt[nparopt].code = PARC_RAN ;
3353 paropt[nparopt].vb = -vv ;
3354 paropt[nparopt].vt = vv ; nparopt++ ;
3355 paropt[nparopt].np = 2 ;
3356 paropt[nparopt].code = PARC_RAN ;
3357 paropt[nparopt].vb = -vv ;
3358 paropt[nparopt].vt = vv ; nparopt++ ; iarg++ ; continue ;
3359 }
3360
3361 /*-----*/
3362
3363 if( strcmp(argv[iarg],"-parini") == 0 ){
3364 if( ++iarg >= argc-1 ) ERROR_exit("need 2 arguments after '%s' :-(",argv[iarg-1]) ;
3365 if( nparopt >= MAXPAR ) ERROR_exit("too many -par... options :-(") ;
3366 ii = (int)strtod(argv[iarg],NULL) ;
3367 if( ii <= 0 ) ERROR_exit("-parini '%s' is illegal :-(",argv[iarg]) ;
3368 v1 = (float)strtod(argv[++iarg],NULL) ;
3369 paropt[nparopt].np = ii-1 ;
3370 paropt[nparopt].code = PARC_INI ;
3371 paropt[nparopt].vb = v1 ;
3372 nparopt++ ; iarg++ ; continue ;
3373 }
3374
3375 /*-----*/
3376
3377 if( strncmp(argv[iarg],"-FPS",4)==0 || strncmp(argv[iarg],"-EPI",4)==0 ){
3378 int fe=-1 , pe=-1 , se=-1 ; char *fps , *aaa=argv[iarg] ;
3379
3380 if( epi_targ >= 0 )
3381 ERROR_exit("Can't have multiple '%4.4s' options :-(",aaa) ;
3382
3383 /* is the EPI dataset the target (default) or base? */
3384
3385 epi_targ = (aaa[4] != '\0' && toupper(aaa[4]) == 'B') ? 0 : 1 ;
3386
3387 if( aaa[1] == 'F' ){ /* -FPS code */
3388 if( ++iarg >= argc ) ERROR_exit("no argument after '%s' :-(",argv[iarg-1]);
3389 fps = argv[iarg] ;
3390 if( strlen(fps) < 3 ) ERROR_exit("Too short %4.4s codes '%s' :-(",aaa,fps);
3391 } else {
3392 fps = "123" ; /* -EPI */
3393 }
3394
3395 /* decode the FPS directions, so that
3396 epi_fe = freq encode direction = 0 or 1 or 2
3397 epi_pe = phase encode direction
3398 epi_se = slice encode direction */
3399
3400 switch( fps[0] ){
3401 default: ERROR_exit("Illegal %4.4s f code '%c' :-(" , aaa,fps[0] );
3402 case 'i': case 'I': case 'x': case 'X': case '1': fe = 1; break;
3403 case 'j': case 'J': case 'y': case 'Y': case '2': fe = 2; break;
3404 case 'k': case 'K': case 'z': case 'Z': case '3': fe = 3; break;
3405 }
3406 switch( fps[1] ){
3407 default: ERROR_exit("Illegal %4.4s p code '%c' :-(" , aaa,fps[1] );
3408 case 'i': case 'I': case 'x': case 'X': case '1': pe = 1; break;
3409 case 'j': case 'J': case 'y': case 'Y': case '2': pe = 2; break;
3410 case 'k': case 'K': case 'z': case 'Z': case '3': pe = 3; break;
3411 }
3412 switch( fps[2] ){
3413 default: ERROR_exit("Illegal %4.4s s code '%c' :-(" , aaa,fps[2] );
3414 case 'i': case 'I': case 'x': case 'X': case '1': se = 1; break;
3415 case 'j': case 'J': case 'y': case 'Y': case '2': se = 2; break;
3416 case 'k': case 'K': case 'z': case 'Z': case '3': se = 3; break;
3417 }
3418 if( fe+pe+se != 6 ) ERROR_exit("Illegal %4.4s combination '%s' :-(",aaa,fps);
3419
3420 epi_fe = fe-1 ; epi_pe = pe-1 ; epi_se = se-1 ; /* process later */
3421
3422 if( verb > 1 )
3423 INFO_message("EPI parameters: targ=%d fe=%d pe=%d se=%d",
3424 epi_targ,epi_fe,epi_pe,epi_se ) ;
3425
3426 /* restrict some transformation parameters */
3427
3428 smat = SMAT_YYY ; /* shear only in y (PE) direction */
3429 warp_freeze = 1 ; /* 10 Oct 2006 */
3430
3431 /* matrix order depends on if we are restricting transformation
3432 parameters in the base image or in the target image coordinates */
3433
3434 matorder = (epi_targ) ? MATORDER_SDU : MATORDER_USD ;
3435
3436 paropt[nparopt].np = 6 ; /* fix x-scale to 1 */
3437 paropt[nparopt].code = PARC_FIX ;
3438 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3439
3440 paropt[nparopt].np = 8 ; /* fix z-scale to 1 */
3441 paropt[nparopt].code = PARC_FIX ;
3442 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3443
3444 paropt[nparopt].np = 11 ; /* fix last shear to 0 */
3445 paropt[nparopt].code = PARC_FIX ;
3446 paropt[nparopt].vb = 0.0 ; nparopt++ ;
3447
3448 twofirst = 1; replace_base = 1;
3449 #if 0
3450 replace_meth = GA_MATCH_PEARSON_SCALAR;
3451 #endif
3452 iarg++ ; continue ;
3453 }
3454
3455 /*-----*/
3456
3457 if( strcmp(argv[iarg],"-replacebase") == 0 ){ /* 18 Oct 2006 */
3458 twofirst = replace_base = 1 ; iarg++ ; continue ;
3459 }
3460 if( strcmp(argv[iarg],"-warpfreeze") == 0 ){ /* 18 Oct 2006 */
3461 warp_freeze = 1 ; iarg++ ; continue ;
3462 }
3463
3464 if( strcmp(argv[iarg],"-nowarpfreeze") == 0 ){ /* 01 Feb 2007 */
3465 warp_freeze = 0 ; iarg++ ; continue ;
3466 }
3467 if( strcmp(argv[iarg],"-noreplacebase") == 0 ){ /* 01 Feb 2007 */
3468 replace_base = 0 ; iarg++ ; continue ;
3469 }
3470
3471 /*-----*/
3472
3473 if( strcmp(argv[iarg],"-replacemeth") == 0 ){ /* 18 Oct 2006 */
3474 if( ++iarg >= argc ) ERROR_exit("no argument after '-replacemeth' :-(") ;
3475
3476 if( strcmp(argv[iarg],"0") == 0 ){
3477 replace_meth = 0 ; iarg++ ; continue ; /* special case */
3478 }
3479
3480 jj = meth_name_to_code(argv[iarg]) ;
3481 if( jj > 0 ){ replace_meth = jj ; iarg++ ; continue ; }
3482
3483 ERROR_exit("Unknown code '%s' after -replacemeth :-(",argv[iarg]) ;
3484 iarg++ ; continue ;
3485 }
3486
3487 /*-----*/
3488
3489 if( strcmp(argv[iarg],"-Xwarp") == 0 ){ /* 02 Oct 2006 */
3490 if( XYZ_warp > 0 ) ERROR_exit("only one use of -[XYZ]warp is allowed :-(");
3491 matorder = MATORDER_USD ; /* rotation after shear and scale */
3492
3493 paropt[nparopt].np = 7 ; /* fix y-scale to 1 */
3494 paropt[nparopt].code = PARC_FIX ;
3495 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3496
3497 paropt[nparopt].np = 8 ; /* fix z-scale to 1 */
3498 paropt[nparopt].code = PARC_FIX ;
3499 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3500
3501 paropt[nparopt].np = 11 ; /* fix last shear to 0 */
3502 paropt[nparopt].code = PARC_FIX ;
3503 paropt[nparopt].vb = 0.0 ; nparopt++ ;
3504
3505 smat = SMAT_XXX ; /* fix shear matrix to x-only */
3506 XYZ_warp = 1 ;iarg++ ; continue ;
3507 }
3508
3509 if( strcmp(argv[iarg],"-Ywarp") == 0 ){ /* 02 Oct 2006 */
3510 if( XYZ_warp > 0 ) ERROR_exit("only one use of -[XYZ]warp is allowed :-(");
3511 matorder = MATORDER_USD ; /* rotation after shear and scale */
3512
3513 paropt[nparopt].np = 6 ; /* fix x-scale to 1 */
3514 paropt[nparopt].code = PARC_FIX ;
3515 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3516
3517 paropt[nparopt].np = 8 ; /* fix z-scale to 1 */
3518 paropt[nparopt].code = PARC_FIX ;
3519 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3520
3521 paropt[nparopt].np = 11 ; /* fix last shear to 0 */
3522 paropt[nparopt].code = PARC_FIX ;
3523 paropt[nparopt].vb = 0.0 ; nparopt++ ;
3524
3525 smat = SMAT_YYY ; /* fix shear matrix to y-only */
3526 XYZ_warp = 2 ; iarg++ ; continue ;
3527 }
3528
3529 if( strcmp(argv[iarg],"-Zwarp") == 0 ){ /* 02 Oct 2006 */
3530 if( XYZ_warp > 0 ) ERROR_exit("only one use of -[XYZ]warp is allowed :-(");
3531 matorder = MATORDER_USD ; /* rotation after shear and scale */
3532
3533 paropt[nparopt].np = 6 ; /* fix x-scale to 1 */
3534 paropt[nparopt].code = PARC_FIX ;
3535 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3536
3537 paropt[nparopt].np = 7 ; /* fix y-scale to 1 */
3538 paropt[nparopt].code = PARC_FIX ;
3539 paropt[nparopt].vb = 1.0 ; nparopt++ ;
3540
3541 paropt[nparopt].np = 11 ; /* fix last shear to 0 */
3542 paropt[nparopt].code = PARC_FIX ;
3543 paropt[nparopt].vb = 0.0 ; nparopt++ ;
3544
3545 smat = SMAT_ZZZ ; /* fix shear matrix to x-only */
3546 XYZ_warp = 3 ; iarg++ ; continue ;
3547 }
3548
3549 /*-----*/
3550
3551 if( strcmp(argv[iarg],"-SDU") == 0 ){
3552 matorder = MATORDER_SDU ; iarg++ ; continue ;
3553 }
3554 if( strcmp(argv[iarg],"-SUD") == 0 ){
3555 matorder = MATORDER_SUD ; iarg++ ; continue ;
3556 }
3557 if( strcmp(argv[iarg],"-DSU") == 0 ){
3558 matorder = MATORDER_DSU ; iarg++ ; continue ;
3559 }
3560 if( strcmp(argv[iarg],"-DUS") == 0 ){
3561 matorder = MATORDER_DUS ; iarg++ ; continue ;
3562 }
3563 if( strcmp(argv[iarg],"-USD") == 0 ){
3564 matorder = MATORDER_USD ; iarg++ ; continue ;
3565 }
3566 if( strcmp(argv[iarg],"-UDS") == 0 ){
3567 matorder = MATORDER_UDS ; iarg++ ; continue ;
3568 }
3569 if( strcmp(argv[iarg],"-ashift") == 0 ){
3570 dcode = DELTA_AFTER ; iarg++ ; continue ;
3571 }
3572 if( strcmp(argv[iarg],"-bshift") == 0 ){
3573 dcode = DELTA_BEFORE ; iarg++ ; continue ;
3574 }
3575 if( strcmp(argv[iarg],"-Slower") == 0 ){
3576 smat = SMAT_LOWER ; iarg++ ; continue ;
3577 }
3578 if( strcmp(argv[iarg],"-Supper") == 0 ){
3579 smat = SMAT_UPPER ; iarg++ ; continue ;
3580 }
3581
3582 /*-----*/
3583
3584 if( strcasecmp(argv[iarg],"-Lunch") == 0 ){ /* 23 Sep 2020 */
3585 WARNING_message("There is no free '%s' with AFNI :(",argv[iarg]) ;
3586 iarg++ ; continue ;
3587 }
3588 if( strcasecmp(argv[iarg],"-Faster") == 0 ){
3589 WARNING_message("You want '%s'? We already use OpenMP! Give me a break.",argv[iarg]) ;
3590 iarg++ ; continue ;
3591 }
3592
3593 /*-----*/
3594
3595 if( strcmp(argv[iarg],"-blok") == 0 ){
3596 int ia=0 ;
3597 if( ++iarg >= argc ) ERROR_exit("Need argument after -blok :-(") ;
3598 if( strncmp(argv[iarg],"SPHERE(",7) == 0 ){
3599 ia = 7 ; bloktype = GA_BLOK_BALL ;
3600 } else if( strncmp(argv[iarg],"BALL(",5) == 0 ){
3601 ia = 5 ; bloktype = GA_BLOK_BALL ;
3602 } else if( strncmp(argv[iarg],"RECT(",5) == 0 ){
3603 ia = 5 ; bloktype = GA_BLOK_CUBE ;
3604 } else if( strncmp(argv[iarg],"CUBE(",5) == 0 ){
3605 ia = 5 ; bloktype = GA_BLOK_CUBE ;
3606 } else if( strncmp(argv[iarg],"RHDD(",5) == 0 ){
3607 ia = 5 ; bloktype = GA_BLOK_RHDD ;
3608 } else if( strncmp(argv[iarg],"TOHD(",5) == 0 ){
3609 ia = 5 ; bloktype = GA_BLOK_TOHD ;
3610 } else {
3611 ERROR_exit("Illegal argument after -blok :-(") ;
3612 }
3613 blokrad = (float)strtod(argv[iarg]+ia,NULL) ;
3614 iarg++ ; continue ;
3615 }
3616
3617 /*-----*/
3618
3619 ERROR_message("Unknown and Illegal option '%s' :-( :-( :-(",argv[iarg]) ;
3620 suggest_best_prog_option(argv[0], argv[iarg]);
3621 exit(1);
3622
3623 } /*----------------- end of loop over command line args -----------------*/
3624
3625 if( iarg < argc ) /* oopsie [should never happen, I hope] */
3626 WARNING_message("Processing command line options stopped at '%s'",argv[iarg]);
3627
3628 /*---------------------------------------------------------------*/
3629 /*--- check inputs for validity, consistency, and moral fibre ---*/
3630 /*---------------------------------------------------------------*/
3631
3632 /** open target/source from last argument, if not already open **/
3633
3634 if( dset_targ == NULL ){
3635 if( iarg >= argc )
3636 ERROR_exit("no source datset on command line!?") ;
3637 dset_targ = THD_open_dataset( argv[iarg] ) ;
3638 if( dset_targ == NULL )
3639 ERROR_exit("Can't open source dataset '%s'",argv[iarg]) ;
3640 }
3641
3642 /* speak to the user? */
3643
3644 if( verb ){
3645 INFO_message("Source dataset: %s",DSET_HEADNAME(dset_targ)) ;
3646 INFO_message("Base dataset: %s",
3647 (dset_base != NULL) ? DSET_HEADNAME(dset_base) : "(not given)" ) ;
3648 }
3649
3650 /* set random seed, if not set already */
3651 if( seed == 0 ) seed = (long)time(NULL)+(long)getpid() ;
3652 srand48(seed) ;
3653
3654 /* ls/lpc/lpa: turn -autoweight on unless it was forced off. */
3655 /* [changed from pure warning to current status 23 Jan 2017] */
3656
3657 if( !wtspecified &&
3658 ( meth_code == GA_MATCH_PEARSON_SCALAR ||
3659 meth_code == GA_MATCH_PEARSON_LOCALS ||
3660 meth_code == GA_MATCH_PEARSON_LOCALA ||
3661 meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
3662 meth_code == GA_MATCH_LPA_MICHO_SCALAR ) ){
3663 wtspecified = auto_weight = 1 ;
3664 WARNING_message(
3665 "Cost 'ls' or 'lpc' or 'lpa' ==> turning '-autoweight' on\n"
3666 " If you DO NOT want this to happen, then use one\n"
3667 " of '-autobox' or '-automask' or '-noauto'.\n" ) ;
3668 }
3669
3670 if( im_tmask == NULL && !auto_tmask &&
3671 ( meth_code == GA_MATCH_PEARSON_LOCALS ||
3672 meth_code == GA_MATCH_PEARSON_LOCALA ||
3673 meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
3674 meth_code == GA_MATCH_LPA_MICHO_SCALAR ) ){
3675 WARNING_message(
3676 "'-source_automask' is strongly recommended when using -lpc or -lpa") ;
3677 }
3678
3679 if( doing_2D &&
3680 ( meth_code == GA_MATCH_PEARSON_LOCALS ||
3681 meth_code == GA_MATCH_PEARSON_LOCALA ||
3682 meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
3683 meth_code == GA_MATCH_LPA_MICHO_SCALAR ) ){
3684 WARNING_message(
3685 "-lpc or -lpa cost functionals do NOT work well with 2D images :(") ;
3686 }
3687
3688 /*--- set histogram mode (for computing -hel, -mi, -cr, etc) ---*/
3689
3690 if( !hist_setbyuser ){ /* 25 Jul 2007 */
3691 switch( meth_code ){
3692 case GA_MATCH_PEARSON_LOCALS:
3693 case GA_MATCH_PEARSON_LOCALA:
3694 case GA_MATCH_SPEARMAN_SCALAR:
3695 case GA_MATCH_PEARSON_SCALAR:
3696 hist_mode = (do_allcost || meth_check_count) ? GA_HIST_CLEQWD : 0 ;
3697 break ;
3698
3699 default:
3700 hist_mode = GA_HIST_CLEQWD ;
3701 break ;
3702 }
3703 }
3704
3705 /*--- if the user wants to see all cost functional values ---*/
3706
3707 if( do_allcost < 0 && prefix != NULL ){ /* 19 Sep 2007 */
3708 prefix = NULL ;
3709 WARNING_message("-allcostX means -prefix is ignored :-(") ;
3710 }
3711 if( do_allcost < 0 && param_save_1D != NULL ){
3712 param_save_1D = NULL ;
3713 WARNING_message("-allcostX means -1Dparam_save is ignored :-(") ;
3714 }
3715 if( do_allcost < 0 && matrix_save_1D != NULL ){
3716 matrix_save_1D = NULL ;
3717 WARNING_message("-allcostX means -1Dmatrix_save is ignored :-(") ;
3718 }
3719
3720 /*--- I don't think anyone has every used this option ---*/
3721
3722 if( warp_freeze ) twofirst = 1 ; /* 10 Oct 2006 */
3723
3724 #ifdef ALLOW_NWARP /*********************************************************/
3725 /* applying an input transformation from -nwarp: obsolescent code */
3726
3727 if( apply_mode > 0 && nwarp_pass ){
3728 switch( nwarp_type ){
3729 default: ERROR_exit("Can't apply that nonlinear warp :-( [%d]",nwarp_type) ;
3730
3731 case WARP_BILINEAR:{
3732 if( apply_nx == NPBIL+4 ){
3733 apply_mode = APPLY_BILIN ;
3734 INFO_message(
3735 "found %d param/row in param file '%s'; applying bilinear warp",
3736 apply_nx , apply_1D) ;
3737 } else {
3738 ERROR_exit(
3739 "found %d param/row in param file '%s'; not right for bilinear warp",
3740 apply_nx , apply_1D) ;
3741 }
3742 }
3743 break ;
3744
3745 case WARP_CUBIC:{
3746 if( apply_nx == NPCUB+4 ){
3747 apply_mode = APPLY_CUBIC ;
3748 INFO_message(
3749 "found %d param/row in param file '%s'; applying cubic/poly3 warp",
3750 apply_nx , apply_1D) ;
3751 } else {
3752 ERROR_exit(
3753 "found %d param/row in param file '%s'; not right for cubic/poly3 warp",
3754 apply_nx , apply_1D) ;
3755 }
3756 }
3757 break ;
3758
3759 case WARP_QUINT:{
3760 if( apply_nx == NPQUINT+4 ){
3761 apply_mode = APPLY_QUINT ;
3762 INFO_message(
3763 "found %d param/row in param file '%s'; applying quintic/poly5 warp",
3764 apply_nx , apply_1D) ;
3765 } else {
3766 ERROR_exit(
3767 "found %d param/row in param file '%s'; not right for quintic/poly5 warp",
3768 apply_nx , apply_1D) ;
3769 }
3770 }
3771 break ;
3772
3773 case WARP_HEPT:{
3774 if( apply_nx == NPHEPT+4 ){
3775 apply_mode = APPLY_HEPT ;
3776 INFO_message(
3777 "found %d param/row in param file '%s'; applying heptic/poly7 warp",
3778 apply_nx , apply_1D) ;
3779 } else {
3780 ERROR_exit(
3781 "found %d param/row in param file '%s'; not right for heptic/poly7 warp",
3782 apply_nx , apply_1D) ;
3783 }
3784 }
3785 break ;
3786
3787 case WARP_NONI:{
3788 if( apply_nx == NPNONI+4 ){
3789 apply_mode = APPLY_NONI ;
3790 INFO_message(
3791 "found %d param/row in param file '%s'; applying nonic/poly9 warp",
3792 apply_nx , apply_1D) ;
3793 } else {
3794 ERROR_exit(
3795 "found %d param/row in param file '%s'; not right for nonic/poly9 warp",
3796 apply_nx , apply_1D) ;
3797 }
3798 }
3799 break ;
3800 } /* end of switch on nwarp_type */
3801 }
3802
3803 if( nwarp_pass && meth_check_count > 0 ){ /* 15 Dec 2010 */
3804 meth_check_count = 0 ;
3805 if( verb ) WARNING_message("-check disabled because of -nwarp") ;
3806 }
3807
3808 if( nwarp_pass && DSET_NVALS(dset_targ) > 1 )
3809 ERROR_exit("Can't use -nwarp on more than 1 sub-brick :-(") ;
3810
3811 if( nwarp_save_prefix != NULL && !nwarp_pass ){
3812 WARNING_message("Can't use -nwarp_save without -nwarp! :-(") ;
3813 nwarp_save_prefix = NULL ;
3814 }
3815 #endif /* ALLOW_NWARP */ /**************************************************/
3816
3817 /*--- tb_mast is set by -master SOURCE or -master BASE ---*/
3818
3819 switch( tb_mast ){ /* 19 Jul 2007 */
3820 case 1: dset_mast = dset_targ ; break ;
3821 case 2: dset_mast = dset_base ; break ;
3822 }
3823
3824 /*--- I don't think this is ever used, either ---*/
3825
3826 if( replace_base && DSET_NVALS(dset_targ) == 1 ) replace_base = 0 ;
3827
3828 /**--- check target dataset type ---**/
3829
3830 targ_kind = (int)DSET_BRICK_TYPE(dset_targ,0) ;
3831 if( targ_kind != MRI_float && targ_kind != MRI_short && targ_kind != MRI_byte ){
3832 INFO_message("source dataset %s has non-scalar data type '%s'",
3833 DSET_BRIKNAME(dset_targ) , MRI_TYPE_name[targ_kind] ) ;
3834 targ_kind = MRI_float ; /* for allineation purposes */
3835 /* The only vector type likely to be included is MRI_RGB; */
3836 /* this code will let the base image be aligned by its float */
3837 /* 'intensity' value, and then the final output be made by */
3838 /* applying the warp parameters to each component separately. */
3839 /* see mri_genalign_scalar_warpone() in mri_genalign.c; */
3840 /* and see mrilib.h for ISVECTIM() macro */
3841 targ_was_vector = !floatize && ISVECTIM(DSET_BRICK(dset_targ,0)) ;
3842 if( targ_was_vector ) targ_vector_kind = (int)DSET_BRICK_TYPE(dset_targ,0) ;
3843 }
3844 if( !DSET_datum_constant(dset_targ) )
3845 WARNING_message("source dataset %s does not have constant data type :-(",
3846 DSET_BRIKNAME(dset_targ)) ;
3847
3848 /*--- if applying a set of parameters, some options are contradictory ---*/
3849
3850 if( apply_1D != NULL ){
3851 if( prefix == NULL ) ERROR_exit("-1D*_apply also needs -prefix :-(") ;
3852 if( param_save_1D != NULL ) WARNING_message("-1D*_apply: Can't do -1Dparam_save") ;
3853 if( matrix_save_1D != NULL ) WARNING_message("-1D*_apply: Can't do -1Dmatrix_save") ;
3854 wtprefix = param_save_1D = matrix_save_1D = NULL ;
3855 zeropad = 0 ; auto_weight = auto_tmask = 0 ;
3856 if( dset_weig != NULL ){
3857 INFO_message("-1D*_apply: Ignoring weight dataset") ;
3858 DSET_delete(dset_weig) ; dset_weig=NULL ;
3859 }
3860 if( im_tmask != NULL ){
3861 INFO_message("-1D*_apply: Ignoring -source_mask") ;
3862 mri_free(im_tmask) ; im_tmask = NULL ;
3863 }
3864 if( dset_mast == NULL && dxyz_mast == 0.0 )
3865 INFO_message("You might want to use '-master' when using '-1D*_apply'") ;
3866 if( do_allcost ){ /* 19 Sep 2007 */
3867 do_allcost = 0 ;
3868 INFO_message("-allcost option illegal with -1D*_apply") ;
3869 }
3870 }
3871
3872 /**--- if no base input, target should have more than 1 sub-brick ---**/
3873 /** (I don't think anyone every uses this ability) **/
3874
3875 if( dset_base == NULL && apply_1D == NULL ){
3876 if( DSET_NVALS(dset_targ) == 1 )
3877 ERROR_exit("No base dataset AND source dataset has only 1 sub-brick") ;
3878
3879 WARNING_message("No -base dataset: using sub-brick #0 of source") ;
3880 skip_first = 1 ; /* don't register sub-brick #0 of targ to itself! */
3881 }
3882
3883 /**-- check on interpolation codes --**/
3884
3885 if( final_interp < 0 ) final_interp = interp_code ; /* not used */
3886
3887 if( got_interp && !got_final && apply_mode > 0 ){
3888 WARNING_message("you are applying a warp, AND you gave a -interp code;\n"
3889 " BUT for applying a warp to produce a dataset, it is\n"
3890 " always -final that matters ==> setting -final to %s" ,
3891 INTERP_methname(interp_code) ) ;
3892 final_interp = interp_code ;
3893 }
3894
3895 /* check for inconsistency between the interp_code for computing optimal
3896 parameters, and the final_interp for computing the output dataset */
3897
3898 if( (interp_code == MRI_NN && final_interp != MRI_NN) ||
3899 ( MRI_HIGHORDER(interp_code) && !MRI_HIGHORDER(final_interp) ) )
3900 WARNING_message("-interp is %s but -final is %s -- are you sure?",
3901 INTERP_methname(interp_code) , INTERP_methname(final_interp) ) ;
3902
3903 /*-- check if saving Pearson map is practicable [25 Jan 2021] --*/
3904
3905 if( do_save_pearson_map && dset_base == NULL ){
3906 WARNING_message(
3907 "-PearSave option disabled -- you did not input a base dataset :(") ;
3908 do_save_pearson_map = 0 ;
3909 }
3910
3911 /*--- load input datasets ---*/
3912
3913 if( verb ) INFO_message("Loading datasets into memory") ;
3914
3915 /*- target MUST be present -*/
3916
3917 DSET_load(dset_targ) ; CHECK_LOAD_ERROR(dset_targ) ;
3918 nx_targ = DSET_NX(dset_targ) ; dx_targ = fabsf(DSET_DX(dset_targ)) ;
3919 ny_targ = DSET_NY(dset_targ) ; dy_targ = fabsf(DSET_DY(dset_targ)) ;
3920 nz_targ = DSET_NZ(dset_targ) ; dz_targ = fabsf(DSET_DZ(dset_targ)) ;
3921
3922 nxyz_targ[0] = nx_targ; nxyz_targ[1] = ny_targ; nxyz_targ[2] = nz_targ;
3923 dxyz_targ[0] = dx_targ; dxyz_targ[1] = dy_targ; dxyz_targ[2] = dz_targ;
3924
3925 if( nx_targ < 2 || ny_targ < 2 )
3926 ERROR_exit("Source dataset has nx=%d ny=%d ???",nx_targ,ny_targ) ;
3927
3928 /*-- 07 Aug 2007: make target automask? --*/
3929
3930 if( im_tmask == NULL && apply_1D == NULL ){ /* 01 Mar 2010: (almost) always make this mask */
3931
3932 byte *mmm ; int ndil=auto_tdilation ;
3933 mmm = THD_automask( dset_targ ) ; /* output = just a byte array */
3934 if( mmm == NULL )
3935 ERROR_exit("Can't make -source_automask for some reason :-(") ;
3936 im_tmask = mri_new_vol_empty( nx_targ,ny_targ,nz_targ , MRI_byte ) ;
3937 mri_fix_data_pointer( mmm , im_tmask ) ; /* attach byte array to MRI_IMAGE struct */
3938 if( ndil > 0 ){
3939 for( ii=0 ; ii < ndil ; ii++ ){
3940 THD_mask_dilate ( nx_targ,ny_targ,nz_targ , mmm , 3, 2 ) ;
3941 THD_mask_fillin_once( nx_targ,ny_targ,nz_targ , mmm , 2 ) ;
3942 }
3943 }
3944 if( auto_tstring == NULL ){
3945 auto_tstring = (char *)malloc(sizeof(char)*32) ;
3946 sprintf(auto_tstring,"source_automask+%d",ndil) ;
3947 }
3948 ntmask = THD_countmask( im_tmask->nvox , mmm ) ;
3949 if( ntmask < 666 && auto_tmask )
3950 ERROR_exit("Too few (%d) voxels in %s :-(",ntmask,auto_tstring) ;
3951 if( verb > 1 )
3952 INFO_message("%d voxels in %s",ntmask,auto_tstring) ;
3953
3954 } else if( im_tmask != NULL ){ /*-- check -source_mask vs. target --*/
3955
3956 if( im_tmask->nx != nx_targ ||
3957 im_tmask->ny != ny_targ || im_tmask->nz != nz_targ )
3958 ERROR_exit("-source_mask and -source datasets "
3959 "have different dimensions! :-(\n"
3960 "Have: %d %d %d versus %d %d %d\n",
3961 im_tmask->nx, im_tmask->ny , im_tmask->nz,
3962 nx_targ, ny_targ, nz_targ) ;
3963 }
3964
3965 /*-- load base dataset if defined --*/
3966
3967 if( dset_base != NULL ){
3968
3969 DSET_load(dset_base) ; CHECK_LOAD_ERROR(dset_base) ;
3970 im_base = mri_scale_to_float( DSET_BRICK_FACTOR(dset_base,0) ,
3971 DSET_BRICK(dset_base,0) ) ;
3972 if( im_base == NULL )
3973 ERROR_exit("Cannot extract float image from base dataset :(") ;
3974
3975 DSET_unload(dset_base) ;
3976 dx_base = fabsf(DSET_DX(dset_base)) ;
3977 dy_base = fabsf(DSET_DY(dset_base)) ;
3978 dz_base = fabsf(DSET_DZ(dset_base)) ;
3979 if( im_base->nx < 2 || im_base->ny < 2 )
3980 ERROR_exit("Base dataset has nx=%d ny=%d ???",im_base->nx,im_base->ny) ;
3981
3982 } else { /* no -base, so use target[0] as the base image */
3983
3984 if( apply_mode == 0 )
3985 INFO_message("no -base option ==> base is #0 sub-brick of source") ;
3986 im_base = mri_scale_to_float( DSET_BRICK_FACTOR(dset_targ,0) ,
3987 DSET_BRICK(dset_targ,0) ) ;
3988 if( im_base == NULL ) /* should not be possible */
3989 ERROR_exit("Cannot extract float image from source dataset :(") ;
3990 dx_base = dx_targ; dy_base = dy_targ; dz_base = dz_targ;
3991 if( do_cmass && apply_mode == 0 ){ /* 30 Jul 2007 */
3992 INFO_message("no base dataset ==> -cmass is disabled"); do_cmass = 0;
3993 }
3994
3995 }
3996 nx_base = im_base->nx ;
3997 ny_base = im_base->ny ; nxy_base = nx_base *ny_base ;
3998 nz_base = im_base->nz ; nvox_base = nxy_base*nz_base ;
3999
4000 /*--- check base dimensions for reasonability ---*/
4001
4002 if( nx_base < 9 || ny_base < 9 )
4003 ERROR_exit("Base volume i- and/or j-axis dimension < 9") ;
4004
4005 /*-- check if there is some substance to the base image --*/
4006
4007 if( !APPLYING ){ /* 13 Mar 2017 */
4008 nnz = mri_nonzero_count(im_base) ;
4009 if( nnz < 100 )
4010 ERROR_exit("3dAllineate fails :: base image has only %d nonzero voxel%s (< 100)",
4011 nnz , (nnz==1) ? "\0" : "s" ) ;
4012 }
4013
4014 /*-- check if 3D volume of source is significantly bigger than
4015 volume of base; if so, use the max number of -twbest trials --*/
4016
4017 if( twopass && tbest < PARAM_MAXTRIAL ){ /* 29 May 2021 */
4018 float vol_targ , vol_base ;
4019 vol_targ = (nx_targ*dx_targ)*(ny_targ*dy_targ)*(nz_targ*dz_targ) ;
4020 vol_base = (nx_base*dx_base)*(ny_base*dy_base)*(nz_base*dz_base) ;
4021 if( vol_targ > 1.3f*vol_base || big_cmass ){
4022 tbest = PARAM_MAXTRIAL ;
4023 if( verb > 1 )
4024 INFO_message("largeness ==> set -twobest %d",PARAM_MAXTRIAL) ;
4025 }
4026 }
4027
4028 /*-- is this 2D image registration? --*/
4029
4030 doing_2D = (nz_base == 1) ; /* 28 Apr 2020 */
4031
4032 /*--- Check emask for OK-ness [14 Feb 2013] ---*/
4033
4034 if( apply_mode != 0 && emask != NULL ){
4035 INFO_message("-emask is ignored in apply mode") ;
4036 KILL_bytevec(emask) ;
4037 }
4038 if( emask != NULL && emask->nar != nvox_base )
4039 ERROR_exit("-emask doesn't match base dataset dimensions :-(") ;
4040
4041 /*-- largest grid spacing in either dataset --*/
4042
4043 dxyz_top = dx_base ;
4044 dxyz_top = MAX(dxyz_top,dy_base) ; dxyz_top = MAX(dxyz_top,dz_base) ;
4045 dxyz_top = MAX(dxyz_top,dx_targ) ;
4046 dxyz_top = MAX(dxyz_top,dy_targ) ; dxyz_top = MAX(dxyz_top,dz_targ) ;
4047
4048 /*-- crop off negative voxels in the base? --*/
4049
4050 if( do_zclip ){
4051 float *bar = MRI_FLOAT_PTR(im_base) ;
4052 for( ii=0 ; ii < nvox_base ; ii++ ) if( bar[ii] < 0.0f ) bar[ii] = 0.0f ;
4053 }
4054
4055 /*-- x flip base for mirror check? [18 Jun 2019] --*/
4056
4057 if( do_xflip_bset ){
4058 int nbx=im_base->nx, nby=im_base->ny, nbz=im_base->nz, nbyz = nby*nbz, ii,kk,koff,knnn ;
4059 float *bimar = MRI_FLOAT_PTR(im_base) , *tar ;
4060 tar = (float *)malloc(sizeof(float)*nbx) ;
4061 for( kk=0 ; kk < nbyz ; kk++ ){
4062 koff = kk*nbx ; knnn = koff+nbx-1 ;
4063 for( ii=0 ; ii < nbx ; ii++ ) tar[ii] = bimar[ii+koff] ; /* get row */
4064 for( ii=0 ; ii < nbx ; ii++ ) bimar[knnn-ii] = tar[ii] ; /* flip it */
4065 }
4066 free(tar) ;
4067 INFO_message("3dAllineate: x-flipped base dataset") ;
4068 }
4069
4070 /*----- find the autobbox, and setup zero-padding -----*/
4071
4072 #undef MPAD
4073 #define MPAD 8
4074 if( zeropad ){
4075 float cv , *qar ; int xpad,ypad,zpad,mpad ;
4076 cv = 0.33f * THD_cliplevel(im_base,0.33f) ; /* set threshold */
4077 qim = mri_copy(im_base); qar = MRI_FLOAT_PTR(qim);
4078 for( ii=0 ; ii < qim->nvox ; ii++ ) if( qar[ii] < cv ) qar[ii] = 0.0f ;
4079
4080 /* make padding depend on dataset size [22 May 2019] */
4081
4082 xpad = nx_base/8; ypad = ny_base/8; zpad = nz_base/8; mpad = MPAD;
4083 if( mpad < xpad ) mpad = xpad ;
4084 if( mpad < ypad ) mpad = ypad ;
4085 if( mpad < zpad ) mpad = zpad ;
4086
4087 /* find edges of box that contain supra-threshold contents */
4088
4089 MRI_autobbox( qim, &pad_xm,&pad_xp, &pad_ym,&pad_yp, &pad_zm,&pad_zp ) ;
4090 mri_free(qim) ;
4091 #if 0
4092 if( verb > 1 ){
4093 INFO_message("bbox: xbot=%3d xtop=%3d nx=%3d",pad_xm,pad_xp,nx_base);
4094 INFO_message(" : ybot=%3d ytop=%3d ny=%3d",pad_ym,pad_yp,ny_base);
4095 if( nz_base > 1 )
4096 INFO_message(" : zbot=%3d ztop=%3d nz=%3d",pad_zm,pad_zp,nz_base);
4097 }
4098 #endif
4099
4100 /* compute padding so that at least mpad all-zero slices on each face */
4101
4102 pad_xm = mpad - pad_xm ; if( pad_xm < 0 ) pad_xm = 0 ;
4103 pad_ym = mpad - pad_ym ; if( pad_ym < 0 ) pad_ym = 0 ;
4104 pad_zm = mpad - pad_zm ; if( pad_zm < 0 ) pad_zm = 0 ;
4105 pad_xp = mpad - (nx_base-1 - pad_xp) ; if( pad_xp < 0 ) pad_xp = 0 ;
4106 pad_yp = mpad - (ny_base-1 - pad_yp) ; if( pad_yp < 0 ) pad_yp = 0 ;
4107 pad_zp = mpad - (nz_base-1 - pad_zp) ; if( pad_zp < 0 ) pad_zp = 0 ;
4108 if( doing_2D ){ pad_zm = pad_zp = 0 ; } /* don't z-pad 2D image! */
4109
4110 zeropad = (pad_xm > 0 || pad_xp > 0 ||
4111 pad_ym > 0 || pad_yp > 0 || pad_zm > 0 || pad_zp > 0) ;
4112
4113 if( verb > 1 && apply_mode == 0 ){
4114 if( zeropad ){
4115 if( pad_xm > 0 || pad_xp > 0 )
4116 INFO_message("Zero-pad: xbot=%d xtop=%d",pad_xm,pad_xp) ;
4117 if( pad_ym > 0 || pad_yp > 0 )
4118 INFO_message("Zero-pad: ybot=%d ytop=%d",pad_ym,pad_yp) ;
4119 if( pad_zm > 0 || pad_zp > 0 )
4120 INFO_message("Zero-pad: zbot=%d ztop=%d",pad_zm,pad_zp) ;
4121 } else {
4122 INFO_message("Zero-pad: not needed (plenty of internal padding)") ;
4123 }
4124 }
4125
4126 /* zeropad the base image at this point in spacetime? */
4127
4128 if( zeropad ){
4129 int nxold=nx_base , nyold=ny_base , nzold=nz_base ;
4130 qim = mri_zeropad_3D( pad_xm,pad_xp , pad_ym,pad_yp ,
4131 pad_zm,pad_zp , im_base ) ;
4132 mri_free(im_base) ; im_base = qim ;
4133 nx_base = im_base->nx ;
4134 ny_base = im_base->ny ; nxy_base = nx_base *ny_base ;
4135 nz_base = im_base->nz ; nvox_base = nxy_base*nz_base ;
4136
4137 if( emask != NULL ){ /* also zeropad emask [14 Feb 2013] */
4138 byte *ezp = (byte *)EDIT_volpad( pad_xm,pad_xp ,
4139 pad_ym,pad_yp ,
4140 pad_zm,pad_zp ,
4141 nxold,nyold,nzold ,
4142 MRI_byte , emask->ar ) ;
4143 if( ezp == NULL )
4144 ERROR_exit("zeropad of emask fails !?!") ; /* should not happen */
4145 free(emask->ar) ; emask->ar = ezp ; emask->nar = nvox_base ;
4146 }
4147
4148 }
4149 }
4150
4151 /*--- dimensions of the (possibly padded) base image ---*/
4152
4153 nxyz_base[0] = nx_base; nxyz_base[1] = ny_base; nxyz_base[2] = nz_base;
4154 dxyz_base[0] = dx_base; dxyz_base[1] = dy_base; dxyz_base[2] = dz_base;
4155
4156 /*-- orientation code for base --*/
4157
4158 { THD_3dim_dataset *qset = (dset_base != NULL) ? dset_base : dset_targ ;
4159 xx_code = ORIENT_xyzint[ qset->daxes->xxorient ] ;
4160 yy_code = ORIENT_xyzint[ qset->daxes->yyorient ] ;
4161 zz_code = ORIENT_xyzint[ qset->daxes->zzorient ] ;
4162 }
4163
4164 /*-- some 2D stuff --*/
4165
4166 if( doing_2D ){ /* 2D input image */
4167 char *tnam ;
4168 twodim_code = zz_code ;
4169 tnam = (twodim_code == 1) ? "sagittal" /* twodim_code = slice direction */
4170 :(twodim_code == 2) ? "coronal"
4171 :(twodim_code == 3) ? "axial"
4172 : "UNKNOWABLE" ;
4173 if( twodim_code < 1 || twodim_code > 3 )
4174 ERROR_exit("2D image: orientation is %s",tnam) ;
4175 else if( verb )
4176 ININFO_message("2D image registration: orientation is %s",tnam) ;
4177
4178 #ifdef ALLOW_NWARP /*********************************************************/
4179 if( nwarp_pass ){ nwarp_fixaff = nwarp_fixmotK = nwarp_fixdepK = 1 ; }
4180 #endif /* ALLOW_NWARP */ /**************************************************/
4181 }
4182
4183 #ifdef ALLOW_NWARP /*********************************************************/
4184 /* set parameter freeze directions for -nwarp_fix* now [07 Dec 2010] */
4185
4186 if( nwarp_pass ){
4187 if( twodim_code ){ nwarp_fixmotK = nwarp_fixdepK = 1 ; } /* 2D images: no out-of-plane stuff */
4188 if( nwarp_fixmotI ){
4189 switch( xx_code ){
4190 case 1: nwarp_fixmotX=1;break; case 2: nwarp_fixmotY=1;break; case 3: nwarp_fixmotZ=1;break;
4191 }
4192 }
4193 if( nwarp_fixmotJ ){
4194 switch( yy_code ){
4195 case 1: nwarp_fixmotX=1;break; case 2: nwarp_fixmotY=1;break; case 3: nwarp_fixmotZ=1;break;
4196 }
4197 }
4198 if( nwarp_fixmotK ){
4199 switch( zz_code ){
4200 case 1: nwarp_fixmotX=1;break; case 2: nwarp_fixmotY=1;break; case 3: nwarp_fixmotZ=1;break;
4201 }
4202 }
4203 if( nwarp_fixdepI ){
4204 switch( xx_code ){
4205 case 1: nwarp_fixdepX=1;break; case 2: nwarp_fixdepY=1;break; case 3: nwarp_fixdepZ=1;break;
4206 }
4207 }
4208 if( nwarp_fixdepJ ){
4209 switch( yy_code ){
4210 case 1: nwarp_fixdepX=1;break; case 2: nwarp_fixdepY=1;break; case 3: nwarp_fixdepZ=1;break;
4211 }
4212 }
4213 if( nwarp_fixdepK ){
4214 switch( zz_code ){
4215 case 1: nwarp_fixdepX=1;break; case 2: nwarp_fixdepY=1;break; case 3: nwarp_fixdepZ=1;break;
4216 }
4217 }
4218
4219 if( nwarp_fixmotX && nwarp_fixmotY && nwarp_fixmotZ )
4220 ERROR_exit("-nwarp_fixmot has frozen all nonlinear warping parameters :-(") ;
4221
4222 if( nwarp_fixdepX && nwarp_fixdepY && nwarp_fixdepZ )
4223 ERROR_exit("-nwarp_fixdep has frozen all nonlinear warping parameters :-(") ;
4224
4225 if( (nwarp_fixmotX || nwarp_fixmotY || nwarp_fixmotZ ||
4226 nwarp_fixdepX || nwarp_fixdepY || nwarp_fixdepZ ) &&
4227 !NONLINEAR_IS_POLY(nwarp_type) )
4228 ERROR_exit("-nwarp_fix... cannot be used with non-polynomial -nwarp types") ;
4229
4230 if( verb ){
4231 if( nwarp_fixmotX ) ININFO_message("-nwarp: X motions are frozen") ;
4232 if( nwarp_fixmotY ) ININFO_message("-nwarp: Y motions are frozen") ;
4233 if( nwarp_fixmotZ ) ININFO_message("-nwarp: Z motions are frozen") ;
4234 if( nwarp_fixdepX ) ININFO_message("-nwarp: X dependencies are frozen") ;
4235 if( nwarp_fixdepY ) ININFO_message("-nwarp: Y dependencies are frozen") ;
4236 if( nwarp_fixdepZ ) ININFO_message("-nwarp: Z dependencies are frozen") ;
4237 }
4238 }
4239 #endif /* ALLOW_NWARP */ /**************************************************/
4240
4241 /*----- check for base:target dimensionality mismatch -----*/
4242
4243 if( !doing_2D && nz_targ == 1 )
4244 ERROR_exit("Can't register 2D source into 3D base :-(") ;
4245
4246 if( doing_2D && nz_targ > 1 )
4247 ERROR_exit("Can't register 3D source onto 2D base :-(") ;
4248
4249 #ifdef ALLOW_NWARP /*********************************************************/
4250 if( doing_2D && nwarp_pass && !NONLINEAR_IS_POLY(nwarp_type) )
4251 ERROR_exit("Can't use non-polynomial -nwarp on 2D images :: see 3dQwarp") ;
4252 #endif /* ALLOW_NWARP */ /**************************************************/
4253
4254 /*----- load user-supplied weight dataset if defined -----*/
4255
4256 if( dset_weig != NULL ){
4257
4258 STATUS("load weight dataset") ;
4259 DSET_load(dset_weig) ; CHECK_LOAD_ERROR(dset_weig) ;
4260 im_weig = mri_scale_to_float( DSET_BRICK_FACTOR(dset_weig,0) ,
4261 DSET_BRICK(dset_weig,0) ) ;
4262 DSET_unload(dset_weig) ;
4263 if( im_weig == NULL )
4264 ERROR_exit("Cannot extract float image from weight dataset :(") ;
4265
4266 /* zeropad weight to match base? */
4267
4268 if( zeropad ){
4269 STATUS("zeropad weight dataset") ;
4270 qim = mri_zeropad_3D( pad_xm,pad_xp , pad_ym,pad_yp ,
4271 pad_zm,pad_zp , im_weig ) ;
4272 mri_free(im_weig) ; im_weig = qim ;
4273 }
4274 if( im_weig->nx != nx_base ||
4275 im_weig->ny != ny_base || im_weig->nz != nz_base )
4276 ERROR_exit("-weight and base volumes don't match in 3D grid dimensions :-(") ;
4277
4278 /*-- convert to 0..1 range [23 Mar 2017] --*/
4279 { float clip=0.0f, *wf=MRI_FLOAT_PTR(im_weig); int ii,nxyz=im_weig->nvox;
4280 for( ii=0 ; ii < nxyz ; ii++ ) if( wf[ii] > clip ) clip = wf[ii] ;
4281 if( clip == 0.0f )
4282 ERROR_exit("Input -weight is never positive!") ;
4283 clip = 1.0f / clip ;
4284 for( ii=0 ; ii < nxyz ; ii++ ) wf[ii] *= clip ;
4285 }
4286 /*-----------------------------------------------*/
4287 } else if( auto_weight ){ /* manufacture weight from base = the USUAL case */
4288
4289 if( meth_noweight[meth_code-1] && auto_weight == 1 && auto_wclip == 0.0f ){
4290 WARNING_message("Cost function '%s' ('%s') uses -automask NOT -autoweight",
4291 meth_longname[meth_code-1] , meth_shortname[meth_code-1] ) ;
4292 auto_weight = 2 ;
4293 } else if( verb > 1 ){
4294 INFO_message("Computing %s",auto_string) ;
4295 }
4296 if( verb > 1 ) ctim = COX_cpu_time() ;
4297 /*-- here is where the default weight volume is created --*/
4298 im_weig = mri_weightize(im_base,auto_weight,auto_dilation,auto_wclip,auto_wpow) ;
4299 if( verb > 1 ) INFO_message("%s net CPU time = %.1f s" ,
4300 auto_string , COX_cpu_time()-ctim ) ;
4301 }
4302
4303 /*--- Apply the emask to the weight volume [14 Feb 2013] ---*/
4304
4305 if( emask != NULL ){
4306 float *war ; byte *ear=emask->ar ; int near=0 ;
4307
4308 if( im_weig == NULL ){ /* no weight volume ==> make one up */
4309 im_weig = mri_new_conforming(im_base,MRI_float) ; /* all zero */
4310 war = MRI_FLOAT_PTR(im_weig) ;
4311 for( ii=0 ; ii < nvox_base ; ii++ ){
4312 if( ear[ii] == 0 ) war[ii] = 1.0f ; else near++ ;
4313 }
4314
4315 } else { /* edit existing weight volume */
4316 war = MRI_FLOAT_PTR(im_weig) ;
4317 for( ii=0 ; ii < nvox_base ; ii++ ){
4318 if( ear[ii] != 0 && war[ii] != 0.0f ){ war[ii] = 0.0f ; near++ ; }
4319 }
4320
4321 }
4322 if( verb ) INFO_message("-emask excludes %d voxels from weight/mask",near) ;
4323 KILL_bytevec(emask) ;
4324 }
4325
4326 /*-- also, make a binary mask from the weight (not used much, if at all) --*/
4327
4328 if( im_weig != NULL ){
4329 float *wf = MRI_FLOAT_PTR(im_weig) ;
4330 byte *mf ;
4331 im_mask = mri_new_conforming(im_weig,MRI_byte) ;
4332 mf = MRI_BYTE_PTR(im_mask) ;
4333 for( ii=0 ; ii < im_mask->nvox ; ii++ ) mf[ii] = (wf[ii] > 0.0f) ;
4334 nmask = THD_countmask(im_mask->nvox,mf) ;
4335 if( verb > 1 ) INFO_message("%d voxels [%.1f%%] in weight mask",
4336 nmask, 100.0*nmask/(float)im_mask->nvox ) ;
4337 if( !APPLYING && nmask < 100 )
4338 ERROR_exit("3dAllineate fails: not enough voxels in weight mask") ;
4339
4340 } else {
4341 nmask = nvox_base ; /* the universal 'mask' */
4342 }
4343 if( usetemp ) mri_purge(im_mask) ;
4344
4345 /*--- save weight into a dataset? [mostly for "fun"] ---*/
4346
4347 if( wtprefix != NULL && im_weig != NULL ){
4348 THD_3dim_dataset *wset ;
4349 wset = EDIT_empty_copy( (dset_base!=NULL) ? dset_base : dset_targ ) ;
4350 EDIT_dset_items( wset ,
4351 ADN_prefix , wtprefix ,
4352 ADN_nvals , 1 ,
4353 ADN_ntt , 0 ,
4354 ADN_datum_all , MRI_float ,
4355 ADN_none ) ;
4356 EDIT_BRICK_FACTOR(wset,0,0.0);
4357 if( zeropad ) qim = mri_zeropad_3D( -pad_xm,-pad_xp , -pad_ym,-pad_yp ,
4358 -pad_zm,-pad_zp , im_weig ) ;
4359 else qim = mri_copy(im_weig) ;
4360 EDIT_substitute_brick( wset, 00, MRI_float, MRI_FLOAT_PTR(qim) );
4361 mri_clear_data_pointer(qim) ; mri_free(qim) ;
4362 DSET_write(wset); if( verb ) WROTE_DSET(wset);
4363 DSET_delete(wset) ;
4364 }
4365
4366 /*--- initialize ntask, regardless 26 Aug 2008 [rickr] ---*/
4367 ntask = DSET_NVOX(dset_targ) ;
4368 ntask = (ntask < nmask) ? (int)sqrt(ntask*(double)nmask) : nmask ;
4369
4370 /*--- number of points to use for matching base to target ---*/
4371
4372 if( nmask_frac < 0 ){
4373 if( npt_match < 0 ) npt_match = (int)(-0.01f*npt_match*ntask) ;
4374 if( npt_match < 9999 ) npt_match = 9999 ;
4375 if( npt_match > ntask ) npt_match = ntask ;
4376 } else {
4377 npt_match = (int)(nmask_frac*(double)nmask);
4378 }
4379 if( verb > 1 && apply_mode == 0 )
4380 INFO_message("Number of points for matching = %d",npt_match) ;
4381
4382 /*----------------------------------------------------------*/
4383 /*---------- setup alignment structure parameters ----------*/
4384 /*- the stup struct controls much of the alignment process -*/
4385 /*-- see mrilib.h for definition of this masterful struct --*/
4386 /*----------------------------------------------------------*/
4387
4388 memset(&stup,0,sizeof(GA_setup)) ; /* NULL out */
4389
4390 stup.match_code = meth_code ;
4391 stup.usetemp = usetemp ; /* 20 Dec 2006 */
4392
4393 stup.hist_mode = hist_mode ; /* 08 May 2007 */
4394 stup.hist_param = hist_param ;
4395
4396 /*-- spatial coordinates: 'cmat' transforms from ijk to xyz --*/
4397 /*-- 'c' means coordinates are output --*/
4398
4399 if( !ISVALID_MAT44(dset_targ->daxes->ijk_to_dicom) )
4400 THD_daxes_to_mat44(dset_targ->daxes) ;
4401 stup.targ_cmat = DSET_CMAT(dset_targ,use_realaxes) ; /* source dset */
4402
4403 /*-- base coordinates are drawn from it's header, or are same as target --*/
4404
4405 if( dset_base != NULL ){ /* base dset coords */
4406
4407 float bdet , tdet ;
4408
4409 if( !ISVALID_MAT44(dset_base->daxes->ijk_to_dicom) )
4410 THD_daxes_to_mat44(dset_base->daxes) ;
4411 stup.base_cmat = DSET_CMAT(dset_base,use_realaxes) ;
4412
4413 /** check if handedness of source and base are the same; **/
4414 /** this check is purely advisory -- it is not a problem **/
4415
4416 bdet = MAT44_DET(stup.base_cmat) ; /* sign of determinant */
4417 tdet = MAT44_DET(stup.targ_cmat) ; /* determines handedness */
4418 if( bdet * tdet < 0.0f ){ /* AHA - opposite signs! */
4419 INFO_message("NOTE: base and source coordinate systems have different handedness") ;
4420 ININFO_message(
4421 " Orientations: base=%s handed (%c%c%c); source=%s handed (%c%c%c)" ,
4422 (bdet < 0.0f) ? "Left" : "Right" ,
4423 ORIENT_typestr[dset_base->daxes->xxorient][0] ,
4424 ORIENT_typestr[dset_base->daxes->yyorient][0] ,
4425 ORIENT_typestr[dset_base->daxes->zzorient][0] ,
4426 (tdet < 0.0f) ? "Left" : "Right" ,
4427 ORIENT_typestr[dset_targ->daxes->xxorient][0] ,
4428 ORIENT_typestr[dset_targ->daxes->yyorient][0] ,
4429 ORIENT_typestr[dset_targ->daxes->zzorient][0] ) ;
4430 ININFO_message(
4431 " - It is nothing to worry about: 3dAllineate aligns based on coordinates." ) ;
4432 ININFO_message(
4433 " - But it is always important to check the alignment visually to be sure." ) ;
4434 }
4435
4436 } else {
4437 stup.base_cmat = stup.targ_cmat ; /* base coords == source coords */
4438 }
4439
4440 /*----- for the local correlation methods:
4441 set the blok type (RHDD, etc), and the blok radius -----*/
4442
4443 stup.blokset = NULL ;
4444 if( METH_USES_BLOKS(meth_code) || do_allcost || do_save_pearson_map ){
4445 #ifdef USE_OLD_BLOK_DEFAULTS
4446 float mr = 1.23f * ( MAT44_COLNORM(stup.base_cmat,0)
4447 +MAT44_COLNORM(stup.base_cmat,1)
4448 +MAT44_COLNORM(stup.base_cmat,2) ) ;
4449 if( blokrad < mr ) blokrad = mr ;
4450 #else
4451 float idel = MAT44_COLNORM(stup.base_cmat,0) ; /* length of step in i */
4452 float jdel = MAT44_COLNORM(stup.base_cmat,1) ; /* length of step in j */
4453 float kdel = MAT44_COLNORM(stup.base_cmat,2) ; /* length of step in k */
4454 float vvv = idel*jdel*kdel ; /* volume of base voxel */
4455 /** compute blokrad so that 555 base voxels are contained in one blok **/
4456 blokrad = cbrtf( 555.0f * vvv / GA_BLOK_VOLFAC(bloktype) ) ;
4457 #endif
4458 stup.bloktype = bloktype ; stup.blokrad = blokrad ; stup.blokmin = 0 ;
4459 if( verb ) INFO_message("Local correlation: blok type = '%s(%g)'",
4460 GA_BLOK_STRING(bloktype) , blokrad ) ;
4461 }
4462
4463 /*-- setup for a matching functional that combines multiple methods --*/
4464
4465 if( meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
4466 meth_code == GA_MATCH_LPA_MICHO_SCALAR ){
4467 if( verb )
4468 INFO_message("%s parameters: hel=%.2f mi=%.2f nmi=%.2f crA=%.2f ov=%.2f %s",
4469 meth_shortname[meth_code-1] ,
4470 micho_hel , micho_mi , micho_nmi , micho_crA , micho_ov ,
4471 micho_zfinal ? "[to be zeroed at Final iteration]" : "\0" ) ;
4472 GA_setup_micho( micho_hel , micho_mi , micho_nmi , micho_crA , micho_ov ) ;
4473 if( micho_zfinal ) do_refinal = 1 ; /* this must be turned on for +ZZ */
4474 }
4475
4476 /*-- modify base_cmat to allow for zeropad? --*/
4477
4478 if( pad_xm > 0 || pad_ym > 0 || pad_zm > 0 )
4479 MAT44_EXTEND_IJK( stup.base_cmat , pad_xm,pad_ym,pad_zm ) ;
4480
4481 /*-- compute inverse of cmats (cmat = index-to-coord)
4482 which will give the coord-to-index transformations --*/
4483
4484 targ_cmat = stup.targ_cmat; targ_cmat_inv = MAT44_INV(targ_cmat); /* 23 Jul 2007 */
4485 base_cmat = stup.base_cmat; base_cmat_inv = MAT44_INV(base_cmat);
4486
4487 /*---------- define warp 'before' and 'after' matrices ----------*/
4488
4489 AL_setup_warp_coords( epi_targ,epi_fe,epi_pe,epi_se,
4490 nxyz_base, dxyz_base, stup.base_cmat,
4491 nxyz_targ, dxyz_targ, stup.targ_cmat ) ;
4492
4493 /*----------- define warp parameters and function -----------*/
4494 /* that is, the mapping from parameters to the matrix itself */
4495
4496 mri_genalign_affine_setup( matorder , dcode , smat ) ;
4497
4498 stup.wfunc = mri_genalign_affine ; /* warping function in mri_genalign.c */
4499 stup.wfunc_param = (GA_param *)calloc(12,sizeof(GA_param)) ;
4500
4501 #ifdef ALLOW_NWARP /*********************************************************/
4502 if( nwarp_pass && warp_code != WARP_AFFINE ){
4503 WARNING_message("Use of -nwarp ==> must allow all 12 affine parameters") ;
4504 warp_code = WARP_AFFINE ;
4505 }
4506 #endif /* ALLOW_NWARP */ /**************************************************/
4507
4508 /*--- how many parameters will be used for affine transformation (3D) ---*/
4509
4510 switch( warp_code ){
4511 case WARP_SHIFT: stup.wfunc_numpar = 3; strcpy(warp_code_string,"shift_only") ; break;
4512 case WARP_ROTATE: stup.wfunc_numpar = 6; strcpy(warp_code_string,"shift_rotate") ; break;
4513 case WARP_SCALE: stup.wfunc_numpar = 9; strcpy(warp_code_string,"shift_rotate_scale"); break;
4514 case WARP_AFFINE: stup.wfunc_numpar = 12; strcpy(warp_code_string,"affine_general") ; break;
4515 }
4516
4517 /*-- check if -1Dapply_param is giving us enough parameters for this warp --*/
4518
4519 if( apply_1D != NULL ){
4520 if( apply_mode == APPLY_PARAM && apply_nx < stup.wfunc_numpar )
4521 ERROR_exit(
4522 "-1Dparam_apply '%s': %d isn't enough parameters per row for desired warp",
4523 apply_1D,apply_nx);
4524
4525 if( apply_ny < DSET_NVALS(dset_targ) )
4526 WARNING_message(
4527 "-1D*_apply '%s': %d isn't enough rows for source dataset -- last row will repeat",
4528 apply_1D,apply_ny);
4529 }
4530
4531 /*------------------------------------------------------------------------*/
4532 /*--------- macro to set up control values for a given parameter ---------*/
4533
4534 #define DEFPAR(p,nm,bb,tt,id,dd,ll) \
4535 do{ stup.wfunc_param[p].min = (bb) ; \
4536 stup.wfunc_param[p].max = (tt) ; \
4537 stup.wfunc_param[p].delta = (dd) ; \
4538 stup.wfunc_param[p].toler = (ll) ; \
4539 stup.wfunc_param[p].ident = (id) ; \
4540 stup.wfunc_param[p].val_init = (id) ; \
4541 stup.wfunc_param[p].val_pinit= (id) ; \
4542 stup.wfunc_param[p].val_fixed= (id) ; \
4543 stup.wfunc_param[p].val_out = (id) ; \
4544 strcpy( stup.wfunc_param[p].name , (nm) ) ; \
4545 stup.wfunc_param[p].fixed = 0 ; \
4546 } while(0)
4547
4548 /*-- compute range of shifts allowed --*/
4549
4550 xxx = 0.321f * (nx_base-1) ; /* about 1/3 of base */
4551 yyy = 0.321f * (ny_base-1) ;
4552 zzz = 0.321f * (nz_base-1) ; xxx_m = yyy_m = zzz_m = 0.01f ;
4553 /* transform 8 corners of a cube from index space to DICOM */
4554 /* space, and then find largest coordinates that happen */
4555 for( ii=-1 ; ii <= 1 ; ii+=2 ){
4556 for( jj=-1 ; jj <= 1 ; jj+=2 ){
4557 for( kk=-1 ; kk <= 1 ; kk+=2 ){
4558 MAT33_VEC( base_cmat , (ii*xxx),(jj*yyy),(kk*zzz) ,
4559 xxx_p,yyy_p,zzz_p ) ;
4560 xxx_p = fabsf(xxx_p); yyy_p = fabsf(yyy_p); zzz_p = fabsf(zzz_p);
4561 xxx_m = MAX(xxx_m,xxx_p);
4562 yyy_m = MAX(yyy_m,yyy_p); zzz_m = MAX(zzz_m,zzz_p);
4563 }}}
4564 xxx = xxx_m ; yyy = yyy_m ; zzz = zzz_m ; /* largest coords seen */
4565
4566 /*-- 30 Jul 2007: center-of-mass sets range of shifts --*/
4567 /*-- 26 Feb 2020: always compute, maybe not use --*/
4568
4569 if( 1 || do_cmass ){
4570 float xtarg,ytarg,ztarg , xbase,ybase,zbase ;
4571
4572 mri_get_cmass_3D( im_base , &xc,&yc,&zc ) ; /* cmass in indexes */
4573 MAT44_VEC( base_cmat , xc,yc,zc , xbase,ybase,zbase ) ;
4574 if( verb > 1 )
4575 INFO_message("base center of mass = %.3f %.3f %.3f (index)",xc,yc,zc) ;
4576 im_targ = THD_median_brick( dset_targ ) ; /* if more than 1 source volume */
4577 mri_get_cmass_3D( im_targ , &xc,&yc,&zc ) ; mri_free(im_targ) ;
4578 if( verb > 1 )
4579 ININFO_message("source center of mass = %.3f %.3f %.3f (index)",xc,yc,zc) ;
4580 MAT44_VEC( targ_cmat , xc,yc,zc , xtarg,ytarg,ztarg ) ;
4581 /* center of mass shifts */
4582 xxc = xc = xtarg-xbase ; yyc = yc = ytarg-ybase ; zzc = zc = ztarg-zbase ;
4583 if( verb > 1 )
4584 ININFO_message("source-target CM = %.3f %.3f %.3f (xyz)",xc,yc,zc) ;
4585 if (do_cmass < 0) {
4586 /* try to figure what is OK, for partial coverage */
4587 if (fabs(xc) >= fabs(yc) && fabs(xc) >= fabs(zc)) {
4588 if ( fabs(xc) > 4.0 /* more than 4 voxels */
4589 && fabs(xc) > 2.0*fabs(yc) /* more than twice the 2nd */
4590 && fabs(xc) > 2.0*fabs(zc) /* more than twice the 3rd */) {
4591 xc = 0.0f;
4592 if( verb > 1 ) ININFO_message(" automatic -cmass disables x-shift") ;
4593 }
4594 } else if (fabs(yc) >= fabs(xc) && fabs(yc) >= fabs(zc)) {
4595 if ( fabs(yc) > 4.0 /* more than 4 voxels */
4596 && fabs(yc) > 2.0*fabs(xc) /* more than twice the 2nd */
4597 && fabs(yc) > 2.0*fabs(zc) /* more than twice the 3rd */) {
4598 yc = 0.0f;
4599 if( verb > 1 ) ININFO_message(" automatic -cmass disables y-shift") ;
4600 }
4601 } else if (fabs(zc) >= fabs(xc) && fabs(zc) >= fabs(yc)) {
4602 if ( fabs(zc) > 4.0 /* more than 4 voxels */
4603 && fabs(zc) > 2.0*fabs(xc) /* more than twice the 2nd */
4604 && fabs(zc) > 2.0*fabs(yc) /* more than twice the 3rd */) {
4605 zc = 0.0f;
4606 if( verb > 1 ) ININFO_message(" automatic -cmass disables z-shift") ;
4607 }
4608 }
4609 } else { /* check do_cmass as a binary mask for which coords to use */
4610 if( (do_cmass & 1) == 0 ) xc = 0.0f ;
4611 if( (do_cmass & 2) == 0 ) yc = 0.0f ;
4612 if( (do_cmass & 4) == 0 ) zc = 0.0f ;
4613 }
4614 if( do_cmass && verb > 1 && apply_mode == 0 ){
4615 ININFO_message("estimated center of mass shifts = %.3f %.3f %.3f",xc,yc,zc) ;
4616 }
4617 } else {
4618 xc = yc = zc = 0.0f ; /* this code is currently unreachable */
4619 }
4620
4621 /* WARNING message if unused cmass shifts are large compared to search range */
4622 /* large = 20-49% terribly large = 50+% */
4623
4624 if( 1 ){ /* 29 May 2021 */
4625 float rrr ;
4626 rrr = fabsf(xxc)/xxx ; CMbad += (rrr < 0.20f) ? 0 : (rrr < 0.5f) ? 1 : 100 ;
4627 rrr = fabsf(yyc)/yyy ; CMbad += (rrr < 0.20f) ? 0 : (rrr < 0.5f) ? 1 : 100 ;
4628 rrr = fabsf(zzc)/zzz ; CMbad += (rrr < 0.20f) ? 0 : (rrr < 0.5f) ? 1 : 100 ;
4629 big_cmass = (CMbad > 2) ;
4630 }
4631
4632 if( !do_cmass ){ /* 26 Feb 2020 */
4633 if( CMbad > 0 && CMbad < 100 ){
4634 WARNING_message("center of mass shifts (-cmass) are turned off, but would be large") ;
4635 WARNING_message(" - at least one is more than 20%% of search range") ;
4636 } else if( CMbad >= 100 ){
4637 WARNING_message("center of mass shifts (-cmass) are turned off, but would be TERRIBLY large!") ;
4638 WARNING_message(" - at least one is more than 50%% of search range") ;
4639 }
4640
4641 ININFO_message(" -cmass x y z shifts = %8.3f %8.3f %8.3f",xxc,yyc,zzc) ;
4642 ININFO_message(" shift search range is +/- = %8.3f %8.3f %8.3f",xxx,yyy,zzz) ;
4643 if( CMbad > 0 ){
4644 ININFO_message(" %7.1f%% %7.1f%% %7.1f%%",
4645 100.0f*fabsf(xxc)/xxx, 100.0f*fabsf(yyc)/yyy,100.0f*fabsf(zzc)/zzz ) ;
4646 }
4647
4648 xc = yc = zc = 0.0f ; /* pleonastic, to be safe */
4649 }
4650
4651 /*-- use a smaller than normal range for parameter search? --*/
4652
4653 if( do_small ){ xxx *= 0.5f ; yyy *= 0.5f ; zzz *= 0.5f ; }
4654 xxx_p = xc + xxx ; xxx_m = xc - xxx ;
4655 yyy_p = yc + yyy ; yyy_m = yc - yyy ;
4656 zzz_p = zc + zzz ; zzz_m = zc - zzz ;
4657
4658 if( do_cmass && verb > 1 && apply_mode == 0 )
4659 INFO_message("shift param auto-range: %.1f..%.1f %.1f..%.1f %.1f..%.1f",
4660 xxx_m,xxx_p , yyy_m,yyy_p , zzz_m,zzz_p ) ;
4661
4662 /*-- now define all 12 affine parameters in stup; not all may be used --*/
4663
4664 /* shifts = the first 3 */
4665
4666 DEFPAR( 0, "x-shift" , xxx_m , xxx_p , 0.0 , 0.0 , 0.0 ) ; /* mm */
4667 DEFPAR( 1, "y-shift" , yyy_m , yyy_p , 0.0 , 0.0 , 0.0 ) ;
4668 DEFPAR( 2, "z-shift" , zzz_m , zzz_p , 0.0 , 0.0 , 0.0 ) ;
4669 if( do_cmass ){ /* 31 Jul 2007 */
4670 if( nx_base > 1 ) stup.wfunc_param[0].val_pinit = xc ;
4671 if( ny_base > 1 ) stup.wfunc_param[1].val_pinit = yc ;
4672 if( nz_base > 1 ) stup.wfunc_param[2].val_pinit = zc ;
4673 }
4674
4675 /*-- now the other 9 affine parameters --*/
4676
4677 { float rval,sval ;
4678
4679 /*-- angles = the next 3 --*/
4680
4681 rval = (do_small) ? 15.0f : 30.0 ;
4682 DEFPAR( 3, "z-angle" , -rval , rval , 0.0 , 0.0 , 0.0 ) ; /* degrees */
4683 DEFPAR( 4, "x-angle" , -rval , rval , 0.0 , 0.0 , 0.0 ) ;
4684 DEFPAR( 5, "y-angle" , -rval , rval , 0.0 , 0.0 , 0.0 ) ;
4685
4686 /*-- scales = the next 3 --*/
4687
4688 rval = (do_small) ? 0.85f : 0.711f ; sval = 1.0f / rval ;
4689 DEFPAR( 6, "x-scale" , rval , sval , 1.0 , 0.0 , 0.0 ) ; /* identity */
4690 DEFPAR( 7, "y-scale" , rval , sval , 1.0 , 0.0 , 0.0 ) ; /* == 1.0 */
4691 DEFPAR( 8, "z-scale" , rval , sval , 1.0 , 0.0 , 0.0 ) ;
4692
4693 /* shears = the final 3:
4694 The code below (for shear params) was modified 16 Jul 2014, to
4695 correct the labels (per user Mingbo) for the various EPI/FPS cases;
4696 see the usage of the 'a', 'b', 'c' parameters in defining the shear
4697 matrix 'ss' in function GA_setup_affine() in file mri_genalign.c. */
4698
4699 { char *alab , *blab , *clab ;
4700 switch( smat ){
4701 default: alab = "y/x-shear" ; blab = "z/x-shear" ; clab = "z/y-shear" ; break ;
4702 case SMAT_XXX: alab = "y/x-shear" ; blab = "z/x-shear" ; clab = "unused" ; break ;
4703 case SMAT_YYY: alab = "y/x-shear" ; blab = "z/y-shear" ; clab = "unused" ; break ;
4704 case SMAT_ZZZ: alab = "z/x-shear" ; blab = "z/y-shear" ; clab = "unused" ; break ;
4705 }
4706 rval = (do_small) ? 0.0555f : 0.1111f ;
4707 DEFPAR( 9, alab , -rval , rval , 0.0 , 0.0 , 0.0 ) ;
4708 DEFPAR( 10, blab , -rval , rval , 0.0 , 0.0 , 0.0 ) ;
4709 DEFPAR( 11, clab , -rval , rval , 0.0 , 0.0 , 0.0 ) ;
4710 }
4711 }
4712
4713 /*-- adjustments for 2D images --*/
4714
4715 if( twodim_code > 0 ){ /* 03 Dec 2010 */
4716 int i1=0,i2=0,i3=0,i4=0,i5=0,i6=0 ;
4717 switch( twodim_code ){ /* 2D images: freeze some parameters */
4718 case 3: /* axial slice == k-axis is I-S */
4719 i1=3 ; i2=5 ; i3=6 ; i4=9 ; i5=11 ; i6=12 ; break ;
4720 case 2: /* coronal slice == k-axis is A-P */
4721 i1=2 ; i2=4 ; i3=5 ; i4=8 ; i5=10 ; i6=12 ; break ;
4722 case 1: /* sagittal slice == k-axis is L-R */
4723 i1=1 ; i2=4 ; i3=6 ; i4=7 ; i5=10 ; i6=11 ; break ;
4724 }
4725 if( i1 > 0 ){
4726 stup.wfunc_param[i1-1].fixed = 2 ; /* fixed==2 means cannot be unfixed */
4727 stup.wfunc_param[i2-1].fixed = 2 ; /* fixed==1 is 'temporarily fixed' */
4728 stup.wfunc_param[i3-1].fixed = 2 ;
4729 stup.wfunc_param[i4-1].fixed = 2 ;
4730 stup.wfunc_param[i5-1].fixed = 2 ;
4731 stup.wfunc_param[i6-1].fixed = 2 ;
4732 if( verb && apply_mode == 0 )
4733 INFO_message("base dataset is 2D ==> froze out-of-plane affine parameters") ;
4734 }
4735 }
4736
4737 /*-- apply any parameter-altering user commands --*/
4738
4739 for( ii=0 ; ii < nparopt ; ii++ ){ /* loop over parameter-altering opts */
4740
4741 jj = paropt[ii].np ; /* which parameter? */
4742
4743 if( jj < stup.wfunc_numpar ){
4744
4745 if( stup.wfunc_param[jj].fixed && verb )
4746 ININFO_message("Altering fixed param#%d [%s]" ,
4747 jj+1 , stup.wfunc_param[jj].name ) ;
4748
4749 switch( paropt[ii].code ){ /* which option was given? */
4750
4751 case PARC_FIX: stup.wfunc_param[jj].fixed = 2 ; /* permanent fix */
4752 stup.wfunc_param[jj].val_fixed = paropt[ii].vb;
4753 if( verb > 1 )
4754 ININFO_message("Fix param#%d [%s] = %f",
4755 jj+1 , stup.wfunc_param[jj].name ,
4756 stup.wfunc_param[jj].val_fixed ) ;
4757 break;
4758
4759 case PARC_INI: stup.wfunc_param[jj].fixed = 0 ; /* initial val */
4760 stup.wfunc_param[jj].val_fixed =
4761 stup.wfunc_param[jj].val_init =
4762 stup.wfunc_param[jj].val_pinit = paropt[ii].vb;
4763 if( verb > 1 )
4764 ININFO_message("Init param#%d [%s] = %f",
4765 jj+1 , stup.wfunc_param[jj].name ,
4766 stup.wfunc_param[jj].val_pinit ) ;
4767 break;
4768
4769 case PARC_RAN:{ /* parameter search range to allow */
4770 float vb = paropt[ii].vb , vt = paropt[ii].vt ;
4771 if( do_cmass ){ /* 06 Aug 2007 */
4772 switch(jj){
4773 case 0: vb += xc ; vt += xc ; break ;
4774 case 1: vb += yc ; vt += yc ; break ;
4775 case 2: vb += zc ; vt += zc ; break ;
4776 }
4777 }
4778 /** stup.wfunc_param[jj].fixed = 0 ; **/
4779 stup.wfunc_param[jj].min = vb;
4780 stup.wfunc_param[jj].max = vt;
4781 if( verb > 1 )
4782 ININFO_message("Range param#%d [%s] = %f .. %f center = %f",
4783 jj+1 , stup.wfunc_param[jj].name ,
4784 stup.wfunc_param[jj].min ,
4785 stup.wfunc_param[jj].max ,
4786 0.5f*(stup.wfunc_param[jj].min+stup.wfunc_param[jj].max) );
4787 }
4788 break;
4789
4790 }
4791
4792 } else { /* this means the user is a doofus or a wizard */
4793 WARNING_message("Can't alter parameter #%d: out of range :-(",jj+1) ;
4794 }
4795
4796 } /* end of loop over parameter-altering options */
4797
4798 /*-- check to see if we have free parameters so we can actually do something --*/
4799
4800 for( ii=jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* count free params */
4801 if( !stup.wfunc_param[jj].fixed ) ii++ ;
4802 if( ii == 0 ) ERROR_exit("No free parameters for aligning datasets?! :-(") ;
4803 nparam_free = ii ;
4804 if( verb > 1 && apply_mode == 0 ) ININFO_message("%d free parameters",ii) ;
4805
4806 /*-- should have some free parameters in the first 6 if using twopass --*/
4807 /*----- [This situation has never arisen in all recorded history!] -----*/
4808
4809 if( twopass ){
4810 for( ii=jj=0 ; jj < stup.wfunc_numpar && jj < 6 ; jj++ )
4811 if( !stup.wfunc_param[jj].fixed ) ii++ ;
4812 if( ii == 0 ){
4813 WARNING_message("Disabling twopass because no free parameters in first 6!?");
4814 twopass = 0 ;
4815 }
4816 }
4817
4818 /*---- set normalized convergence radius for parameter search ----*/
4819
4820 /* get size of box we are dealing with */
4821 if( im_weig == NULL ){
4822 xsize = xxx = 0.5f * (nx_base-1) * dx_base ; /* no weight means all */
4823 ysize = yyy = 0.5f * (ny_base-1) * dy_base ; /* voxels are available */
4824 zsize = zzz = 0.5f * (nz_base-1) * dz_base ; /* for use in alignment */
4825 } else {
4826 int xm,xp , ym,yp , zm,zp ; /* otherwise, get size of the weight mask */
4827 MRI_autobbox_clust(0) ;
4828 MRI_autobbox( im_weig , &xm,&xp , &ym,&yp , &zm,&zp ) ;
4829 MRI_autobbox_clust(1) ;
4830 xsize = xxx = 0.5f * (xp-xm) * dx_base ;
4831 ysize = yyy = 0.5f * (yp-ym) * dy_base ;
4832 zsize = zzz = 0.5f * (zp-zm) * dz_base ;
4833 }
4834 /* convert box size to a single size */
4835 xxx = (nz_base > 1) ? cbrt(xxx*yyy*zzz) : sqrt(xxx*yyy) ;
4836 zzz = 0.01f ; /* smallest normalized value */
4837 for( jj=0 ; jj < 9 && jj < stup.wfunc_numpar ; jj++ ){ /* loop over params */
4838 if( stup.wfunc_param[jj].fixed ) continue ; /* except shears */
4839 siz = stup.wfunc_param[jj].max - stup.wfunc_param[jj].min ; /* par range */
4840 if( siz <= 0.0f ) continue ; /* skip this parameter */
4841
4842 /* normalization = conv_mm scaled by mm size of the param range since */
4843 /* optimization is done on unitless params scaled to [0..1] range */
4844 /* mm size of param range is */
4845 /* shift is in mm already, siz is in mm, */
4846 /* so conv_mm/siz is ratio of convergence */
4847 /* criterion to parameter range, which */
4848 /* in turn is the range of the unitless */
4849 /* [0..1] parameter -- got that? */
4850 /* angle is in degrees, so siz/57.3 is in radians */
4851 /* so xxx*siz/57.3 is in mm, */
4852 /* so conv_mm/(xx*siz/57.3) is ratio */
4853 /* scale is unitless, so siz*xxx is in mm */
4854 if( jj < 3 ) yyy = conv_mm / siz ; /* shift param */
4855 else if( jj < 6 ) yyy = 57.3f * conv_mm / (xxx*siz) ; /* angle param */
4856 else yyy = conv_mm / (xxx*siz) ; /* scale param */
4857 zzz = MIN(zzz,yyy) ; /* smallest scaled value found so far */
4858 }
4859 conv_rad = MIN(zzz,0.001f); conv_rad = MAX(conv_rad,0.000005f); /* limits */
4860 if( verb > 1 && apply_mode == 0 )
4861 INFO_message("Normalized (unitless) convergence radius = %.7f",conv_rad) ;
4862
4863 /*---- print parameter search ranges [10 Mar 2020] ----*/
4864
4865 if( verb > 1 ){
4866 INFO_message("Final parameter search ranges:") ;
4867 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
4868 if( !stup.wfunc_param[jj].fixed )
4869 ININFO_message(" %12s = %8.3f .. %8.3f",
4870 stup.wfunc_param[jj].name ,
4871 stup.wfunc_param[jj].min ,
4872 stup.wfunc_param[jj].max ) ;
4873 }
4874 }
4875
4876 #ifdef ALLOW_NWARP /*********************************************************/
4877 /*-- special case: 04 Apr 2008 --*/
4878
4879 switch( apply_mode ){ /* all this is for -nwarp inputs (obsolescent) */
4880 default: break ;
4881 case APPLY_BILIN: SETUP_BILINEAR_PARAMS ; break ;
4882 case APPLY_CUBIC: SETUP_CUBIC_PARAMS ; break ;
4883 case APPLY_QUINT: SETUP_QUINT_PARAMS ; break ;
4884 case APPLY_HEPT : SETUP_HEPT_PARAMS ; break ;
4885 case APPLY_NONI : SETUP_NONI_PARAMS ; break ;
4886 }
4887 #endif /* ALLOW_NWARP */ /**************************************************/
4888
4889 /*----------------------------------------------------*/
4890 /***--- creeping up on actually doing something! ---***/
4891 /*****------ create shell of output dataset ------*****/
4892
4893 if( prefix == NULL ){
4894 WARNING_message("No output dataset will be calculated") ;
4895 if( dxyz_mast > 0.0 )
4896 WARNING_message("-mast_dxyz %g option was meaningless :-(",dxyz_mast) ;
4897 } else {
4898 if( dset_mast == NULL ){ /* pick a master dataset to control output grid */
4899 if( dset_base != NULL ){
4900 if( verb ) INFO_message("master dataset for output = base") ;
4901 dset_mast = dset_base ;
4902 } else {
4903 if( verb ) INFO_message("master dataset for output = source") ;
4904 dset_mast = dset_targ ;
4905 }
4906 }
4907 if( dxyz_mast > 0.0 ){ /* 24 Jul 2007 -- alter grid size */
4908 THD_3dim_dataset *qset ;
4909 qset = r_new_resam_dset( dset_mast , NULL ,
4910 dxyz_mast,dxyz_mast,dxyz_mast ,
4911 NULL , RESAM_NN_TYPE , NULL , 0 , 0) ;
4912 if( qset != NULL ){
4913 dset_mast = qset ;
4914 THD_daxes_to_mat44(dset_mast->daxes) ;
4915 if( verb )
4916 INFO_message("changing output grid spacing to %.4f mm",dxyz_mast) ;
4917 }
4918 }
4919 if( !ISVALID_MAT44(dset_mast->daxes->ijk_to_dicom) ) /* make sure have */
4920 THD_daxes_to_mat44(dset_mast->daxes) ; /* index-to-DICOM matrix */
4921 mast_cmat = DSET_CMAT(dset_mast,use_realaxes) ;
4922 mast_cmat_inv = MAT44_INV(mast_cmat) ;
4923
4924 dset_out = EDIT_empty_copy( dset_mast ) ; /* create the output dataset! */
4925 EDIT_dset_items( dset_out , /* and patch it up */
4926 ADN_prefix , prefix ,
4927 ADN_nvals , DSET_NVALS(dset_targ) ,
4928 ADN_datum_all , MRI_float ,
4929 ADN_none ) ;
4930 /* do not let time info from master confuse things, we'll go back */
4931 /* to ntt > 1 later, if approprate [1 Jun 2020 rickr] */
4932 if( DSET_NUM_TIMES(dset_out) > 1 )
4933 EDIT_dset_items( dset_out , ADN_ntt , 1 , ADN_none ) ;
4934
4935 if( DSET_NUM_TIMES(dset_targ) > 1 )
4936 EDIT_dset_items( dset_out ,
4937 ADN_ntt , DSET_NVALS(dset_targ) ,
4938 ADN_ttdel , DSET_TR(dset_targ) ,
4939 ADN_tunits, UNITS_SEC_TYPE ,
4940 ADN_nsl , 0 ,
4941 ADN_none ) ;
4942 else
4943 EDIT_dset_items( dset_out ,
4944 ADN_func_type , ISANAT(dset_out) ? ANAT_BUCK_TYPE
4945 : FUNC_BUCK_TYPE ,
4946 ADN_none ) ;
4947
4948 /* copy brick info into output */
4949
4950 THD_copy_datablock_auxdata( dset_targ->dblk , dset_out->dblk ) ;
4951 if (!THD_copy_labeltable_atr( dset_out->dblk, dset_targ->dblk)) {
4952 WARNING_message("Failed trying to preserve labeltables");
4953 }
4954 for( kk=0 ; kk < DSET_NVALS(dset_out) ; kk++ )
4955 EDIT_BRICK_FACTOR(dset_out,kk,0.0);
4956
4957 tross_Copy_History( dset_targ , dset_out ) ; /* historic records */
4958 tross_Make_History( "3dAllineate" , argc,argv , dset_out ) ;
4959
4960 memset(&cmat_tout,0,sizeof(mat44)) ;
4961 memset(&cmat_bout,0,sizeof(mat44)) ;
4962 THD_daxes_to_mat44(dset_out->daxes) ; /* save coord transforms */
4963 cmat_tout = DSET_CMAT(dset_targ,use_realaxes) ;
4964 cmat_bout = DSET_CMAT(dset_out ,use_realaxes) ;
4965 nxout = DSET_NX(dset_out) ; dxout = fabsf(DSET_DX(dset_out)) ;
4966 nyout = DSET_NY(dset_out) ; dyout = fabsf(DSET_DY(dset_out)) ;
4967 nzout = DSET_NZ(dset_out) ; dzout = fabsf(DSET_DZ(dset_out)) ;
4968 nxyz_dout[0] = nxout; nxyz_dout[1] = nyout; nxyz_dout[2] = nzout;
4969 dxyz_dout[0] = dxout; dxyz_dout[1] = dyout; dxyz_dout[2] = dzout;
4970 }
4971
4972 #ifdef ALLOW_NWARP /*********************************************************/
4973 /*-- check if have dataset prefix for saving the 3D warp --*/
4974
4975 if( dset_out == NULL && nwarp_save_prefix != NULL ){
4976 WARNING_message("Can't use -nwarp_save without -prefix! :-(") ;
4977 nwarp_save_prefix = NULL ;
4978 }
4979 #endif /* ALLOW_NWARP */ /**************************************************/
4980
4981 /***~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~***/
4982 /***---------------------- start alignment process ----------------------***/
4983 /***~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~***/
4984
4985 #ifdef USE_OMP
4986 #pragma omp parallel
4987 {
4988 if( verb && omp_get_thread_num() == 0 )
4989 INFO_message("OpenMP thread count = %d",omp_get_num_threads()) ;
4990 }
4991 #endif
4992
4993 /*------- macros useful for verbosity -------*/
4994
4995 /* print all parameters from stup, on one line, with a string ss */
4996
4997 #undef PARDUMP /* xxx = field name in param */
4998 #define PARDUMP(ss,xxx) \
4999 do{ fprintf(stderr," + %s Parameters =",ss) ; \
5000 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){ \
5001 if( jj == 12 ) fprintf(stderr," |") ; \
5002 fprintf(stderr," %.4f",stup.wfunc_param[jj].xxx) ; \
5003 } \
5004 fprintf(stderr,"\n") ; \
5005 } while(0)
5006
5007 /* print the output parameters */
5008
5009 #undef PAROUT
5010 #define PAROUT(ss) PARDUMP(ss,val_out)
5011
5012 /* print the input parameters */
5013
5014 #undef PARINI
5015 #define PARINI(ss) PARDUMP(ss,val_init)
5016
5017 /* print a parameter vector, not from stup */
5018
5019 #undef PARVEC
5020 #define PARVEC(ss,vv) \
5021 do{ fprintf(stderr," + %s Parameters =",ss) ; \
5022 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) \
5023 fprintf(stderr," %.4f",vv[jj]) ; \
5024 fprintf(stderr,"\n") ; \
5025 } while(0)
5026
5027 /* copy parameter set 'xxx' from stup to the
5028 allpar array, for ease of use in calling other functions */
5029
5030 #undef PAR_CPY
5031 #define PAR_CPY(xxx) \
5032 do{ for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) \
5033 allpar[jj] = stup.wfunc_param[jj].xxx ; \
5034 } while(0)
5035
5036 /*----- for the beautiful final output of parameters, with commentary -----*/
5037
5038 #undef PARLIST
5039 #define PARLIST(ss,xxx) \
5040 do{ fprintf(stderr," + %s Parameters:\n ",ss) ; \
5041 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){ \
5042 if( jj > 0 && jj%3 == 0 ) fprintf(stderr,"\n "); \
5043 fprintf(stderr," %9s=%8.4f", \
5044 stup.wfunc_param[jj].name,stup.wfunc_param[jj].xxx) ; \
5045 if( jj == 2 ) \
5046 fprintf(stderr," ... enorm=%8.4f mm", \
5047 sqrt( stup.wfunc_param[0].xxx*stup.wfunc_param[0].xxx \
5048 +stup.wfunc_param[1].xxx*stup.wfunc_param[1].xxx \
5049 +stup.wfunc_param[2].xxx*stup.wfunc_param[2].xxx)); \
5050 else if( jj == 5 ) \
5051 fprintf(stderr," ... total=%8.4f deg", \
5052 total_rotation_degrees(stup.wfunc_param[3].xxx, \
5053 stup.wfunc_param[4].xxx, \
5054 stup.wfunc_param[5].xxx)); \
5055 else if( jj == 8 ){ \
5056 float fff = stup.wfunc_param[6].xxx \
5057 *stup.wfunc_param[7].xxx \
5058 *stup.wfunc_param[8].xxx ; \
5059 fprintf(stderr," ... vol3D=%8.4f=(%.4f)^3 %s", \
5060 fff , cbrt(fff) , \
5061 (fff < 0.97f ) \
5062 ? "[base bigger than source]" \
5063 :(fff > 1.03f ) \
5064 ? "[base smaller than source]" \
5065 : "[base about same as source]" ) ; \
5066 } \
5067 } \
5068 fprintf(stderr,"\n") ; \
5069 } while(0)
5070
5071 #undef PARNOUT
5072 #define PARNOUT(ss) PARLIST(ss,val_out) /* 30 Aug 2013 */
5073
5074 /*--------------------------- the Annunciation ---------------------------*/
5075
5076 if( do_allcost >= 0 && verb ){
5077 if( apply_1D == NULL )
5078 INFO_message("======= Allineation of %d sub-bricks using %s =======",
5079 DSET_NVALS(dset_targ) , meth_username[meth_code-1] ) ;
5080 else
5081 INFO_message("========== Applying transformation to %d sub-bricks ==========",
5082 DSET_NVALS(dset_targ) ) ;
5083 }
5084
5085 if( verb > 1 ) mri_genalign_verbose(verb-1) ; /* verbosity in mri_genalign.c */
5086
5087 /*-- array in which to save parameters for later waterboarding --*/
5088
5089 if( param_save_1D != NULL || apply_mode != APPLY_AFF12 )
5090 parsave = (float **)calloc(sizeof(float *),DSET_NVALS(dset_targ)) ;
5091
5092 #ifdef ALLOW_NWARP /*********************************************************/
5093 if( !NONLINEAR_APPLY(apply_mode) ){ /* 04 Apr 2008 */
5094 if( matrix_save_1D != NULL || apply_mode != APPLY_AFF12 )
5095 matsave = (mat44 * )calloc(sizeof(mat44),DSET_NVALS(dset_targ)) ; /* 23 Jul 2007 */
5096 }
5097 #else
5098 if( matrix_save_1D != NULL || apply_mode != APPLY_AFF12 )
5099 matsave = (mat44 * )calloc(sizeof(mat44),DSET_NVALS(dset_targ)) ; /* 23 Jul 2007 */
5100 #endif /* ALLOW_NWARP */ /**************************************************/
5101
5102 /*--- as you might guess, this is for saving joint histogram to a file ---*/
5103 /*--- which is only done by the excessively inquisitive type of users ---*/
5104 #undef SAVEHIST
5105 #define SAVEHIST(nnn,docc) \
5106 do{ int nbin ; float *xyc ; \
5107 if( docc ) (void)mri_genalign_scalar_cost( &stup , NULL ) ; \
5108 nbin = retrieve_2Dhist( &xyc ) ; \
5109 if( nbin > 0 && xyc != NULL ){ \
5110 char fname[256] ; MRI_IMAGE *fim ; double ftop ; \
5111 fim = mri_new(nbin,nbin,MRI_float); mri_fix_data_pointer(xyc,fim); \
5112 if( strstr(save_hist,"FF") == NULL ){ \
5113 ftop = mri_max(fim) ; qim = mri_to_byte_scl(255.4/ftop,0.0,fim) ; \
5114 mri_clear_data_pointer(fim); mri_free(fim); \
5115 fim = mri_flippo(MRI_ROT_90,0,qim); mri_free(qim); \
5116 sprintf(fname,"%s_%s_%04d.pgm",save_hist,nnn,kk) ; \
5117 mri_write_pnm(fname,fim); mri_free(fim); \
5118 } else { \
5119 qim = mri_flippo(MRI_ROT_90,0,fim); \
5120 mri_clear_data_pointer(fim); mri_free(fim); \
5121 sprintf(fname,"%s_%s_%04d.mri",save_hist,nnn,kk) ; \
5122 mri_write(fname,qim); mri_free(qim); \
5123 } \
5124 if( verb ) ININFO_message("- Saved histogram to %s",fname) ; \
5125 } \
5126 } while(0)
5127
5128 /***==================== loop over target sub-bricks ====================***/
5129
5130 im_bset = im_base ; /* base image for first loop */
5131 im_wset = im_weig ;
5132
5133 /** 3dUnifize the base image? [23 Dec 2016] **/
5134
5135 #ifdef ALLOW_UNIFIZE
5136 if( do_unifize_base && dset_base != NULL && nz_base > 5 && apply_1D == NULL ){
5137 THD_3dim_dataset *qset, *uset ;
5138 char *uuu, bname[32], uname[32] , cmd[1024] ;
5139 float *bar , urad ;
5140
5141 uuu = UNIQ_idcode_11() ;
5142 sprintf(uname,"UU.%s.nii",uuu) ;
5143 sprintf(bname,"BB.%s.nii",uuu) ;
5144
5145 qset = THD_image_to_dset(im_bset,bname) ;
5146 qset->dblk->diskptr->storage_mode = STORAGE_BY_NIFTI ;
5147 DSET_write(qset) ;
5148
5149 urad = 18.3f / cbrtf(dx_base*dy_base*dz_base) ;
5150 if( urad < 5.01f ) urad = 5.01f ;
5151 else if( urad > 23.3f ) urad = 23.3f ;
5152 sprintf(cmd,
5153 "3dUnifize -input %s -prefix %s -T2 -Urad %.2f -clfrac 0.333",
5154 bname , uname , urad ) ;
5155 INFO_message("About to do -unifize_base:\n %s",cmd) ;
5156 system(cmd) ;
5157 THD_delete_3dim_dataset(qset,True) ;
5158
5159 uset = THD_open_dataset(uname) ;
5160 if( uset == NULL ){
5161 WARNING_message("-unifize_base failed :(") ;
5162 } else {
5163 DSET_load(uset) ;
5164 if( !DSET_LOADED(uset) ){
5165 WARNING_message("-unifize_base did something weird :((") ;
5166 } else {
5167 im_bset = im_ubase = mri_copy(DSET_BRICK(uset,0)) ;
5168 }
5169 THD_delete_3dim_dataset(uset,True) ;
5170 }
5171 } /* end of -unifize_base */
5172 #endif /* ALLOW_UNIFIZE */
5173
5174 /*--- for filling the outside of the target mask with random crap ---*/
5175
5176 stup.ajmask_ranfill = 0 ; /* 02 Mar 2010: oops */
5177 if( im_tmask != NULL ){
5178 mri_genalign_set_targmask( im_tmask , &stup ) ; /* 07 Aug 2007 */
5179 mri_free(im_tmask) ; im_tmask = NULL ; /* is copied inside */
5180 if( fill_source_mask ) stup.ajmask_ranfill = 1 ; /* 01 Mar 2010 */
5181 }
5182
5183 /*--- for the overlap portion of lpc+ and lpa+ [latter no longer applies] ---*/
5184
5185 if( !APPLYING && micho_ov != 0.0 ){
5186 byte *mmm ; int ndil=auto_tdilation ; MRI_IMAGE *bsm ;
5187 mmm = mri_automask_image(im_base) ;
5188 if( mmm != NULL ){
5189 bsm = mri_new_vol_empty( nx_base,ny_base,nz_base , MRI_byte ) ;
5190 mri_fix_data_pointer( mmm , bsm ) ;
5191 if( ndil > 0 ){
5192 for( ii=0 ; ii < ndil ; ii++ ){
5193 THD_mask_dilate ( nx_base,ny_base,nz_base , mmm , 3, 2 ) ;
5194 THD_mask_fillin_once( nx_base,ny_base,nz_base , mmm , 2 ) ;
5195 }
5196 }
5197 mri_genalign_set_basemask( bsm , &stup ) ;
5198 mri_free(bsm) ;
5199 }
5200 }
5201
5202 MEMORY_CHECK("about to start alignment loop") ;
5203
5204 /*-- blurring in coarse pass must not be 0 if using BLOKs --*/
5205
5206 if( sm_rad == 0.0f &&
5207 ( meth_code == GA_MATCH_PEARSON_LOCALS ||
5208 meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
5209 meth_code == GA_MATCH_LPA_MICHO_SCALAR ||
5210 meth_code == GA_MATCH_PEARSON_LOCALA ) ) sm_rad = MAX(2.222f,dxyz_top) ;
5211
5212 /*-- lpa method gets more twobest checks [27 May 2021] --*/
5213
5214 if( METH_IS_LPA(meth_code) && tbest < DEFAULT_TBEST_LPA )
5215 tbest = DEFAULT_TBEST_LPA ;
5216
5217 /*----------- process the target dataset volumes, one at a time -----------*/
5218
5219 for( kk=0 ; kk < DSET_NVALS(dset_targ) ; kk++ ){ /** the sub-brick loop **/
5220
5221 stup.match_code = meth_code ; /* the cost functional code */
5222
5223 ZERO_MAT44(aff12_xyz) ; /* 23 Jul 2007: invalidate the matrix */
5224
5225 bfac = DSET_BRICK_FACTOR(dset_targ,kk) ; /* sub-brick scale factor [14 Oct 2008] */
5226
5227 skipped = 0 ;
5228 if( kk == 0 && skip_first ){ /* skip first image since it == im_base */
5229
5230 if( verb )
5231 INFO_message("========= Skipping sub-brick #0: it's also base image =========");
5232 DSET_unload_one(dset_targ,0) ;
5233
5234 /*-- load parameters with identity transform --*/
5235
5236 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* for -1Dfile output */
5237 stup.wfunc_param[jj].val_out = stup.wfunc_param[jj].ident ;
5238
5239 /*-- load aff12_xyz matrix with identity transform [23 Jul 2007] --*/
5240
5241 LOAD_DIAG_MAT44(aff12_xyz,1.0f,1.0f,1.0f) ;
5242 skipped = 1 ; goto WRAP_IT_UP_BABY ; /* where params/matrix will be used */
5243
5244 } /* end of skipping first volume */
5245
5246 /*-- show if verbose, else use light output 7 Jul 2017 [rickr] -- */
5247 if( verb > 1 )
5248 INFO_message("========== sub-brick #%d ========== [total CPU to here=%.1f s]",
5249 kk , COX_cpu_time() ) ;
5250 else if ( verb && DSET_NVALS(dset_targ) > 1 ) {
5251 if( kk == 0 ) fprintf(stderr,"volume 0");
5252 else fprintf(stderr,"..%d", kk);
5253 if( kk == DSET_NVALS(dset_targ)-1 ) fputc('\n', stderr);
5254 }
5255
5256
5257 /*-- make copy of target brick, and process that --*/
5258
5259 im_targ = mri_scale_to_float( bfac , DSET_BRICK(dset_targ,kk) ) ;
5260 if( im_targ == NULL ) /* this is bad news, but also very improbable */
5261 ERROR_exit("Cannot extract float image from source dataset :(") ;
5262
5263 if( targ_was_vector ){ /* 12 May 2020 (for RGB images) */
5264 if( im_targ_vector != NULL ) mri_free(im_targ_vector) ;
5265 im_targ_vector = mri_copy( DSET_BRICK(dset_targ,kk) ) ; /* for use at end */
5266 }
5267 DSET_unload_one(dset_targ,kk) ; /* it's been copied in, so can unload it now */
5268
5269 /*-- clip off negative values from the target? --*/
5270
5271 if( do_zclip ){
5272 float *bar = MRI_FLOAT_PTR(im_targ) ;
5273 for( ii=0 ; ii < im_targ->nvox ; ii++ ) if( bar[ii] < 0.0f ) bar[ii] = 0.0f ;
5274 }
5275
5276 /*-- check for empty-ish volume --*/
5277
5278 nnz = mri_nonzero_count(im_targ) ;
5279 if( nnz < 66 )
5280 WARNING_message("3dAllineate :: source image #%d has only %d nonzero voxel%s (< 66)",
5281 kk , nnz , (nnz==1) ? "\0" : "s" ) ;
5282
5283 /*** if we are just applying input parameters, set up for that now ***/
5284 /*** we set output params as if they had been found by optimizing ***/
5285
5286 if( apply_1D != NULL ){ /* apply_1D = filename of params/matrix to apply */
5287 int rr=kk ;
5288 if( rr >= apply_ny ){ /* 19 Jul 2007 */
5289 rr = apply_ny-1 ;
5290 WARNING_message("Re-using final row of -1D*_apply '%s' for sub-brick #%d",
5291 apply_1D , kk ) ;
5292 }
5293 stup.interp_code = final_interp ; /* this IS the final operation */
5294 stup.smooth_code = 0 ;
5295 stup.npt_match = 11 ; /* this is just some small positive number */
5296 mri_genalign_scalar_setup( im_bset , NULL , im_targ , &stup ) ;
5297 im_bset = NULL ; /* after setting base, don't need to set it again */
5298 mri_free(im_targ) ; im_targ = NULL ;
5299
5300 /* store the parameters to apply OR the matrix to apply,
5301 then jump down to the end of the optimization phases,
5302 where these values will be used for something useful */
5303
5304 switch( apply_mode ){ /* 23 Jul 2007 */
5305 default:
5306 #ifdef ALLOW_NWARP /*********************************************************/
5307 case APPLY_BILIN:
5308 #endif /* ALLOW_NWARP */ /**************************************************/
5309 case APPLY_PARAM: /* load parameters from file into structure */
5310 if( verb > 1 ) INFO_message("using -1Dparam_apply") ;
5311 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5312 stup.wfunc_param[jj].val_out = APL(jj,rr) ;
5313 break ;
5314
5315 case APPLY_AFF12: /* load matrix from file into aff12_xyz */
5316 if( verb > 1 ) INFO_message("using -1Dmatrix_apply") ;
5317 LOAD_MAT44_AR( aff12_xyz , &APL(0,rr) ) ; /* DICOM coord matrix */
5318 break ;
5319 }
5320 goto WRAP_IT_UP_BABY ; /* all roads lead to WRAP_IT_UP_BABY */
5321 }
5322
5323 /*--- at this point, am actually going to do optimization!! ---*/
5324
5325 /*-- initialize parameters (for the -onepass case) --*/
5326
5327 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5328 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_pinit ;
5329
5330 /* initialize coordinate systems */
5331
5332 AL_setup_warp_coords( epi_targ,epi_fe,epi_pe,epi_se,
5333 nxyz_base, dxyz_base, stup.base_cmat,
5334 nxyz_targ, dxyz_targ, stup.targ_cmat ) ;
5335
5336 /*--- print all cost functionals, for fun? ---*/
5337
5338 if( do_allcost != 0 ){
5339
5340 stup.interp_code = MRI_LINEAR ;
5341 stup.npt_match = npt_match ;
5342 if( do_allcost < 0 && fine_rad > 0.0f ){
5343 stup.smooth_code = sm_code ;
5344 stup.smooth_radius_base = stup.smooth_radius_targ = fine_rad ;
5345 }
5346
5347 mri_genalign_scalar_setup( im_bset , im_wset , im_targ , &stup ) ;
5348
5349 if( allcostX1D == NULL ){ /* just do init parameters == the old way */
5350
5351 PAR_CPY(val_init) ; /* copy init parameters into the allpar arrary */
5352 allcost = mri_genalign_scalar_allcosts( &stup , allpar ) ;
5353 PARINI("initial") ;
5354 INFO_message("allcost output: init #%d",kk) ;
5355 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
5356 fprintf(stderr," %-4s = %g\n",meth_shortname[jj],allcost->ar[jj]) ;
5357 KILL_floatvec(allcost) ;
5358
5359 if( do_allcost == -1 ){
5360 SAVE_PEARSON_MAP(save_pearson_prefix,val_init) ;
5361 do_save_pearson_map = 0 ;
5362 }
5363
5364 if( save_hist != NULL ) SAVEHIST("allcost_init",0) ;
5365 if( do_allcost == -1 ) continue ; /* skip to next sub-brick */
5366
5367 } else { /* 02 Sep 2008: do a bunch of parameter vectors [for Ziad] */
5368
5369 float *av=MRI_FLOAT_PTR(allcostX1D); int nxp=allcostX1D->nx; FILE *fp;
5370
5371 if( strcmp(allcostX1D_outname,"-") == 0 ||
5372 strcmp(allcostX1D_outname,"stdout") == 0 ){
5373 fp = stdout ;
5374 } else {
5375 fp = fopen( allcostX1D_outname , "w" ) ;
5376 if( fp == NULL )
5377 ERROR_exit("Can't open file '%s' for -allcostX1D output :-(" ,
5378 allcostX1D_outname ) ;
5379 }
5380 INFO_message("Writing -allcostX1D results to '%s'",allcostX1D_outname) ;
5381 fprintf( fp , "# 3dAllineate -allcostX1D results:\n" ) ;
5382 fprintf( fp , "#" ) ;
5383 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
5384 fprintf( fp , " ___ %-4s ___",meth_shortname[jj]) ;
5385 fprintf( fp , "\n") ;
5386 for( ii=0 ; ii < allcostX1D->ny ; ii++ ){
5387 allcost = mri_genalign_scalar_allcosts( &stup , av + ii*nxp ) ;
5388 fprintf( fp , " " ) ;
5389 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
5390 fprintf( fp , " %12.6f" , allcost->ar[jj] ) ;
5391 fprintf( fp , "\n") ;
5392 KILL_floatvec(allcost) ;
5393 if( save_hist != NULL ){
5394 char fn[32] ; sprintf(fn,"allcost%06d",ii) ; SAVEHIST(fn,0) ;
5395 }
5396 }
5397 if( fp != stdout ) fclose(fp) ;
5398 INFO_message("-allcostX1D finished") ; exit(0) ;
5399 }
5400
5401 } /* end of -allcost stuff at the startup of optimization */
5402
5403 /*-------- do coarse resolution pass? --------*/
5404
5405 didtwo = 0 ;
5406 if( twopass && (!twofirst || !tfdone) ){
5407
5408 int tb , ib , ccode , nrand ; char *eee ;
5409
5410 if( verb ) INFO_message("*** Coarse pass begins ***") ;
5411 /* used to do NN in the coarse pass, but found that Linear worked better; */
5412 /* we still keep NN if ordered by user with -interp option, which would */
5413 /* be done if trying to use 3dAllineate to align ROI masks (e.g.) - weird */
5414
5415 ccode = (interp_code == MRI_NN) ? MRI_NN : MRI_LINEAR ;
5416 stup.interp_code = ccode ;
5417 stup.npt_match = ntask / 15 ; /* small number of matching points */
5418 if( stup.npt_match < nmatch_setup ) stup.npt_match = nmatch_setup;
5419
5420 /*- smoothing to use for coarse pass -*/
5421
5422 stup.smooth_code = sm_code ;
5423 stup.smooth_radius_base =
5424 stup.smooth_radius_targ = (sm_rad == 0.0f) ? 7.777f : sm_rad ;
5425
5426 /*- setup the parameters for evaluation and optimization -*/
5427
5428 mri_genalign_scalar_setup( im_bset , im_wset , im_targ , &stup ) ;
5429 im_bset = NULL; im_wset = NULL; /* after being set, needn't set again */
5430 if( usetemp ){
5431 mri_purge(im_targ); mri_purge(im_base); mri_purge(im_weig);
5432 }
5433
5434 if( save_hist != NULL ) SAVEHIST("start",1) ; /* for weird users */
5435
5436 /*-- search for coarse start parameters, then optimize them? --*/
5437
5438 if( tbest > 0 ){ /* tbest = result of -twobest option */
5439 int nrefine ;
5440
5441 if( verb > 1 ) ININFO_message("- Search for coarse starting parameters") ;
5442
5443 /*- startup search only allows up to 6 parameters, so freeze excess; -*/
5444 /*- all this complication below was for testing alternate strategies -*/
5445 /*- but those proved to be fairly fruitless (as I recall) -*/
5446
5447 eee = my_getenv("AFNI_TWOPASS_NUM") ; /* normally, eee is NULL */
5448 if( eee == NULL || *eee != ':' ){ /* so this branch is taken */
5449 if( eee != NULL ) sscanf( eee , "%d" , &nptwo ) ;
5450 if( nptwo < 1 || nptwo > 6 ) nptwo = 6 ; /* default = use 6 params */
5451 if( nparam_free > nptwo ){ /* old way: just free first nptwo params */
5452 for( ii=jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
5453 if( !stup.wfunc_param[jj].fixed ){
5454 ii++ ; /* number free so far */
5455 if( ii > nptwo ) stup.wfunc_param[jj].fixed = 1 ; /* temp freeze */
5456 }
5457 }
5458 }
5459 } else { /* the new way: free from a list */
5460 int npk[6]={-1,-1,-1,-1,-1,-1} ;
5461 sscanf( eee , ":%d:%d:%d:%d:%d:%d" ,
5462 npk+0 , npk+1 , npk+2 , npk+3 , npk+4 , npk+5 ) ;
5463 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
5464 if( !stup.wfunc_param[jj].fixed ) stup.wfunc_param[jj].fixed = 1 ;
5465 }
5466 for( ii=0 ; ii < 6 ; ii++ ){
5467 jj = npk[ii] ;
5468 if( jj >= 0 && jj < stup.wfunc_numpar && stup.wfunc_param[jj].fixed == 1 )
5469 stup.wfunc_param[jj].fixed = 0 ;
5470 }
5471 }
5472
5473 /*-- do the startup parameter search:
5474 saves best param set in val_init (and val_out),
5475 plus a few more good sets in val_trial for refinement --*/
5476
5477 if( verb > 1 ) ctim = COX_cpu_time() ;
5478
5479 powell_set_mfac( 1.0f , 3.0f ) ; /* 07 Jun 2011 - for some speedup */
5480
5481 /* number of random trials depends on number to
5482 be optimized further (-twobest ==> tbest value) */
5483
5484 nrand = 17 + 4*tbest ;
5485 if( METH_USES_BLOKS(meth_code) ) nrand += 2*tbest ; /* 27 May 2021 */
5486 nrand = MAX(nrand,31) ; /* num random param setups to try */
5487
5488 /***** Carryout the initial 'random' search for parameters *****/
5489
5490 mri_genalign_scalar_ransetup( &stup , nrand ) ;
5491
5492 if( verb > 1 )
5493 ININFO_message("- Coarse startup search net CPU time = %.1f s",COX_cpu_time()-ctim);
5494
5495 /*-- unfreeze those parameters that were temporarily frozen above --*/
5496
5497 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5498 if( stup.wfunc_param[jj].fixed == 1 ) stup.wfunc_param[jj].fixed = 0 ;
5499
5500 /*---- now refine the tbest values saved already (from val_trial) ----*/
5501
5502 tb = MIN(tbest,stup.wfunc_ntrial) ; nfunc=0 ;
5503 if( verb > 1 ) ctim = COX_cpu_time() ;
5504
5505 /*- copy the trial parameters out into tfparm -*/
5506
5507 for( ib=0 ; ib < tb ; ib++ ){
5508 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
5509 tfparm[ib][jj] = stup.wfunc_param[jj].val_trial[ib] ;
5510 }
5511 tfiorg[ib] = stup.wfunc_param[0].idx_trial[ib] ; /* 24 Jun 2021 */
5512 tfi2bs[ib] = ib ;
5513 }
5514
5515 /*- add identity transform to set, for comparisons (and insurance) -*/
5516
5517 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5518 tfparm[tb][jj] = stup.wfunc_param[jj].val_pinit ;
5519 tfiorg[tb] = -1 ;
5520 tfi2bs[tb] = -1 ;
5521
5522 tfdone = tb+1 ; /* number of parameter sets now saved in tfparm */
5523
5524 /*- number of refinement stages (default = max = 3) -*/
5525
5526 nrefine = (int)AFNI_numenv("AFNI_TWOPASS_REFINE") ;
5527 if( nrefine <= 0 || nrefine >= 3 ) nrefine = 3 ; /* number of refinement passes */
5528 rad = 0.0555 ; /* initial search radius in parameter space */
5529 /* recall that each scaled parameter search range is 0..1 */
5530 /* see powell_int.c for details on how this is implemented */
5531
5532 /*- loop over refinement passes: try to make each trial parameter set better -*/
5533
5534 for( rr=0 ; rr < nrefine ; rr++ , rad*=0.6789 ){ /* refine with less smoothing */
5535
5536 if( verb > 1 )
5537 INFO_message("Start refinement #%d on %d coarse parameter sets",rr+1,tfdone);
5538
5539 powell_set_mfac( 1.0f , 5.0f+2.0f*rr ) ; /* 07 Jun 2011 */
5540
5541 stup.smooth_radius_base *= 0.7777 ; /* less smoothing at each refinement */
5542 stup.smooth_radius_targ *= 0.7777 ;
5543 stup.smooth_radius_base = MAX(stup.smooth_radius_base,fine_rad) ;
5544 stup.smooth_radius_targ = MAX(stup.smooth_radius_targ,fine_rad) ;
5545
5546 stup.npt_match *= 1.5 ; /* more voxels for matching at each refinement */
5547
5548 /* re-set up the optimization, as the above stup parameters have changed */
5549
5550 mri_genalign_scalar_setup( NULL,NULL,NULL , &stup ) ;
5551
5552 for( ib=0 ; ib < tfdone ; ib++ ){ /* loop over param sets */
5553 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* load initial parameters */
5554 stup.wfunc_param[jj].val_init = tfparm[ib][jj] ; /* from tfparm */
5555
5556 /* optimize a little */
5557
5558 nfunc += mri_genalign_scalar_optim( &stup, rad, 0.01*rad, 99+11*rr ) ;
5559
5560 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* save optimized params */
5561 tfparm[ib][jj] = stup.wfunc_param[jj].val_out ; /* back into tfparm */
5562
5563 tfcost[ib] = stup.vbest ; tfindx[ib] = ib ; /* save cost */
5564 if( verb > 1 )
5565 ININFO_message("- param set #%d has cost=%f [o=%d t=%d]",ib+1,stup.vbest,tfiorg[ib],tfi2bs[ib]) ;
5566 if( verb > 2 ) PAROUT("--") ;
5567 }
5568
5569 /* 29 Aug 2008: sort tfparm by cost, then cast out the close ones */
5570
5571 if( !nocast && tfdone > 2 ){
5572 int jb,ncast=0 ; float pdist ;
5573
5574 if( verb > 1 ) ININFO_message("- sorting parameter sets by cost") ;
5575 for( ib=0 ; ib < tfdone ; ib++ ){ /* copy tfparm into ffparm */
5576 memcpy( ffparm[ib], tfparm[ib], sizeof(float)*stup.wfunc_numpar );
5577 ffiorg[ib] = tfiorg[ib] ; /* 24 Jun 2021 */
5578 ffi2bs[ib] = tfi2bs[ib] ;
5579 }
5580 qsort_floatint( tfdone , tfcost , tfindx ) ; /* sort by cost */
5581 for( ib=0 ; ib < tfdone ; ib++ ){ /* copy back into tfparm */
5582 jb = tfindx[ib] ; /* jb = index in unsorted copy in ffparm */
5583 memcpy( tfparm[ib], ffparm[jb], sizeof(float)*stup.wfunc_numpar );
5584 tfiorg[ib] = ffiorg[jb] ;
5585 tfi2bs[ib] = ffi2bs[jb] ;
5586 }
5587
5588 /* now cast out parameter sets that are very close to the best one */
5589
5590 #undef CTHRESH
5591 #define CTHRESH 0.02f
5592 if( verb > 1 ) ININFO_message("- scanning for distances from #1") ;
5593 for( ib=1 ; ib < tfdone ; ib++ ){
5594 pdist = param_dist( &stup , tfparm[0] , tfparm[ib] ) ;
5595 if( verb > 2 ) ININFO_message("--- dist(#%d,#1) = %.3g %s" ,
5596 ib+1, pdist, (pdist<CTHRESH)?"XXX":"" ) ;
5597 if( tfdone > 2 && pdist < CTHRESH ){
5598 for( jb=ib+1 ; jb < tfdone ; jb++ ){ /* copy those above down */
5599 memcpy( tfparm[jb-1], tfparm[jb], sizeof(float)*stup.wfunc_numpar );
5600 tfiorg[jb-1] = tfiorg[jb] ;
5601 tfi2bs[jb-1] = tfi2bs[jb] ;
5602 }
5603 ncast++ ; tfdone-- ;
5604 }
5605 }
5606 if( ncast > 0 && verb > 1 )
5607 ININFO_message(
5608 "- cast out %d parameter set%s for being too close to best set" ,
5609 ncast , (ncast==1)?"":"s" ) ;
5610 }
5611
5612 } /* end of refinement loop (rr) */
5613
5614 if( verb > 1 )
5615 ININFO_message("- Total coarse refinement net CPU time = %.1f s; %d funcs",
5616 COX_cpu_time()-ctim,nfunc ) ;
5617
5618 /* end of '-twobest x' for x > 0 */
5619
5620 } else { /*- if stoopid user did '-twobest 0' -*/
5621 /*- just optimize coarse setup from default parameters -*/
5622 /*- mimicking the 3 loop passes above --*/
5623
5624 if( verb ) ININFO_message("- Start coarse optimization with -twobest 0") ;
5625 if( verb > 1 ) ctim = COX_cpu_time() ;
5626 powell_set_mfac( 2.0f , 1.0f ) ; /* 07 Jun 2011 */
5627 /* optimize pass 1 */
5628 nfunc = mri_genalign_scalar_optim( &stup , 0.05 , 0.001 , 444 ) ;
5629 if( verb > 2 ) PAROUT("--(a)") ;
5630 /* optimize pass 2 */
5631 stup.npt_match = ntask / 7 ;
5632 if( stup.npt_match < nmatch_setup ) stup.npt_match = nmatch_setup ;
5633 stup.smooth_radius_base *= 0.456 ;
5634 stup.smooth_radius_targ *= 0.456 ;
5635 mri_genalign_scalar_setup( NULL,NULL,NULL , &stup ) ;
5636 nfunc += mri_genalign_scalar_optim( &stup , 0.0333 , 0.001 , 444 ) ;
5637 if( verb > 2 ) PAROUT("--(b)") ;
5638 /* optimize pass 2 */
5639 stup.smooth_radius_base *= 0.456 ;
5640 stup.smooth_radius_targ *= 0.456 ;
5641 mri_genalign_scalar_setup( NULL,NULL,NULL , &stup ) ;
5642 nfunc += mri_genalign_scalar_optim( &stup , 0.0166 , 0.001 , 444 ) ;
5643 if( verb > 2 ) PAROUT("--(c)") ;
5644 if( verb > 1 ) ININFO_message("- Coarse net CPU time = %.1f s; %d funcs",
5645 COX_cpu_time()-ctim,nfunc) ;
5646 if( verb ) ININFO_message("- Coarse optimization: best cost=%f",
5647 stup.vbest) ;
5648
5649 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* save best params */
5650 tfparm[0][jj] = stup.wfunc_param[jj].val_out ;
5651
5652 tfiorg[0] = 0 ;
5653 tfi2bs[0] = 0 ;
5654 tfdone = 1 ; /* number of parameter sets saved in tfparm */
5655
5656 } /* end of '-twobest 0' */
5657
5658 /*-- 22 Sep 2006: add default init params to the tfparm list --*/
5659 /*-- (so there will be at least 2 sets there) --*/
5660
5661 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5662 tfparm[tfdone][jj] = stup.wfunc_param[jj].val_pinit ;
5663 tfiorg[tfdone] = -2 ;
5664 tfi2bs[tfdone] = -2 ;
5665 tfdone++ ;
5666
5667 didtwo = 1 ; /* mark that we did the first pass */
5668
5669 } /*------------- end of the coarse pass --------------------------------*/
5670
5671 /*-----------------------------------------------------------------------*/
5672 /*----------------------- do final resolution pass ----------------------*/
5673 /*-------- which has less smoothing and more voxels for matching --------*/
5674 /*-----------------------------------------------------------------------*/
5675
5676 if( verb > 1 || (verb && kk == 0) ) /* 7 Jan 2019 [rickr] */
5677 INFO_message("*** Fine pass begins ***") ;
5678 ctim = COX_cpu_time() ;
5679
5680 stup.interp_code = interp_code ; /* set interpolation */
5681 stup.smooth_code = sm_code ; /* and smoothing codes */
5682
5683 /*-- setup smoothing --*/
5684
5685 if( fine_rad > 0.0f ){ /* if ordered by user */
5686 stup.smooth_radius_base = stup.smooth_radius_targ = fine_rad ;
5687 } else if( diffblur ){ /* if base finer resolution than target */
5688 float br,tr ;
5689 if( nz_base > 1 ){
5690 br = cbrt(dx_base*dy_base*dz_base) ; /* base voxel size */
5691 tr = cbrt(dx_targ*dy_targ*dz_targ) ; /* targ voxel size */
5692 } else {
5693 br = sqrt(dx_base*dy_base) ;
5694 tr = sqrt(dx_targ*dy_targ) ;
5695 }
5696 stup.smooth_radius_targ = 0.0f ;
5697 stup.smooth_radius_base = (tr <= 1.1f*br) ? 0.0f
5698 : sqrt(tr*tr-br*br) ;
5699 }
5700
5701 /*-- setup the optimization --*/
5702
5703 stup.npt_match = npt_match ;
5704 if( didtwo ){ /* did first pass already: */
5705 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup ); /* simple re-setup */
5706
5707 } else { /* have to setup which images are involved */
5708 mri_genalign_scalar_setup( im_bset , im_wset , im_targ , &stup ) ;
5709 im_bset = NULL; im_wset = NULL; /* after being set, needn't set again */
5710 if( usetemp ) mri_purge( im_targ ) ;
5711 }
5712
5713 /*-- set initial param radius for optimizer --*/
5714
5715 switch( tfdone ){
5716 case 0: rad = 0.0666 ; break ; /* this is size of initial trust region */
5717 case 1: /* -- in the unitless [-1..1] space */
5718 case 2: rad = 0.0444 ; break ;
5719 default: rad = 0.0333 ; break ;
5720 }
5721 if( rad < 44.4*conv_rad ) rad = 44.4*conv_rad ; /* unlikely */
5722 if( rad > 0.111 ) rad = 0.111 ; /* very unlikely */
5723
5724 /*-- choose initial parameters, based on interp_code cost functional --*/
5725
5726 powell_set_mfac( powell_mm , powell_aa ) ; /* 07 Jun 2011 */
5727
5728 /*-- find the best param set in tfparm array (if populated),
5729 using the cost functional evaluation setup for this fine pass --*/
5730
5731 if( tfdone ){
5732 int kb=0 , ib ; float cbest=1.e+33 ;
5733
5734 if( verb > 1 )
5735 INFO_message("Picking best parameter set out of %d cases",tfdone) ;
5736 for( ib=0 ; ib < tfdone ; ib++ ){
5737 cost = mri_genalign_scalar_cost( &stup , tfparm[ib] ) ;
5738 if( verb > 1 ) ININFO_message("- cost(#%d)=%f %c [o=%d t=%d]",
5739 ib+1,cost,(cost<cbest)?'*':' ',tfiorg[ib],tfi2bs[ib]);
5740 if( verb > 2 ) PARVEC("--",tfparm[ib]) ;
5741 if( cost < cbest ){ cbest=cost ; kb=ib ; } /* save best case */
5742 }
5743
5744 if( num_rtb == 0 ){ /*-- 27 Aug 2008: this was the old way, --*/
5745 /*-- to just pick the best at this point --*/
5746 if( verb > 1 )
5747 ININFO_message("-num_rtb 0 ==> pick best of the %d cases (#%d)",tfdone,kb+1);
5748 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* copy best set */
5749 stup.wfunc_param[jj].val_init = tfparm[kb][jj] ; /* for fine work */
5750
5751 for( ib=0 ; ib < tfdone ; ib++ ) /* save all cases into ffparm */
5752 memcpy( ffparm[ib], tfparm[ib], sizeof(float)*stup.wfunc_numpar ) ;
5753
5754 } else { /*-- now: try to make these a little better instead --*/
5755 /*-- and THEN choose the best one at that point -*/
5756 if( verb > 1 )
5757 ININFO_message("-num_rtb %d ==> refine all %d cases",num_rtb,tfdone);
5758 cbest = 1.e+33 ;
5759 for( ib=0 ; ib < tfdone ; ib++ ){
5760 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* load init params */
5761 stup.wfunc_param[jj].val_init = tfparm[ib][jj] ;
5762 nfunc = mri_genalign_scalar_optim( &stup, rad, 0.01*rad, /* improvement */
5763 (ib==tfdone-1) ? 2*num_rtb : num_rtb );
5764 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* save refined */
5765 ffparm[ib][jj] = stup.wfunc_param[jj].val_out ; /* parameters */
5766 cost = stup.vbest ;
5767 if( verb > 1 ) ININFO_message("- cost(#%d)=%f %c [o=%d t=%d]",
5768 ib+1,cost,(cost<cbest)?'*':' ',tfiorg[ib],tfi2bs[ib] );
5769 if( verb > 2 ) PAROUT("--") ;
5770 if( cost < cbest ){ cbest=cost ; kb=ib ; } /* save best case */
5771 }
5772 if( verb > 1 ) ININFO_message("- case #%d [o=%d t=%d] is now the best",kb+1,tfiorg[kb],tfi2bs[kb]) ;
5773 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5774 stup.wfunc_param[jj].val_init = ffparm[kb][jj] ;
5775 }
5776 cost_ini = cbest ;
5777
5778 } else { /*-- did not do first pass, so we start at default params --*/
5779 /*-- this situation is '-onepass' = fast and less reliable -*/
5780
5781 cost_ini = mri_genalign_scalar_cost( &stup , NULL ) ;
5782
5783 }
5784
5785 /*-- BUT .. before we start the fine alignment ....... --*/
5786
5787 if( do_allcost != 0 ){ /*-- print out all cost functionals, for fun --*/
5788 PAR_CPY(val_init) ; /* copy init parameters into allpar[] */
5789 allcost = mri_genalign_scalar_allcosts( &stup , allpar ) ;
5790 INFO_message("allcost output: start fine #%d",kk) ;
5791 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
5792 fprintf(stderr," %-4s = %g\n",meth_shortname[jj],allcost->ar[jj]) ;
5793 KILL_floatvec(allcost) ;
5794 if( save_hist != NULL ) SAVEHIST("allcost_finestart",0) ;
5795 }
5796
5797 if( verb > 1 ){
5798 ININFO_message("- Initial cost = %f",cost_ini) ;
5799 PARINI("- Initial fine") ;
5800 }
5801
5802 powell_set_mfac( powell_mm , powell_aa ) ; /* 07 Jun 2011 */
5803 nfunc = 0 ;
5804
5805 /*-- start with some optimization with linear interp, for speed? --*/
5806 /*-- (that is, if no refinement was done above already) --*/
5807
5808 if( num_rtb == 0 &&
5809 (MRI_HIGHORDER(interp_code) || npt_match > 999999) ){
5810
5811 float pini[MAXPAR] ;
5812 stup.interp_code = MRI_LINEAR ; /* set interpolation to LINEAR */
5813 stup.npt_match = MIN(499999,npt_match) ; /* and fewer points */
5814 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup ) ;
5815 if( verb > 1 ) ININFO_message("- start Intermediate optimization") ;
5816 /*** if( verb > 2 ) GA_do_params(1) ; ***/ /* was for debugging */
5817
5818 nfunc = mri_genalign_scalar_optim( &stup, rad, 0.01*rad, 333 );
5819
5820 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
5821 pini[jj] = stup.wfunc_param[jj].val_init ;
5822 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out ;
5823 }
5824
5825 stup.interp_code = interp_code ; /* check cost of result with */
5826 stup.npt_match = npt_match ; /* actual final interp method */
5827 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup ) ;
5828 cost = mri_genalign_scalar_cost( &stup , NULL ) ; /* interp_code, not LINEAR */
5829 if( cost > cost_ini ){ /* should not happen, but it could since */
5830 if( verb > 1 ) /* LINEAR cost optimized above isn't same */
5831 ININFO_message("- Intrmed cost = %f > Initial cost = %f :-(",cost,cost_ini);
5832 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5833 stup.wfunc_param[jj].val_init = pini[jj] ;
5834 } else {
5835 if( verb > 1 ){
5836 PARINI("- Intrmed fine") ;
5837 ININFO_message("- Intrmed cost = %f ; %d funcs",cost,nfunc) ;
5838 }
5839 if( nfunc < 333 ){
5840 rad *= 0.456 ; if( rad < 9.99*conv_rad ) rad = 9.99*conv_rad ;
5841 }
5842 }
5843
5844 if( do_allcost != 0 ){ /*-- all cost functionals for fun again --*/
5845 PAR_CPY(val_init) ; /* copy init parameters into allpar[] */
5846 allcost = mri_genalign_scalar_allcosts( &stup , allpar ) ;
5847 INFO_message("allcost output: intermed fine #%d",kk) ;
5848 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
5849 fprintf(stderr," %-4s = %g\n",meth_shortname[jj],allcost->ar[jj]) ;
5850 KILL_floatvec(allcost) ;
5851 if( save_hist != NULL ) SAVEHIST("allcost_fineintermed",0) ;
5852 }
5853
5854 } /* end of the 'Intermediate' alignment prologue to the Final work */
5855
5856 /*-- now do final final optimization, with the correct interp mode --*/
5857 /*-- note we now allow a lot of function evaluations, as we are --*/
5858 /*-- telling NEWUOA to evaluate down to a fine convergence radius --*/
5859
5860 nfunc = mri_genalign_scalar_optim( &stup , rad, conv_rad,6666 );
5861
5862 /*-- 14 Nov 2007: a final final final optimization? [default] --*/
5863
5864 if( do_refinal ){ /* and here is where +ZZ is implemented */
5865 if( verb > 1 )
5866 ININFO_message("- Finalish cost = %f ; %d funcs",stup.vbest,nfunc) ;
5867 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5868 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out;
5869 if( verb > 1 ) PARINI("- ini Finalish") ;
5870 stup.need_hist_setup = 1 ;
5871 if( (meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
5872 meth_code == GA_MATCH_LPA_MICHO_SCALAR ) && micho_zfinal ){
5873 GA_setup_micho( 0.0 , 0.0 , 0.0 , 0.0 , 0.0 ) ; /* do +ZZ stuff */
5874 if( verb > 1 )
5875 ININFO_message(" - Set %s parameters back to purity before Final iterations",
5876 meth_shortname[meth_code-1] ) ;
5877 }
5878 rad = 0.0666 ; /* start with a moderately wide trust region */
5879 if( powell_mm == 0.0f ) powell_set_mfac( 4.0f , 4.0f ) ; /* 07 Jun 2011 */
5880 nfunc = mri_genalign_scalar_optim( &stup , rad, conv_rad,6666 );
5881 powell_set_mfac( powell_mm , powell_aa ) ; /* 07 Jun 2011 */
5882 }
5883
5884 /*** if( powell_mm > 0.0f ) powell_set_mfac( 0.0f , 0.0f ) ; ***/
5885 /*** if( verb > 2 ) GA_do_params(0) ; ***/
5886
5887 /*** Optimzation is done, so do some cleanup and some output ***/
5888
5889 if( verb > 1 ) ININFO_message("- Final cost = %f ; %d funcs",stup.vbest,nfunc) ;
5890 if( verb > 1 || (verb==1 && kk==0) ) PARNOUT("Final fine fit") ; /* 30 Aug 2013 */
5891
5892 /* Check if some parameter is near the edge of the allowable [27 May 2021] */
5893
5894 { float pmin , pmax , pdif , pval ;
5895 for( jj=0 ; jj < 12 ; jj++ ){
5896 if( stup.wfunc_param[jj].fixed ) continue ; /* not change-able */
5897 pmin = stup.wfunc_param[jj].min ;
5898 pmax = stup.wfunc_param[jj].max ;
5899 pdif = 0.0101f * fabsf(pmax-pmin) ; if( pdif <= 0.0f ) continue ;
5900 pval = stup.wfunc_param[jj].val_out ;
5901 if( fabsf(pval-pmin) <= pdif || fabsf(pval-pmax) <= pdif ){ /* within 1% of edge */
5902 WARNING_message("Parameter %s = %9.5f is close to edge of its search range %9.5f .. %9.5f" ,
5903 stup.wfunc_param[jj].name , pval , pmin , pmax ) ;
5904 }
5905 }
5906 }
5907
5908 /* some more cleanup */
5909
5910 if( verb > 1 ) ININFO_message("- Fine net CPU time = %.1f s",COX_cpu_time()-ctim) ;
5911
5912 if( save_hist != NULL ) SAVEHIST("final",1) ;
5913
5914 if( (meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
5915 meth_code == GA_MATCH_LPA_MICHO_SCALAR ) && micho_zfinal ) /* set them back */
5916 GA_setup_micho( micho_hel , micho_mi , micho_nmi , micho_crA , micho_ov ) ;
5917
5918 /* some random outputs for the most random of users */
5919
5920 if( do_allcost != 0 ){ /*-- all costs at final affine solution? --*/
5921 PAR_CPY(val_out) ; /* copy output parameters into allpar[] */
5922 allcost = mri_genalign_scalar_allcosts( &stup , allpar ) ;
5923 INFO_message("allcost output: final fine #%d",kk) ;
5924 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
5925 fprintf(stderr," %-4s = %g\n",meth_shortname[jj],allcost->ar[jj]) ;
5926 KILL_floatvec(allcost) ;
5927 if( save_hist != NULL ) SAVEHIST("allcost_finefinal",0) ;
5928 }
5929
5930 if( do_save_pearson_map ){ /*-- Save Pearson map [25 Jan 2021] --*/
5931 SAVE_PEARSON_MAP(save_pearson_prefix,val_out) ;
5932 do_save_pearson_map = 0 ;
5933 }
5934
5935 #if 0 /* I don't recall why this is here -- debugging, I guess? */
5936 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
5937 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out ;
5938 mri_genalign_verbose(9) ;
5939 cost = mri_genalign_scalar_cost( &stup , NULL ) ;
5940 INFO_message("Recomputed final cost = %g",cost) ;
5941 if( verb > 1 ) mri_genalign_verbose(verb-1) ;
5942 #endif
5943
5944 /** Next up: about 400 lines of code that proved not to be very useful **/
5945 #ifdef ALLOW_NWARP /*********************************************************/
5946 /*----------------------------------------------------------------------*/
5947 /*------------ Nonlinear warp improvement to the above results? --------*/
5948 /*----------- Someday soon (?), this code will be expunged! ------------*/
5949
5950 /* macro to (re)setup some parameters in the work below */
5951
5952 #define PARAM_SETUP(pp,ff,vv) \
5953 do{ if( ff ){ stup.wfunc_param[pp].fixed = ff; stup.wfunc_param[pp].val_fixed = vv; } \
5954 else { stup.wfunc_param[pp].fixed = 0; } \
5955 stup.wfunc_param[pp].val_init = vv; \
5956 } while(0)
5957
5958 if( nwarp_pass ){
5959
5960 /* 15 Dec 2010: change cost functional here? */
5961
5962 if( nwarp_meth_code > 0 ){
5963 if( verb ) INFO_message( "-nwarp setup: switch method to '%s' from '%s'",
5964 meth_shortname[nwarp_meth_code-1] ,
5965 meth_shortname[stup.match_code-1] ) ;
5966 stup.match_code = nwarp_meth_code ;
5967 }
5968
5969 /*--- different blocks of code for the different types of warps ---*/
5970
5971 if( nwarp_type == WARP_BILINEAR ){ /*------ special case [old] ------*/
5972
5973 float rr , xcen,ycen,zcen , brad,crad ; int nbf , nite ;
5974
5975 rr = MAX(xsize,ysize) ; rr = MAX(zsize,rr) ; rr = 1.2f / rr ;
5976
5977 SETUP_BILINEAR_PARAMS ; /* nonlinear params */
5978
5979 /* nonlinear transformation is centered at middle of base volume
5980 indexes (xcen,ycen,zcen) and is scaled by reciprocal of size (rr) */
5981
5982 MAT44_VEC( stup.base_cmat,
5983 0.5f*nx_base, 0.5f*ny_base, 0.5f*nz_base,
5984 xcen , ycen , zcen ) ;
5985 stup.wfunc_param[NPBIL ].val_fixed = stup.wfunc_param[NPBIL ].val_init = xcen;
5986 stup.wfunc_param[NPBIL+1].val_fixed = stup.wfunc_param[NPBIL+1].val_init = ycen;
5987 stup.wfunc_param[NPBIL+2].val_fixed = stup.wfunc_param[NPBIL+2].val_init = zcen;
5988 stup.wfunc_param[NPBIL+3].val_fixed = stup.wfunc_param[NPBIL+3].val_init = rr ;
5989
5990 /* affine part is copied from results of work thus far */
5991
5992 for( jj=0 ; jj < 12 ; jj++ )
5993 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out;
5994
5995 stup.need_hist_setup = 1 ;
5996 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup );
5997
5998 /* do the first pass of the bilinear optimization */
5999
6000 if( verb > 0 ) INFO_message("Start bilinear warping") ;
6001 if( verb > 1 ) PARINI("- Bilinear initial") ;
6002 for( jj=12 ; jj <= 14 ; jj++ ){
6003 stup.wfunc_param[jj ].fixed = 0 ; /* just free up diagonal */
6004 stup.wfunc_param[jj+12].fixed = 0 ; /* elements of B tensor */
6005 stup.wfunc_param[jj+24].fixed = 0 ;
6006 }
6007 if( verb ) ctim = COX_cpu_time() ;
6008 brad = MAX(conv_rad,0.001f) ;
6009 if( rad > 55.5f*brad ) rad = 55.5f*brad ;
6010 else if( rad < 22.2f*brad ) rad = 22.2f*brad ;
6011 crad = ( (nwarp_flags&1) == 0 ) ? (11.1f*brad) : (2.22f*brad) ;
6012 nite = MAX(555,nwarp_itemax) ;
6013 nbf = mri_genalign_scalar_optim( &stup , rad, crad, nite );
6014 if( verb ){
6015 dtim = COX_cpu_time() ;
6016 ININFO_message("- Bilinear#1 cost = %f ; %d funcs ; net CPU = %.1f s",
6017 stup.vbest,nbf,dtim-ctim) ;
6018 ctim = dtim ;
6019 }
6020
6021 /* do the second pass, with more parameters varying */
6022
6023 if( (nwarp_flags&1) == 0 ){
6024 float dnor , onor ;
6025
6026 for( jj=0 ; jj < NPBIL ; jj++ )
6027 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out;
6028 #if 1
6029 for( jj=0 ; jj < 12 ; jj++ ){ /* fix affine params */
6030 stup.wfunc_param[jj].val_fixed = stup.wfunc_param[jj].val_out ;
6031 stup.wfunc_param[jj].fixed = 1 ;
6032 }
6033 #endif
6034 for( jj=12 ; jj < NPBIL ; jj++ ) /* now free up all B elements */
6035 stup.wfunc_param[jj].fixed = 0 ;
6036 nite = MAX(1111,nwarp_itemax) ;
6037 nbf = mri_genalign_scalar_optim( &stup, 33.3f*brad, 2.22f*brad,nite );
6038 if( verb ){
6039 dtim = COX_cpu_time() ;
6040 ININFO_message("- Bilinear#2 cost = %f ; %d funcs ; net CPU = %.1f s",
6041 stup.vbest,nbf,dtim-ctim) ;
6042 ctim = dtim ;
6043 }
6044
6045 /* run it again to see if it improves any more? */
6046
6047 dnor = BILINEAR_diag_norm (stup) ;
6048 onor = BILINEAR_offdiag_norm(stup) ;
6049 if( onor > 0.0333f * dnor ){
6050 for( jj=0 ; jj < NPBIL ; jj++ )
6051 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out;
6052 nbf = mri_genalign_scalar_optim( &stup, 4.44f*brad, brad, 222 );
6053 if( verb ){
6054 dtim = COX_cpu_time() ;
6055 ININFO_message("- Bilinear#3 cost = %f ; %d funcs ; net CPU = %.1f s",
6056 stup.vbest,nbf,dtim-ctim) ;
6057 ctim = dtim ;
6058 }
6059 }
6060 }
6061 /** if( verb > 1 ) PAROUT("- Bilinear final") ; **/
6062
6063 strcpy(warp_code_string,"bilinear") ;
6064
6065 } else if( nwarp_type == WARP_CUBIC ){ /*------ special case ------------*/
6066
6067 float rr , xcen,ycen,zcen , brad,crad ; int nbf , nite ;
6068
6069 rr = MAX(xsize,ysize) ; rr = MAX(zsize,rr) ; rr = 1.2f / rr ;
6070
6071 SETUP_CUBIC_PARAMS ; /* nonlinear params */
6072
6073 /* nonlinear transformation is centered at middle of base volume
6074 indexes (xcen,ycen,zcen) and is scaled by reciprocal of size (rr) */
6075
6076 MAT44_VEC( stup.base_cmat,
6077 0.5f*nx_base, 0.5f*ny_base, 0.5f*nz_base,
6078 xcen , ycen , zcen ) ;
6079 stup.wfunc_param[NPCUB ].val_fixed = stup.wfunc_param[NPCUB ].val_init = xcen;
6080 stup.wfunc_param[NPCUB+1].val_fixed = stup.wfunc_param[NPCUB+1].val_init = ycen;
6081 stup.wfunc_param[NPCUB+2].val_fixed = stup.wfunc_param[NPCUB+2].val_init = zcen;
6082 stup.wfunc_param[NPCUB+3].val_fixed = stup.wfunc_param[NPCUB+3].val_init = rr ;
6083
6084 /* affine part is copied from results of work thus far */
6085
6086 for( jj=0 ; jj < 12 ; jj++ ){
6087 nbf = (stup.wfunc_param[jj].fixed) ? stup.wfunc_param[jj].fixed : nwarp_fixaff ;
6088 PARAM_SETUP( jj , nbf , stup.wfunc_param[jj].val_out ) ;
6089 }
6090
6091 stup.need_hist_setup = 1 ;
6092 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup );
6093
6094 /* do the optimization */
6095
6096 /** if( verb > 1 ) PARINI("- Cubic/Poly3 initial") ; **/
6097 for( jj=12 ; jj < NPCUB ; jj++ ) stup.wfunc_param[jj].fixed = 0 ;
6098 FREEZE_POLYNO_PARAMS ; /* 07 Dec 2010 */
6099
6100 COUNT_FREE_PARAMS(nbf) ;
6101 if( verb > 0 )
6102 INFO_message("Start Cubic/Poly3 warping: %d free parameters",nbf) ;
6103
6104 if( verb ) ctim = COX_cpu_time() ;
6105 rad = 0.01f ; crad = 0.003f ;
6106 nite = MAX(2222,nwarp_itemax) ;
6107 nbf = mri_genalign_scalar_optim( &stup , rad, crad, nite );
6108 if( verb ){
6109 dtim = COX_cpu_time() ;
6110 ININFO_message("- Cubic/Poly3 cost = %f ; %d funcs ; net CPU = %.1f s",
6111 stup.vbest,nbf,dtim-ctim) ;
6112 ctim = dtim ;
6113 }
6114
6115 /** if( verb > 1 ) PAROUT("- Cubic/Poly3 final") ; **/
6116 strcpy(warp_code_string,"cubic") ;
6117
6118 } else if( nwarp_type == WARP_QUINT ){ /*------ special case ------------*/
6119
6120 float rr , xcen,ycen,zcen , brad,crad ; int nbf , nite ;
6121
6122 rr = MAX(xsize,ysize) ; rr = MAX(zsize,rr) ; rr = 1.2f / rr ;
6123
6124 SETUP_QUINT_PARAMS ; /* nonlinear params */
6125
6126 /* nonlinear transformation is centered at middle of base volume
6127 indexes (xcen,ycen,zcen) and is scaled by reciprocal of size (rr) */
6128
6129 MAT44_VEC( stup.base_cmat,
6130 0.5f*nx_base, 0.5f*ny_base, 0.5f*nz_base,
6131 xcen , ycen , zcen ) ;
6132 PARAM_SETUP( NPQUINT , 2 , xcen ) ;
6133 PARAM_SETUP( NPQUINT+1 , 2 , ycen ) ;
6134 PARAM_SETUP( NPQUINT+2 , 2 , zcen ) ;
6135 PARAM_SETUP( NPQUINT+3 , 2 , rr ) ;
6136
6137 /* affine part is copied from results of work thus far */
6138
6139 for( jj=0 ; jj < 12 ; jj++ ){
6140 nbf = (stup.wfunc_param[jj].fixed) ? stup.wfunc_param[jj].fixed : nwarp_fixaff ;
6141 PARAM_SETUP( jj , nbf , stup.wfunc_param[jj].val_out ) ;
6142 }
6143
6144 stup.need_hist_setup = 1 ;
6145 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup );
6146 GA_set_nperval(0) ;
6147
6148 /* do the optimization */
6149
6150 for( jj=12 ; jj < NPQUINT ; jj++ ) stup.wfunc_param[jj].fixed = 0 ;
6151 FREEZE_POLYNO_PARAMS ; /* 07 Dec 2010 */
6152
6153 COUNT_FREE_PARAMS(nbf) ;
6154 if( verb > 0 )
6155 INFO_message("Start Quintic/Poly5 warping: %d free parameters",nbf) ;
6156
6157 if( verb ) ctim = COX_cpu_time() ;
6158 rad = 0.01f ; crad = 0.003f ;
6159 nite = MAX(3333,nwarp_itemax) ;
6160 nbf = mri_genalign_scalar_optim( &stup , rad, crad, nite );
6161 if( verb ){
6162 dtim = COX_cpu_time() ;
6163 ININFO_message("- Quintic/Poly5 cost = %f ; %d funcs ; net CPU = %.1f s",
6164 stup.vbest,nbf,dtim-ctim) ;
6165 ctim = dtim ;
6166 }
6167
6168 /** if( verb > 1 ) PAROUT("- Quintic/Poly5 final") ; **/
6169 strcpy(warp_code_string,"quintic") ;
6170
6171 } else if( nwarp_type == WARP_HEPT ){ /*------ special case ------------*/
6172
6173 float rr , xcen,ycen,zcen , brad,crad ; int nbf , nite ;
6174
6175 rr = MAX(xsize,ysize) ; rr = MAX(zsize,rr) ; rr = 1.2f / rr ;
6176
6177 SETUP_HEPT_PARAMS ; /* nonlinear params */
6178
6179 /* nonlinear transformation is centered at middle of base volume
6180 indexes (xcen,ycen,zcen) and is scaled by reciprocal of size (rr) */
6181
6182 MAT44_VEC( stup.base_cmat,
6183 0.5f*nx_base, 0.5f*ny_base, 0.5f*nz_base,
6184 xcen , ycen , zcen ) ;
6185 PARAM_SETUP( NPHEPT , 2 , xcen ) ;
6186 PARAM_SETUP( NPHEPT+1 , 2 , ycen ) ;
6187 PARAM_SETUP( NPHEPT+2 , 2 , zcen ) ;
6188 PARAM_SETUP( NPHEPT+3 , 2 , rr ) ;
6189
6190 /* affine part is copied from results of work thus far */
6191
6192 for( jj=0 ; jj < 12 ; jj++ ){
6193 nbf = (stup.wfunc_param[jj].fixed) ? stup.wfunc_param[jj].fixed : nwarp_fixaff ;
6194 PARAM_SETUP( jj , nbf , stup.wfunc_param[jj].val_out ) ;
6195 }
6196
6197 stup.need_hist_setup = 1 ;
6198 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup );
6199 GA_set_nperval(0) ;
6200
6201 /* do the optimization */
6202
6203 for( jj=12 ; jj < NPHEPT ; jj++ ) stup.wfunc_param[jj].fixed = 0 ;
6204 FREEZE_POLYNO_PARAMS ; /* 07 Dec 2010 */
6205
6206 COUNT_FREE_PARAMS(nbf) ;
6207 if( verb > 0 )
6208 INFO_message("Start Heptic/Poly7 warping: %d free parameters",nbf) ;
6209
6210 if( verb ) ctim = COX_cpu_time() ;
6211 rad = 0.01f ; crad = 0.003f ;
6212 nite = MAX(4444,nwarp_itemax) ;
6213 nbf = mri_genalign_scalar_optim( &stup , rad, crad, nite );
6214 if( verb ){
6215 dtim = COX_cpu_time() ;
6216 ININFO_message("- Heptic/Poly7 cost = %f ; %d funcs ; net CPU = %.1f s",
6217 stup.vbest,nbf,dtim-ctim) ;
6218 ctim = dtim ;
6219 }
6220
6221 /** if( verb > 1 ) PAROUT("- Heptic/Poly7 final") ; **/
6222 strcpy(warp_code_string,"heptic") ;
6223
6224 } else if( nwarp_type == WARP_NONI ){ /*------ special case ------------*/
6225
6226 float rr , xcen,ycen,zcen , brad,crad ; int nbf , nite ;
6227
6228 rr = MAX(xsize,ysize) ; rr = MAX(zsize,rr) ; rr = 1.2f / rr ;
6229
6230 SETUP_NONI_PARAMS ; /* nonlinear params */
6231
6232 /* nonlinear transformation is centered at middle of base volume
6233 indexes (xcen,ycen,zcen) and is scaled by reciprocal of size (rr) */
6234
6235 MAT44_VEC( stup.base_cmat,
6236 0.5f*nx_base, 0.5f*ny_base, 0.5f*nz_base,
6237 xcen , ycen , zcen ) ;
6238 PARAM_SETUP( NPNONI , 2 , xcen ) ;
6239 PARAM_SETUP( NPNONI+1 , 2 , ycen ) ;
6240 PARAM_SETUP( NPNONI+2 , 2 , zcen ) ;
6241 PARAM_SETUP( NPNONI+3 , 2 , rr ) ;
6242
6243 /* affine part is copied from results of work thus far */
6244
6245 for( jj=0 ; jj < 12 ; jj++ ){
6246 nbf = (stup.wfunc_param[jj].fixed) ? stup.wfunc_param[jj].fixed : nwarp_fixaff ;
6247 PARAM_SETUP( jj , nbf , stup.wfunc_param[jj].val_out ) ;
6248 }
6249
6250 stup.need_hist_setup = 1 ;
6251 mri_genalign_scalar_setup( NULL,NULL,NULL, &stup );
6252 GA_set_nperval(0) ;
6253
6254 /* do the optimization */
6255
6256 powell_set_mfac( 1.2f , 5.0f ) ;
6257
6258 if( AFNI_noenv("AFNI_NONIC_GRADUAL") ){ /* old way: all params at once */
6259 for( jj=12 ; jj < NPNONI ; jj++ ) stup.wfunc_param[jj].fixed = 0 ;
6260 FREEZE_POLYNO_PARAMS ; /* 07 Dec 2010 */
6261 COUNT_FREE_PARAMS(nbf) ;
6262 if( verb > 0 )
6263 INFO_message("Start Nonic/Poly9 warping: %d free parameters",nbf) ;
6264
6265 if( verb ) ctim = COX_cpu_time() ;
6266 rad = 0.03f ; crad = 0.003f ;
6267 nite = MAX(7777,nwarp_itemax) ;
6268 nbf = mri_genalign_scalar_optim( &stup , rad, crad, nite );
6269 if( verb ){
6270 dtim = COX_cpu_time() ;
6271 ININFO_message("- Nonic/Poly9 cost = %f ; %d funcs ; net CPU = %.1f s",
6272 stup.vbest,nbf,dtim-ctim) ;
6273 ctim = dtim ;
6274 }
6275 } else { /* the new way: cubic, then quintic, then heptic, then nonic */
6276 #undef NPOL
6277 #define NPOL(k) (((k)+1)*((k)+2)*((k)+3)/6-4)
6278 static int fst[8] = { 12+3*NPOL(3) , 12+3*NPOL(4) , 12+3*NPOL(5) ,
6279 12+3*NPOL(6) , 12+3*NPOL(7) , 12+3*NPOL(8) ,
6280 12+3*NPOL(9) } ;
6281 int pq , ngite=2 , ig ; char *eee ; float gfac ;
6282 eee = my_getenv("AFNI_NONIC_GRADUAL") ;
6283 if( eee != NULL && isdigit(*eee) ) ngite = (int)strtod(eee,NULL) ;
6284 for( ig=0 ; ig < ngite ; ig++ ){
6285 if( verb > 0 )
6286 INFO_message("Start iteration #%d/%d of Nonic/Poly9 gradual warp",ig+1,ngite) ;
6287 for( pq=0 ; pq < 7 ; pq++ ){
6288 for( jj=12 ; jj < NPNONI ; jj++ ) stup.wfunc_param[jj].fixed = 0 ;
6289 FREEZE_POLYNO_PARAMS ;
6290 for( jj=fst[pq] ; jj < NPNONI ; jj++ )
6291 if( stup.wfunc_param[jj].fixed == 0 ) stup.wfunc_param[jj].fixed = 1 ;
6292 COUNT_FREE_PARAMS(nbf) ;
6293 if( verb > 0 )
6294 ININFO_message("Level %d of Nonic/Poly9 warping: %d free parameters",pq+3,nbf) ;
6295 if( nbf == 0 ) continue ;
6296 if( pq > 0 || ig > 0 ){
6297 for( jj=0 ; jj < fst[pq] ; jj++ ){
6298 if( stup.wfunc_param[jj].fixed == 0 )
6299 stup.wfunc_param[jj].val_init = stup.wfunc_param[jj].val_out ;
6300 }
6301 }
6302 if( verb ) ctim = COX_cpu_time() ;
6303 gfac = 1.0f / sqrtf(ig+1.0f) ;
6304 rad = 0.05f*gfac ; crad = 0.003f*gfac ;
6305 nite = MAX(19*nbf,nwarp_itemax) ;
6306 nbf = mri_genalign_scalar_optim( &stup , rad, crad, nite );
6307 for( jj=0 ; jj < NPNONI ; jj++ ){ /* for fixers next time thru */
6308 if( stup.wfunc_param[jj].fixed == 0 )
6309 stup.wfunc_param[jj].val_fixed = stup.wfunc_param[jj].val_out ;
6310 }
6311 if( verb ){
6312 dtim = COX_cpu_time() ;
6313 ININFO_message("- Nonic/Poly9 cost = %f ; %d funcs ; net CPU = %.1f s",
6314 stup.vbest,nbf,dtim-ctim) ;
6315 ctim = dtim ;
6316 }
6317 } /* end of pq loop */
6318 } /* end of ig loop */
6319 } /* end of GRADUAL-osity */
6320
6321 /** if( verb > 1 ) PAROUT("- Nonic/Poly9 final") ; **/
6322 strcpy(warp_code_string,"nonic") ;
6323
6324 } else { /*-------- unimplemented ----------*/
6325
6326 ERROR_message("Unknown nonlinear warp type!") ;
6327
6328 } /* end of Warpfield */
6329
6330 } /* end of nonlinear warp */
6331 #endif /* ALLOW_NWARP */ /**************************************************/
6332
6333 /*-------- FINALLY HAVE FINISHED (hah!) ----------------------*/
6334
6335 mri_free(im_targ) ; im_targ = NULL ;
6336
6337 #ifdef ALLOW_METH_CHECK
6338 /*--- 27 Sep 2006: check if results are stable when
6339 we optimize a different cost functional ---*/
6340
6341 if( meth_check_count > 0 ){
6342 float pval[MAXPAR] , pdist , dmax ; int jmax,jtop ;
6343 float **aval = NULL ;
6344 int mm , mc ;
6345
6346 if( meth_check_count > 1 ){ /* save for median-izing at end */
6347 aval = (float **)malloc(sizeof(float *)*stup.wfunc_numpar) ;
6348 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
6349 aval[jj] = (float *)malloc(sizeof(float)*(meth_check_count+1)) ;
6350 aval[jj][0] = stup.wfunc_param[jj].val_out ;
6351 }
6352 }
6353
6354 PAROUT("Final fit") ;
6355 INFO_message("Checking %s (%s) vs other costs",
6356 meth_longname[meth_code-1] , meth_shortname[meth_code-1] ) ;
6357 for( mm=0 ; mm < meth_check_count ; mm++ ){
6358 mc = meth_check[mm] ; if( mc <= 0 ) continue ;
6359 if( verb > 1 ){
6360 ININFO_message("- checking vs cost %s (%s)",
6361 meth_longname[mc-1],meth_shortname[mc-1]) ;
6362 ctim = COX_cpu_time() ;
6363 }
6364 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* save output params */
6365 stup.wfunc_param[jj].val_init = pval[jj] = stup.wfunc_param[jj].val_out;
6366
6367 stup.match_code = mc ;
6368 nfunc = mri_genalign_scalar_optim( &stup, 33.3*conv_rad, conv_rad,666 );
6369 stup.match_code = meth_code ;
6370
6371 if( aval != NULL ){
6372 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6373 aval[jj][mm+1] = stup.wfunc_param[jj].val_out ;
6374 }
6375
6376 /* compute max fractional distance between 2 output parameter sets */
6377
6378 jtop = MIN( 9 , stup.wfunc_numpar ) ; jmax = 0 ;
6379 for( dmax=0.0f,jj=0 ; jj < jtop ; jj++ ){
6380 if( !stup.wfunc_param[jj].fixed ){
6381 pdist = fabsf( stup.wfunc_param[jj].val_out - pval[jj] )
6382 /(stup.wfunc_param[jj].max-stup.wfunc_param[jj].min) ;
6383 if( pdist > dmax ){ dmax = pdist ; jmax = jj ; }
6384 }
6385 }
6386
6387 #define CHECK_TOL 99.0
6388
6389 if( dmax > CHECK_TOL*conv_rad )
6390 WARNING_message(
6391 "Check vs %s (%s): max parameter discrepancy=%.4f%%! tolerance=%.4f%%",
6392 meth_longname[mc-1] , meth_shortname[mc-1] , 100.0*dmax , 100.0*CHECK_TOL*conv_rad ) ;
6393 else
6394 ININFO_message(
6395 "INFO: Check vs %s (%s): max parameter discrepancy=%.4f%% tolerance=%.4f%%",
6396 meth_longname[mc-1] , meth_shortname[mc-1] , 100.0*dmax , 100.0*CHECK_TOL*conv_rad ) ;
6397 PAROUT("Check fit") ;
6398 if( verb > 2 )
6399 ININFO_message("- Check net CPU time=%.1f s; funcs=%d; dmax=%f jmax=%d",
6400 COX_cpu_time()-ctim , nfunc , dmax , jmax ) ;
6401 if( do_allcost != 0 ){
6402 PAR_CPY(val_out) ; /* copy output parameters into allpar */
6403 allcost = mri_genalign_scalar_allcosts( &stup , allpar ) ;
6404 ININFO_message("allcost output: check %s",meth_shortname[mc-1]) ;
6405 for( jj=0 ; jj < GA_MATCH_METHNUM_SCALAR ; jj++ )
6406 fprintf(stderr," %-4s = %g\n",meth_shortname[jj],allcost->ar[jj]) ;
6407 KILL_floatvec(allcost) ;
6408 if( save_hist != NULL ){
6409 char fn[64] ; sprintf(fn,"allcost_check_%s",meth_shortname[mc-1]);
6410 SAVEHIST(fn,0);
6411 }
6412 }
6413
6414 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6415 stup.wfunc_param[jj].val_out = pval[jj] ; /* restore previous param */
6416 } /* end of loop over check methods */
6417
6418 if( aval != NULL ){ /* median-ize the parameter sets */
6419 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ){
6420 pval[jj] = qmed_float( meth_check_count+1 , aval[jj] ) ;
6421 free((void *)aval[jj]) ;
6422 }
6423 free((void *)aval) ;
6424 fprintf(stderr," + Median of Parameters =") ;
6425 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) fprintf(stderr," %.6f",pval[jj]) ;
6426 fprintf(stderr,"\n") ;
6427 if( meth_median_replace ){ /* replace final results with median! NOT GOOD */
6428 ININFO_message("Replacing Final parameters with Median") ;
6429 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6430 stup.wfunc_param[jj].val_out = pval[jj] ;
6431 }
6432 }
6433
6434 } /* end of checking */
6435 #endif
6436
6437 /*-- freeze warp-ing parameters (those after #0..5) for later rounds? --*/
6438 /*-- (count this among the almost never used options) --*/
6439
6440 if( warp_freeze && DSET_NVALS(dset_targ) > 1 ){ /* 10 Oct 2006 */
6441 for( jj=6 ; jj < stup.wfunc_numpar ; jj++ ){
6442 if( !stup.wfunc_param[jj].fixed ){
6443 if( verb > 1 ) INFO_message("Freezing parameter #%d [%s] = %.6f",
6444 jj+1 , stup.wfunc_param[jj].name ,
6445 stup.wfunc_param[jj].val_out ) ;
6446 stup.wfunc_param[jj].fixed = 2 ;
6447 stup.wfunc_param[jj].val_fixed = stup.wfunc_param[jj].val_out ;
6448 }
6449 }
6450 }
6451
6452 /*--- do we replace the base image with warped first target image? ---*/
6453 /*--- (count this among the almost never used options) ---*/
6454
6455 if( replace_base ){
6456 float pp[MAXPAR] ; MRI_IMAGE *aim ;
6457 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6458 pp[jj] = stup.wfunc_param[jj].val_out ;
6459 mri_free(im_base) ;
6460 if( verb > 1 ) INFO_message("Computing replacement base image") ;
6461 aim = (stup.ajimor != NULL) ? stup.ajimor : stup.ajim ;
6462 im_base =
6463 im_bset = mri_genalign_scalar_warpone(
6464 stup.wfunc_numpar , pp , stup.wfunc ,
6465 aim, nx_base,ny_base,nz_base, final_interp );
6466 #if 0
6467 im_wset = im_weig ; /* not needed, since stup 'remembers' the weight */
6468 #endif
6469 replace_base = 0 ; diffblur = 0 ;
6470 }
6471
6472 /*-- an another almost never used option --*/
6473
6474 if( replace_meth ){
6475 if( verb > 1 ) INFO_message("Replacing meth='%s' with '%s'",
6476 meth_shortname[meth_code] ,
6477 meth_shortname[replace_meth] ) ;
6478 meth_code = replace_meth; replace_meth = 0;
6479 }
6480
6481 /*---- get final DICOM coord transformation matrix [23 Jul 2007] ----*/
6482
6483 { float par[MAXPAR] ;
6484 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6485 par[jj] = stup.wfunc_param[jj].val_out ;
6486 #if 0
6487 mri_genalign_set_pgmat(1) ;
6488 #endif
6489 mri_genalign_affine( stup.wfunc_numpar , par , 0,NULL,NULL,NULL , NULL,NULL,NULL ) ;
6490 mri_genalign_affine_get_gammaijk( &qmat ) ;
6491 wmat = MAT44_MUL(targ_cmat,qmat) ; /* matrix multiplies to undo the befafter stuff */
6492 aff12_xyz = MAT44_MUL(wmat,base_cmat_inv) ; /* DICOM coord matrix */
6493 }
6494
6495 #if 0 /* lots of debugging output */
6496 DUMP_MAT44("targ_cmat",targ_cmat) ;
6497 DUMP_MAT44("targ_cmat_inv",targ_cmat_inv) ;
6498 DUMP_MAT44("base_cmat",base_cmat) ;
6499 DUMP_MAT44("base_cmat_inv",base_cmat_inv) ;
6500 DUMP_MAT44("aff12_xyz",aff12_xyz) ;
6501 DUMP_MAT44("aff12_ijk",qmat) ;
6502 #endif
6503
6504 /*-----------------------------------------------------------------------*/
6505 /*--------- at this point, val_out contains alignment parameters --------*/
6506 /*--------- for source/target sub-brick under consideration [kk] --------*/
6507 /*-----------------------------------------------------------------------*/
6508
6509 WRAP_IT_UP_BABY: /***** goto target !!!!! *****/
6510
6511 /* save parameters for the historical record */
6512
6513 if( parsave != NULL ){
6514 parsave[kk] = (float *)malloc(sizeof(float)*stup.wfunc_numpar) ;
6515 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6516 parsave[kk][jj] = stup.wfunc_param[jj].val_out ;
6517 }
6518
6519 /* save matrix for the hysterical record [23 Jul 2007] */
6520
6521 if( matsave != NULL ){
6522 if( ISVALID_MAT44(aff12_xyz) )
6523 matsave[kk] = aff12_xyz ;
6524 else
6525 LOAD_DIAG_MAT44(matsave[kk],1.0f,1.0f,1.0f) ;
6526 }
6527
6528 /**-- store warped volume into the output dataset --**/
6529
6530 if( dset_out != NULL ){
6531 MRI_IMAGE *aim = (stup.ajimor != NULL) ? stup.ajimor : stup.ajim ;
6532 /* lose obliquity if using 3dWarp for any transformation */
6533 /* recompute Tc (Cardinal transformation matrix for new grid output */
6534 THD_make_cardinal(dset_out); /* needed for oblique NIFTI datasets - 07/03/14 drg */
6535
6536 if( verb > 1 ) INFO_message("Computing output image") ;
6537 #if 0
6538 mri_genalign_set_pgmat(1) ;
6539 #endif
6540
6541 switch( apply_mode ){ /* are we applying a matrix or parameter set? */
6542
6543 default: /* applying parameter set */
6544
6545 AL_setup_warp_coords( epi_targ,epi_fe,epi_pe,epi_se,
6546 nxyz_dout, dxyz_dout, cmat_bout,
6547 nxyz_targ, dxyz_targ, cmat_tout ) ;
6548
6549 if( im_targ_vector == NULL ){
6550 im_targ = mri_genalign_scalar_warpone(
6551 stup.wfunc_numpar , parsave[kk] , stup.wfunc ,
6552 aim , nxout,nyout,nzout, final_interp ) ;
6553 } else { /* RGB image [12 May 2020] */
6554 im_targ = mri_genalign_scalar_warpone(
6555 stup.wfunc_numpar , parsave[kk] , stup.wfunc ,
6556 im_targ_vector , nxout,nyout,nzout, final_interp ) ;
6557 mri_free(im_targ_vector) ; im_targ_vector = NULL ;
6558 }
6559
6560 #ifdef ALLOW_NWARP /*********************************************************/
6561 if( nwarp_save_prefix != NULL ){ /* 10 Dec 2010: save map of warp itself */
6562 THD_3dim_dataset *wset; MRI_IMARR *wimar;
6563 MRI_IMAGE *xim,*yim,*zim,*vim=NULL; int iv=0,nw;
6564 wimar = mri_genalign_scalar_xyzwarp(
6565 stup.wfunc_numpar , parsave[kk] , stup.wfunc ,
6566 nxout , nyout , nzout ) ;
6567 nw = IMARR_COUNT(wimar) ;
6568 wset = EDIT_empty_copy(dset_out) ;
6569 EDIT_dset_items( wset ,
6570 ADN_prefix , nwarp_save_prefix ,
6571 ADN_nvals , (twodim_code) ? nw-1 : nw ,
6572 ADN_ntt , 0 ,
6573 ADN_datum_all , MRI_float ,
6574 ADN_none ) ;
6575 xim = IMARR_SUBIM(wimar,0); yim = IMARR_SUBIM(wimar,1);
6576 zim = IMARR_SUBIM(wimar,2); if( nw == 4 ) vim = IMARR_SUBIM(wimar,3) ;
6577 FREE_IMARR(wimar) ;
6578 if( twodim_code != 1 ){
6579 EDIT_BRICK_LABEL( wset , iv , "x_delta" ) ;
6580 EDIT_substitute_brick(wset,iv,MRI_float,MRI_FLOAT_PTR(xim)) ;
6581 mri_clear_data_pointer(xim) ; iv++ ;
6582 }
6583 if( twodim_code != 2 ){
6584 EDIT_BRICK_LABEL( wset , iv , "y_delta" ) ;
6585 EDIT_substitute_brick(wset,iv,MRI_float,MRI_FLOAT_PTR(yim)) ;
6586 mri_clear_data_pointer(yim) ; iv++ ;
6587 }
6588 if( twodim_code != 3 ){
6589 EDIT_BRICK_LABEL( wset , iv , "z_delta" ) ;
6590 EDIT_substitute_brick(wset,iv,MRI_float,MRI_FLOAT_PTR(zim)) ;
6591 mri_clear_data_pointer(zim) ; iv++ ;
6592 }
6593 if( vim != NULL ){
6594 EDIT_BRICK_LABEL( wset , iv , "hexvol" ) ;
6595 EDIT_substitute_brick(wset,iv,MRI_float,MRI_FLOAT_PTR(vim)) ;
6596 mri_clear_data_pointer(vim) ; mri_free(vim) ; iv++ ;
6597 }
6598 mri_free(xim) ; mri_free(yim) ; mri_free(zim) ;
6599 DSET_write(wset) ; WROTE_DSET(wset) ; DSET_delete(wset) ;
6600 } /* end of nwarp_save */
6601 #endif /* ALLOW_NWARP */ /**************************************************/
6602
6603 break ; /* end of default case */
6604
6605 case APPLY_AFF12:{ /* applying a matrix */
6606 float ap[12] ;
6607 wmat = MAT44_MUL(aff12_xyz,mast_cmat) ;
6608 qmat = MAT44_MUL(targ_cmat_inv,wmat) ; /* index transform matrix */
6609 UNLOAD_MAT44_AR(qmat,ap) ;
6610 if( im_targ_vector == NULL ){
6611 im_targ = mri_genalign_scalar_warpone(
6612 12 , ap , mri_genalign_mat44 ,
6613 aim , nxout,nyout,nzout, final_interp ) ;
6614 } else { /* RGB image [12 May 2020] - transform components */
6615 im_targ = mri_genalign_scalar_warpone(
6616 12 , ap , mri_genalign_mat44 ,
6617 im_targ_vector , nxout,nyout,nzout, final_interp ) ;
6618 mri_free(im_targ_vector) ; im_targ_vector = NULL ;
6619 }
6620 }
6621 break ;
6622
6623 } /* end of switch on apply_mode */
6624
6625 /*--- 04 Apr 2007: save matrix into dataset header ---*/
6626
6627 { static mat44 gam , gami ; char anam[64] ; float matar[12] , *psav ;
6628
6629 if( matsave != NULL )
6630 gam = matsave[kk] ;
6631 else if( ISVALID_MAT44(aff12_xyz) )
6632 gam = aff12_xyz ;
6633 else
6634 mri_genalign_affine_get_gammaxyz( &gam ) ; /* should not happen */
6635
6636 /* set the header attribute */
6637
6638 if( ISVALID_MAT44(gam) ){
6639 sprintf(anam,"ALLINEATE_MATVEC_B2S_%06d",kk) ;
6640 UNLOAD_MAT44_AR(gam,matar) ;
6641 THD_set_float_atr( dset_out->dblk , anam , 12 , matar ) ;
6642 gami = MAT44_INV(gam) ;
6643 sprintf(anam,"ALLINEATE_MATVEC_S2B_%06d",kk) ;
6644 UNLOAD_MAT44_AR(gami,matar) ;
6645 THD_set_float_atr( dset_out->dblk , anam , 12 , matar ) ;
6646 }
6647
6648 /* 22 Feb 2010: save the parameters as well */
6649
6650 if( kk == 0 && *warp_code_string != '\0' )
6651 THD_set_string_atr( dset_out->dblk , "ALLINEATE_WARP_TYPE" , warp_code_string ) ;
6652
6653 psav = (float *)malloc(sizeof(float)*stup.wfunc_numpar) ;
6654 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6655 psav[jj] = stup.wfunc_param[jj].val_out ;
6656 sprintf(anam,"ALLINEATE_PARAMS_%06d",kk) ;
6657 THD_set_float_atr( dset_out->dblk , anam , stup.wfunc_numpar , psav ) ;
6658 free(psav) ;
6659
6660 }
6661
6662 /*--- save sub-brick without scaling factor ---*/
6663
6664 if( targ_was_vector ){ /* RGB image [12 May 2020] */
6665 EDIT_substitute_brick( dset_out ,kk ,
6666 im_targ->kind , mri_data_pointer(im_targ) ) ;
6667 mri_clear_data_pointer(im_targ) ;
6668 } else if( floatize || targ_kind == MRI_float ){
6669 EDIT_substitute_brick( dset_out,kk,MRI_float, MRI_FLOAT_PTR(im_targ) );
6670 mri_clear_data_pointer(im_targ) ; /* data in im_targ saved directly */
6671 } else {
6672 EDIT_substscale_brick( dset_out,kk,MRI_float, MRI_FLOAT_PTR(im_targ),
6673 targ_kind ,
6674 (bfac == 0.0f) ? 1.0f : 0.0f ) ;
6675 }
6676 mri_free(im_targ) ; im_targ = NULL ;
6677
6678 if( usetemp && DSET_NVALS(dset_out) > 1 ) /* 31 Jan 2007 */
6679 mri_purge( DSET_BRICK(dset_out,kk) ) ;
6680 }
6681
6682 MEMORY_CHECK("end of sub-brick alignment") ;
6683
6684 } /***------------- end of loop over target sub-bricks ------------------***/
6685
6686 /*--------------- unload stuff we no longer need --------------------------*/
6687
6688 if( verb > 1 ) INFO_message("Unloading unneeded data") ;
6689
6690 DSET_unload(dset_targ) ;
6691 mri_free(im_base); mri_free(im_weig); mri_free(im_mask);
6692 #ifdef ALLOW_UNIFIZE
6693 mri_free(im_ubase);
6694 #endif
6695
6696 MRI_FREE(stup.bsim); MRI_FREE(stup.bsims);
6697 MRI_FREE(stup.ajim); MRI_FREE(stup.ajims); MRI_FREE(stup.bwght);
6698 MRI_FREE(stup.ajimor);
6699
6700 /***--- write output dataset to disk? ---***/
6701
6702 MEMORY_CHECK("end of sub-brick loop (after cleanup)") ;
6703
6704 if( dset_out != NULL ){
6705 DSET_write(dset_out); WROTE_DSET(dset_out); DSET_unload(dset_out);
6706 MEMORY_CHECK("after writing output dataset") ;
6707 }
6708
6709 /*--- save parameters to a file, if desired ---*/
6710
6711 if( param_save_1D != NULL && parsave != NULL ){
6712 FILE *fp ;
6713 fp = (strcmp(param_save_1D,"-") == 0) ? stdout
6714 : fopen(param_save_1D,"w") ;
6715 if( fp == NULL ) ERROR_exit("Can't open -1Dparam_save %s for output!?",param_save_1D);
6716 fprintf(fp,"# 3dAllineate parameters:\n") ;
6717 fprintf(fp,"#") ;
6718 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ ) /* 04 Dec 2010 */
6719 fprintf(fp," %s%c" , stup.wfunc_param[jj].name , /* add '$' for frozen */
6720 (stup.wfunc_param[jj].fixed == 2) ? '$' : ' ' ) ;
6721 fprintf(fp,"\n") ;
6722 for( kk=0 ; kk < DSET_NVALS(dset_targ) ; kk++ ){
6723 for( jj=0 ; jj < stup.wfunc_numpar ; jj++ )
6724 fprintf(fp," %.6f",parsave[kk][jj]) ;
6725 fprintf(fp,"\n") ; /* oops */
6726 }
6727 if( fp != stdout ){
6728 fclose(fp) ; if( verb ) INFO_message("Wrote -1Dparam_save %s",param_save_1D) ;
6729 }
6730 }
6731
6732 /*--- save matrices to disk, if so ordered by the omniscient user ---*/
6733
6734 if( matrix_save_1D != NULL && matsave != NULL ){
6735 FILE *fp ;
6736 float a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34 ;
6737 fp = (strcmp(matrix_save_1D,"-") == 0) ? stdout
6738 : fopen(matrix_save_1D,"w") ;
6739 if( fp == NULL ) ERROR_exit("Can't open -1Dmatrix_save %s for output!?",matrix_save_1D);
6740 fprintf(fp,"# 3dAllineate matrices (DICOM-to-DICOM, row-by-row):\n") ;
6741 for( kk=0 ; kk < DSET_NVALS(dset_targ) ; kk++ ){
6742 UNLOAD_MAT44(matsave[kk],a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34) ;
6743 fprintf(fp,
6744 " %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g %13.6g\n",
6745 a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34 ) ;
6746 }
6747 if( fp != stdout ){
6748 fclose(fp) ; if( verb ) INFO_message("Wrote -1Dmatrix_save %s",matrix_save_1D) ;
6749 }
6750 }
6751
6752 /*--------------- FREE AT LAST, FREE AT LAST (almost) ------------------*/
6753
6754 FREE_GA_setup(&stup) ;
6755 if( parsave != NULL ){
6756 for( kk=0 ; kk < DSET_NVALS(dset_targ) ; kk++ )
6757 if( parsave[kk] != NULL ) free((void *)parsave[kk]) ;
6758 free((void *)parsave) ;
6759 }
6760 if( matsave != NULL ) free((void *)matsave) ;
6761
6762 if( 1 || verb )
6763 INFO_message("3dAllineate: total CPU time = %.1f sec Elapsed = %.1f\n",
6764 COX_cpu_time() , COX_clock_time() ) ;
6765 MEMORY_CHECK("end of program (after final cleanup)") ;
6766 if( verb && apply_1D == NULL && prefix != NULL ){
6767 INFO_message( "###########################################################");
6768 INFO_message( "# PLEASE check results VISUALLY for alignment quality #");
6769
6770 if( (meth_code == GA_MATCH_PEARSON_LOCALS ||
6771 meth_code == GA_MATCH_PEARSON_LOCALA ||
6772 meth_code == GA_MATCH_LPC_MICHO_SCALAR ||
6773 meth_code == GA_MATCH_LPA_MICHO_SCALAR ) &&
6774 (!wtspecified) ){
6775 INFO_message("###########################################################");
6776 INFO_message("# '-autoweight' or some other voxelwise weighting #");
6777 INFO_message("# method is recommended when using -lpc or -lpa #");
6778 INFO_message("# If your results are not good, please try again. #");
6779 }
6780 }
6781 if( verb ){
6782 INFO_message("###########################################################");
6783 }
6784 if( !do_cmass && CMbad > 0 ){ /* 26 Feb 2020 */
6785 ININFO_message (" ") ;
6786 INFO_message ("***********************************************************") ;
6787 WARNING_message("-cmass was turned off, but might have been needed :(" ) ;
6788 ININFO_message (" Please check your results - PLEASE PLEASE PLEASE" ) ;
6789 INFO_message ("***********************************************************") ;
6790 }
6791
6792 exit(0) ;
6793 }
6794
6795 /*---------------------------------------------------------------------------*/
6796 /*! Turn an input image into a weighting factor (for -autoweight).
6797 If acod == 2, then make a binary mask at the end.
6798 If acod == 3, then make a boxed binary mask at the end.
6799 -----------------------------------------------------------------------------*/
6800
mri_weightize(MRI_IMAGE * im,int acod,int ndil,float aclip,float apow)6801 MRI_IMAGE * mri_weightize( MRI_IMAGE *im, int acod, int ndil, float aclip, float apow )
6802 {
6803 float *wf,clip,clip2 ;
6804 int xfade,yfade,zfade , nx,ny,nz,nxy,nxyz , ii,jj,kk,ff ;
6805 byte *mmm ;
6806 MRI_IMAGE *qim , *wim ;
6807
6808 /*-- copy input image --*/
6809
6810 qim = mri_to_float(im) ; wf = MRI_FLOAT_PTR(qim) ;
6811 nx = qim->nx; ny = qim->ny; nz = qim->nz; nxy = nx*ny; nxyz = nxy*nz;
6812 for( ii=0 ; ii < nxyz ; ii++ ) wf[ii] = fabsf(wf[ii]) ;
6813
6814 /*-- zero out along the edges --*/
6815 #undef WW
6816 #define WW(i,j,k) wf[(i)+(j)*nx+(k)*nxy]
6817
6818 xfade = (int)(0.05*qim->nx+3.0) ; /* number of points */
6819 yfade = (int)(0.05*qim->ny+3.0) ; /* along each face */
6820 zfade = (int)(0.05*qim->nz+3.0) ; /* to set to zero */
6821 if( 5*xfade >= qim->nx ) xfade = (qim->nx-1)/5 ;
6822 if( 5*yfade >= qim->ny ) yfade = (qim->ny-1)/5 ;
6823 if( 5*zfade >= qim->nz ) zfade = (qim->nz-1)/5 ;
6824 if( verb > 1 )
6825 ININFO_message("Weightize: xfade=%d yfade=%d zfade=%d",xfade,yfade,zfade);
6826 for( jj=0 ; jj < ny ; jj++ )
6827 for( ii=0 ; ii < nx ; ii++ )
6828 for( ff=0 ; ff < zfade ; ff++ ) WW(ii,jj,ff) = WW(ii,jj,nz-1-ff) = 0.0f;
6829 for( kk=0 ; kk < nz ; kk++ )
6830 for( jj=0 ; jj < ny ; jj++ )
6831 for( ff=0 ; ff < xfade ; ff++ ) WW(ff,jj,kk) = WW(nx-1-ff,jj,kk) = 0.0f;
6832 for( kk=0 ; kk < nz ; kk++ )
6833 for( ii=0 ; ii < nx ; ii++ )
6834 for( ff=0 ; ff < yfade ; ff++ ) WW(ii,ff,kk) = WW(ii,ny-1-ff,kk) = 0.0f;
6835
6836 if( aclip > 0.0f ){ /* 31 Jul 2007 */
6837 int nleft , nclip ;
6838 for( nclip=nleft=ii=0 ; ii < nxyz ; ii++ ){
6839 if( wf[ii] > 0.0f ){
6840 if( wf[ii] < aclip ){ nclip++; wf[ii] = 0.0f; } else nleft++ ;
6841 }
6842 }
6843 if( verb > 1 ) ININFO_message("Weightize: user clip=%g #clipped=%d #left=%d",
6844 aclip,nclip,nleft) ;
6845 }
6846
6847 /*-- squash super-large values down to reasonability --*/
6848
6849 clip = 3.0f * THD_cliplevel(qim,0.5f) ;
6850 if( verb > 1 ) ININFO_message("Weightize: (unblurred) top clip=%g",clip) ;
6851 for( ii=0 ; ii < nxyz ; ii++ ) if( wf[ii] > clip ) wf[ii] = clip ;
6852
6853 /*-- blur a little: median then Gaussian;
6854 the idea is that the median filter smashes localized spikes,
6855 then the Gaussian filter does a litte extra general smoothing. --*/
6856
6857 mmm = (byte *)malloc( sizeof(byte)*nxyz ) ;
6858 for( ii=0 ; ii < nxyz ; ii++ ) mmm[ii] = (wf[ii] > 0.0f) ; /* mask */
6859 if( wt_medsmooth > 0.0f ){
6860 wim = mri_medianfilter( qim , wt_medsmooth , mmm , 0 ) ; mri_free(qim) ;
6861 } else {
6862 wim = qim ;
6863 }
6864 wf = MRI_FLOAT_PTR(wim) ;
6865 if( wt_gausmooth > 0.0f )
6866 FIR_blur_volume_3d( wim->nx , wim->ny , wim->nz ,
6867 1.0f , 1.0f , 1.0f , wf ,
6868 wt_gausmooth , wt_gausmooth , wt_gausmooth ) ;
6869
6870 /*-- clip off small values, and
6871 keep only the largest cluster of supra threshold voxels --*/
6872
6873 clip = 0.05f * mri_max(wim) ;
6874 clip2 = 0.33f * THD_cliplevel(wim,0.33f) ;
6875 clip = MAX(clip,clip2) ;
6876 if( verb > 1 ) ININFO_message("Weightize: (blurred) bot clip=%g",clip) ;
6877 for( jj=ii=0 ; ii < nxyz ; ii++ ){
6878 if( wf[ii] >= clip ){ jj++ ; mmm[ii] = 1 ; }
6879 else { mmm[ii] = 0 ; }
6880 }
6881 if( verb > 1 ) ININFO_message("Weightize: %d voxels survive clip",jj) ;
6882 if( ! doing_2D ){ /* 28 Apr 2020 */
6883 THD_mask_clust( nx,ny,nz, mmm ) ;
6884 THD_mask_erode( nx,ny,nz, mmm, 1, 2 ) ; /* cf. thd_automask.c NN2 */
6885 THD_mask_clust( nx,ny,nz, mmm ) ;
6886 } else {
6887 THD_mask_remove_isolas( nx,ny,nz , mmm ) ;
6888 }
6889 for( jj=nxyz,ii=0 ; ii < nxyz ; ii++ ){
6890 if( !mmm[ii] ){ wf[ii] = 0.0f ; jj-- ; }
6891 }
6892 free((void *)mmm) ;
6893 if( verb > 1 ) ININFO_message("Weightize: %d voxels survive clusterize",jj) ;
6894
6895 /*-- convert to 0..1 range [10 Sep 2007] --*/
6896
6897 clip = 0.0f ;
6898 for( ii=0 ; ii < nxyz ; ii++ ) if( wf[ii] > clip ) clip = wf[ii] ;
6899 if( clip == 0.0f )
6900 ERROR_exit("Can't compute autoweight: max value seen as 0") ;
6901 clip = 1.0f / clip ;
6902 for( ii=0 ; ii < nxyz ; ii++ ) wf[ii] *= clip ;
6903
6904 /*-- power? --*/
6905
6906 if( apow > 0.0f && apow != 1.0f ){
6907 if( verb > 1 ) ININFO_message("Weightize: raising to %g power",apow) ;
6908 for( ii=0 ; ii < nxyz ; ii++ )
6909 if( wf[ii] > 0.0f ) wf[ii] = powf( wf[ii] , apow ) ;
6910 }
6911
6912 /*-- binarize (acod==2)? boxize (acod==3)? --*/
6913
6914 #undef BPAD
6915 #define BPAD 4
6916 if( acod == 2 || acod == 3 ){ /* binary weight: mask=2 or maskbox=3 */
6917 if( verb > 1 ) ININFO_message("Weightize: binarizing") ;
6918 for( ii=0 ; ii < nxyz ; ii++ ) if( wf[ii] != 0.0f ) wf[ii] = 1.0f ;
6919 if( ndil > 0 ){ /* 01 Mar 2007: dilation */
6920 byte *mmm = (byte *)malloc(sizeof(byte)*nxyz) ;
6921 if( verb > 1 ) ININFO_message("Weightize: dilating") ;
6922 for( ii=0 ; ii < nxyz ; ii++ ) mmm[ii] = (wf[ii] != 0.0f) ;
6923 for( ii=0 ; ii < ndil ; ii++ ){
6924 THD_mask_dilate ( nx,ny,nz , mmm , 3, 2 ) ;
6925 THD_mask_fillin_once( nx,ny,nz , mmm , 2 ) ;
6926 }
6927 for( ii=0 ; ii < nxyz ; ii++ ) wf[ii] = (float)mmm[ii] ;
6928 free(mmm) ;
6929 }
6930 if( acod == 3 ){ /* boxize */
6931 int xm,xp , ym,yp , zm,zp ;
6932 MRI_autobbox_clust(0) ;
6933 MRI_autobbox( wim , &xm,&xp , &ym,&yp , &zm,&zp ) ;
6934 xm -= BPAD ; if( xm < 1 ) xm = 1 ;
6935 ym -= BPAD ; if( ym < 1 ) ym = 1 ;
6936 zm -= BPAD ; if( zm < 1 ) zm = 1 ;
6937 xp += BPAD ; if( xp > nx-2 ) xp = nx-2 ;
6938 yp += BPAD ; if( yp > ny-2 ) yp = ny-2 ;
6939 zp += BPAD ; if( zp > nz-2 ) zp = nz-2 ;
6940 if( verb > 1 )
6941 ININFO_message("Weightize: box=%d..%d X %d..%d X %d..%d = %d voxels",
6942 xm,xp , ym,yp , zm,zp , (xp-xm+1)*(yp-ym+1)*(zp-zm+1) ) ;
6943 for( kk=zm ; kk <= zp ; kk++ )
6944 for( jj=ym ; jj <= yp ; jj++ )
6945 for( ii=xm ; ii <= xp ; ii++ ) WW(ii,jj,kk) = 1.0f ;
6946 }
6947 }
6948
6949 return wim ;
6950 }
6951
6952 /*---------------------------------------------------------------------------*/
6953 /*! Return the L_infinity distance between two parameter vectors. */
6954
param_dist(GA_setup * stp,float * aa,float * bb)6955 float param_dist( GA_setup *stp , float *aa , float *bb )
6956 {
6957 int jj ; float ap, bp, pdist, dmax ;
6958
6959 if( stp == NULL || aa == NULL || bb == NULL ) return 1.0f ;
6960
6961 dmax = 0.0f ;
6962 for( jj=0 ; jj < stp->wfunc_numpar ; jj++ ){
6963 if( !stp->wfunc_param[jj].fixed ){
6964 ap = aa[jj] ; bp = bb[jj] ;
6965 pdist = fabsf(ap-bp)
6966 / (stp->wfunc_param[jj].max - stp->wfunc_param[jj].min) ;
6967 if( pdist > dmax ) dmax = pdist ;
6968 }
6969 }
6970 return dmax ;
6971 }
6972
6973 /*---------------------------------------------------------------------------*/
6974 /*! Setup before and after index-to-coordinate matrices in the warp func.
6975 See the notes at the end of this file for the gruesome details.
6976 -----------------------------------------------------------------------------*/
6977
AL_setup_warp_coords(int epi_targ,int epi_fe,int epi_pe,int epi_se,int * nxyz_base,float * dxyz_base,mat44 base_cmat,int * nxyz_targ,float * dxyz_targ,mat44 targ_cmat)6978 void AL_setup_warp_coords( int epi_targ , int epi_fe, int epi_pe, int epi_se,
6979 int *nxyz_base, float *dxyz_base, mat44 base_cmat,
6980 int *nxyz_targ, float *dxyz_targ, mat44 targ_cmat )
6981 {
6982 mat44 cmat_before , imat_after , gmat,tmat,qmat ;
6983 float *dijk ; int *nijk ;
6984
6985 if( epi_targ < 0 ){ /*---------- no EPI info given ----------*/
6986
6987 /* [it] = inv[Ct] [S] [D] [U] [Cb] [ib]
6988 ------- ----------- --------
6989 [after] [transform] [before] */
6990
6991 imat_after = MAT44_INV(targ_cmat) ; /* xyz -> ijk for target */
6992 cmat_before = base_cmat ; /* ijk -> xyz for base */
6993
6994 } else if( epi_targ == 1 ){ /*---------- target is EPI --------------*/
6995
6996 dijk = dxyz_targ ; nijk = nxyz_targ ;
6997
6998 /* -FPS kij should have [ 0 0 dk -mk ]
6999 [gmat] = [ di 0 0 -mi ]
7000 [ 0 dj 0 -mj ]
7001 [ 0 0 0 1 ]
7002 In this example, epi_fe=2, epi_pe=0, epi_se=1 */
7003
7004 ZERO_MAT44(gmat) ;
7005 gmat.m[0][epi_fe] = dijk[epi_fe] ;
7006 gmat.m[1][epi_pe] = dijk[epi_pe] ;
7007 gmat.m[2][epi_se] = dijk[epi_se] ;
7008 gmat.m[0][3] = -0.5f * dijk[epi_fe] * (nijk[epi_fe]-1) ;
7009 gmat.m[1][3] = -0.5f * dijk[epi_pe] * (nijk[epi_pe]-1) ;
7010 gmat.m[2][3] = -0.5f * dijk[epi_se] * (nijk[epi_se]-1) ;
7011
7012 /* [it] = inv[Gt] [S] [D] [U] inv[Rt] [Cb] [ib]
7013 ------- ----------- ------------
7014 [after] [transform] [before] where [Ct] = [Rt] [Gt] */
7015
7016 imat_after = MAT44_INV(gmat) ;
7017 tmat = MAT44_INV(targ_cmat) ; /* inv[Ct] */
7018 qmat = MAT44_MUL(tmat,base_cmat) ; /* inv[Ct] [Cb] */
7019 cmat_before = MAT44_MUL(gmat,qmat) ; /* [G] inv[Ct] [Cb] */
7020
7021 } else { /*---------- base is EPI ----------------*/
7022
7023 dijk = dxyz_base ; nijk = nxyz_base ;
7024
7025 ZERO_MAT44(gmat) ;
7026 gmat.m[0][epi_fe] = dijk[epi_fe] ;
7027 gmat.m[1][epi_pe] = dijk[epi_pe] ;
7028 gmat.m[2][epi_se] = dijk[epi_se] ;
7029 gmat.m[0][3] = -0.5f * dijk[epi_fe] * (nijk[epi_fe]-1) ;
7030 gmat.m[1][3] = -0.5f * dijk[epi_pe] * (nijk[epi_pe]-1) ;
7031 gmat.m[2][3] = -0.5f * dijk[epi_se] * (nijk[epi_se]-1) ;
7032
7033 /* [it] = inv[Ct] [Rb] [U] [S] [D] [Gb] [ib]
7034 ------------ ----------- --------
7035 [after] [transform] [before] where [Cb] = [Rb] [Gb] */
7036
7037 cmat_before = gmat ; /* [Gb] */
7038 qmat = MAT44_INV(gmat) ; /* inv[Gb] */
7039 qmat = MAT44_MUL(base_cmat,qmat) ; /* [Cb] inv[Gb] = [Rb] */
7040 tmat = MAT44_INV(targ_cmat) ; /* inv[Ct] */
7041 imat_after = MAT44_MUL(tmat,qmat) ; /* inv[Ct] [Rb] */
7042 }
7043
7044 /*-- actually let the warping function 'know' about these matrices --*/
7045
7046 mri_genalign_affine_set_befafter( &cmat_before , &imat_after ) ;
7047
7048 return ;
7049 }
7050
7051 /* create MRI_IMAGE Identity parameters (not affine matrix) */
mri_identity_params(void)7052 MRI_IMAGE * mri_identity_params(void)
7053 {
7054
7055 MRI_IMAGE *om;
7056 float id[] = {0,0,0, 0,0,0, 1,1,1, 0,0,0};
7057 float *oar;
7058 int ii;
7059
7060 om = mri_new( 12 , 1 , MRI_float ) ;
7061 oar = MRI_FLOAT_PTR(om) ;
7062
7063 for( ii=0 ; ii < 12 ; ii++ )
7064 oar[ii] = id[ii] ;
7065
7066 RETURN(om) ;
7067 }
7068
7069 #ifdef USE_OMP
7070 #include "mri_genalign_util.c"
7071 #include "mri_genalign.c"
7072 #include "thd_correlate.c"
7073 #endif
7074
7075 /*----------------------------------------------------------------------------*/
7076 #if 0
7077 #undef MMM
7078 #define MMM(i,j,k) mmm[(i)+(j)*nx+(k)*nxy]
7079
7080 int * mri_edgesize( MRI_IMAGE *im ) /* 13 Aug 2007 */
7081 {
7082 byte *mmm ;
7083 int ii,jj,kk , nx,ny,nz , nxy ;
7084 static int eijk[6] ;
7085
7086 ENTRY("mri_edgesize") ;
7087
7088 if( im == NULL ) RETURN( NULL );
7089
7090 nx = im->nx ; ny = im->ny ; nz = im->nz ; nxy = nx*ny ;
7091
7092 STATUS("automask-ing on the cheap") ;
7093
7094 THD_automask_set_cheapo(1) ;
7095 mmm = mri_automask_image( im ) ;
7096 if( mmm == NULL ) RETURN( NULL );
7097
7098 /* check i-direction */
7099
7100 STATUS("check i-direction") ;
7101
7102 for( ii=0 ; ii < nx ; ii++ ){
7103 for( kk=0 ; kk < nz ; kk++ ){
7104 for( jj=0 ; jj < ny ; jj++ ) if( MMM(ii,jj,kk) ) goto I1 ;
7105 }}
7106 I1: eijk[0] = ii ;
7107 for( ii=nx-1 ; ii >= 0 ; ii-- ){
7108 for( kk=0 ; kk < nz ; kk++ ){
7109 for( jj=0 ; jj < ny ; jj++ ) if( MMM(ii,jj,kk) ) goto I2 ;
7110 }}
7111 I2: eijk[1] = nx-1-ii ;
7112
7113 /* check j-direction */
7114
7115 STATUS("check j-direction") ;
7116
7117 for( jj=0 ; jj < ny ; jj++ ){
7118 for( kk=0 ; kk < nz ; kk++ ){
7119 for( ii=0 ; ii < nx ; ii++ ) if( MMM(ii,jj,kk) ) goto J1 ;
7120 }}
7121 J1: eijk[2] = jj ;
7122 for( jj=ny-1 ; jj >= 0 ; jj-- ){
7123 for( kk=0 ; kk < nz ; kk++ ){
7124 for( ii=0 ; ii < nx ; ii++ ) if( MMM(ii,jj,kk) ) goto J2 ;
7125 }}
7126 J2: eijk[3] = ny-1-jj ;
7127
7128 /* check k-direction */
7129
7130 STATUS("check k-direction") ;
7131
7132 for( kk=0 ; kk < nz ; kk++ ){
7133 for( jj=0 ; jj < ny ; jj++ ){
7134 for( ii=0 ; ii < nx ; ii++ ) if( MMM(ii,jj,kk) ) goto K1 ;
7135 }}
7136 K1: eijk[4] = kk ;
7137 for( kk=nz-1 ; kk >= 0 ; kk-- ){
7138 for( jj=0 ; jj < ny ; jj++ ){
7139 for( ii=0 ; ii < nx ; ii++ ) if( MMM(ii,jj,kk) ) goto K2 ;
7140 }}
7141 K2: eijk[5] = nz-1-kk ;
7142
7143 free(mmm) ; RETURN( eijk );
7144 }
7145 #endif
7146
7147 /******************************************************************************
7148 *******************************************************************************
7149
7150 ==============================================================
7151 ===== Notes on Coordinates and Indexes - RWCox - 05 Oct 2006 =====
7152 ==============================================================
7153
7154 The base and target datasets each have their own coordinate systems and
7155 indexes. We use 4x4 matrices to represent affine transformations, and
7156 4-vectors to represent coordinates and indexes. (The last row of a 4x4
7157 matrix is [0 0 0 1] and the last element of a 4-vector is always 1.)
7158 The index-to-coordinate transformations for base and target are given by
7159
7160 [xb] = [Cb] [ib]
7161 [xt] = [Ct] [it]
7162
7163 where [Cb] and [Ct] are the dset->daxes->ijk_to_dicom matrices in the
7164 datasets' header.
7165
7166 The 4x4 affine transformation matrix is not directly parametrized by its 12
7167 non-trivial elements. To give control over and meaning to the parameters,
7168 the matrix is instead modeled as
7169
7170 [T] = [S] [D] [U]
7171
7172 where [S] is a shear matrix, [D] is a diagonal scaling matrix, and [U] is
7173 a proper orthogonal matrix. If we wish to restrict the transformation [T]
7174 to rigid body movement, for example, then we can fix the [S] and [D]
7175 matrices to be the identity.
7176
7177 N.B.: The shift matrix [H] can be inserted before or after the [S][D][U]
7178 product, as desired, so we'd really have [H][S][D][U] for dcode==DELTA_AFTER
7179 and [S][D][U][H] for dcode==DELTA_BEFORE. [H] must be a matrix of the form
7180 [ 1 0 0 a ]
7181 [ 0 1 0 b ]
7182 [ 0 0 1 c ]
7183 [ 0 0 0 1 ]
7184 where {a,b,c} are the shifts. I will ignore [H] in what follows. Also,
7185 the order [S][D][U] can be altered by the user, which I'll pretty much
7186 ignore below, as well.
7187
7188 For EPI data, we may want to restrict the transformation parameters so as
7189 to treat the phase-encoding direction differently than the frequency- and
7190 slice-encoding directions. However, the matrices as described above mean
7191 that the [T] matrix components apply to DICOM coordinates, which may not
7192 be aligned with the FPS directions in the image. In such a case, putting
7193 restrictions on the [T] parameters will not translate in a simple way into
7194 FPS coordinates.
7195
7196 The solution is to break the transformation from indexes to spatial
7197 coordinates into two pieces. Let [C] = [R] [G], where [C] is an index-to
7198 DICOM space matrix, [G] is a matrix that transforms indexes to FPS coordinates,
7199 and [R] is "what's left" (should be a rotation matrix, possibly with
7200 det[R]=-1). A sample [G] is
7201
7202 [ 0 0 dk -mk ]
7203 [G] = [ di 0 0 -mi ]
7204 [ 0 dj 0 -mj ]
7205 [ 0 0 0 1 ]
7206
7207 where the dataset is stored with '-FPS kij':
7208 i=P (phase-encoding), j=S (slice-encoding), and k=F (frequency-encoding);
7209 d{i,j,k} is the grid spacing along the respective dimensions; and
7210 m{i,j,k} is the coordinate at the volume center: e.g., mi=0.5*di*(ni-1).
7211 (Here, 'i' refers to the first index in the dataset, etc.)
7212
7213 If we break up [Ct] this way, then the transformation is
7214
7215 [xt] = [S] [D] [U] [xb], or
7216 [xb] = inv[U] inv[D] inv[S] [xt]
7217 = inv[U] inv[D] inv[S] [Rt] [Gt] [it]
7218
7219 and inv[T] is applied to the DICOM ordered coordinates in [Rt] [Gt] [it].
7220 If we want to apply inv[T] to the FPS ordered coordinates, then we change
7221 the last equation above to
7222
7223 [xb] = [Rt] inv[U] inv[D] inv[S] [Gt] [it]
7224
7225 where inv[T] is now applied to coordinates where xyz=FPS. Now, restricting
7226 the parameters in [T] applies directly to FPS coordinates (e.g., fix the
7227 scaling factor in [D] along the z-axis to 1, and then there will be no
7228 stretching/shrinking of the data along the slice-encoding direction). The
7229 final multiplication by [Rt] rotates the inv[T]-transformed FPS coordinates
7230 to DICOM order.
7231
7232 So, if the target dataset is the EPI dataset, then the transformation from
7233 [ib] to [it] is expressed at
7234
7235 [it] = inv[Gt] [S] [D] [U] inv[Rt] [Cb] [ib]
7236 ------- ----------- ------------
7237 [after] [transform] [before]
7238
7239 where the [transform] matrix is what the parameter searching is all about,
7240 and the [before] and [after] matrices are fixed.
7241
7242 If the base dataset is the EPI dataset, on the other hand, then the index-
7243 to-index transformation (what is really needed, after all) is
7244
7245 [it] = inv[Ct] [Rb] [U] [D] [S] [Gb] [ib]
7246 ------------ ----------- --------
7247 [after] [transform] [before]
7248
7249 (N.B.: The SDU order has been inverted to UDS, on the presumption that
7250 we will control the scaling and shear in the FPS coordinate system given
7251 by [Gb][ib].) In the 'normal' case, where either (a) we are going to allow
7252 full transform generality, or (b) no particular distortions of the image are
7253 to be specially allowed for, then we simply have
7254
7255 [it] = inv[Ct] [S] [D] [U] [Cb] [ib]
7256 ------- ----------- --------
7257 [after] [transform] [before]
7258
7259 All of these cases are possible. They will be especially important if/when
7260 nonlinear warping is allowed in place of the simple [S][D][U] transformation,
7261 the user needs to restrict the warping to occur only in the P-direction, and
7262 the data slices are oblique.
7263
7264 *******************************************************************************
7265 *******************************************************************************/
7266