1
2 /*****************************************************************************/
3 /*** Generally, this set of functions is designed to be #include-d ***/
4 /*** and compiled with OpenMP enabled (USE_OMP #define-d), for speed ***/
5 /*****************************************************************************/
6 /*** These functions are AFNI's implementation of nonlinear spatial ***/
7 /*** warping, and are used by (at least) these programs: ***/
8 /*** 3dQwarp 3dNwarpAdjust 3dNwarpApply ***/
9 /*** 3dNwarpCat 3dNwarpFuncs 3dNwarpXYZ ***/
10 /*****************************************************************************/
11
12 /***-------------------------------------------------------***/
13 /*** Search forward for TOC to find the Table of Contents, ***/
14 /*** wherein you will find lengthy commentary on warping. ***/
15 /***-------------------------------------------------------***/
16
17 /***-----------------------------------------------------------------------
18 Potential housecleaning chores: [Dec 2018]
19 DONE * remove the USE_HLOADER code forever
20 DONE * remove the ALLOW_DUPLO code forever
21 DONE * don't need ALLOW_QMODE #define - that is, make this permanent;
22 DONE and same for ALLOW_PLUSMINUS
23
24 BUT * don't remove/alter ALLOW_BASIS5 and ALLOW_INEDGE yet;
25 still on the fence about these, and the former is a lot
26 of complex code that shouldn't be cast aside just yet
27 * these options aren't useful in general, but there may be
28 some cases where they make a difference (or maybe not)
29
30 TBD * Remove external slopes and replace with affine transformation
31 that applies internally and externally
32 TBD * Remove 3dNwarpCalc functionality, as it becomes complicated
33 when the affine part of the warp is separated from the small
34 wiggly (nonlinear) part -- the grid domain of a warp sometimes
35 has to change when an operation (e.g., inverse) is performed.
36 ---------------------------------------------------------------------------***/
37
38 /*--- include some vitally needed headers ---*/
39
40 #include "mrilib.h"
41 #include "r_new_resam_dset.h"
42
43 /*--- some OpenMP housekeeping setup ---*/
44
45 #ifdef USE_OMP
46 # include <omp.h>
47 # define NUM_DHARRAY 6
48 static int nthmax=1 ; /* size of temp arrays below: */
49 static double *dhaar=NULL ; /* must be set in main program */
50 static double *dhbbr=NULL ; /* see 3dQwarp.c for example */
51 static double *dhccr=NULL ;
52 static double *dhddr=NULL ; /* these arrays are used to store */
53 static double *dheer=NULL ; /* cumulative values on a per-thread */
54 static double *dhffr=NULL ; /* basis for final summation later */
55 #else
56 # define nthmax 1
57 static double dhaar[1] ; /* with only 1 'thread' in OpenMP-free */
58 static double dhbbr[1] ; /* code, these arrays can be fixed size */
59 static double dhccr[1] ;
60 static double dhddr[1] ;
61 static double dheer[1] ;
62 static double dhffr[1] ;
63 #endif
64
65 #define DEBUG_CATLIST /* used to check progress of warp catenation */
66
67 /*----------------------------------------------------------------------------*/
68 /*--- for debugging memory usage, when there is a problem :( --------------*/
69 /*--- not a good idea with OpenMP: mcw_malloc isn't thread safe --------------*/
70
71 #ifdef USE_OMP
72 # undef DEBUG_MEMORY
73 # else
74 # define DEBUG_MEMORY
75 #endif
76 #undef MEMORY_CHECK
77 #undef MEMORY_SHORT
78
79 /*--- use mcw_malloc() functions for memory checking, if available ----*/
80 #if defined(DEBUG_MEMORY) && defined(USING_MCW_MALLOC) /*---------------------*/
81
82 # define MEMORY_CHECK(mm) \
83 do{ long long nb = mcw_malloc_total() ; \
84 if( nb > 0 ) ININFO_message("Memory = %s (%s): %s" , \
85 commaized_integer_string(nb) , \
86 approximate_number_string((double)nb) , (mm) ) ; \
87 } while(0)
88
wans(void)89 static char * wans(void)
90 { static char sss[256] ;
91 long long nb = mcw_malloc_total() ;
92 if( nb > 0 )
93 sprintf( sss , "{%s}" , approximate_number_string((double)nb) ) ;
94 else
95 strcpy( sss , "\0" ) ;
96 return sss ;
97 }
98
99 # define MEMORY_SHORT wans()
100
101 /*--- try something else for memory checking --------------------------*/
102 #elif defined(DEBUG_MEMORY) && ( defined(__linux__) || defined(__FreeBSD__) )
103
104 # define MEMORY_CHECK(mm) show_malloc_stats(mm) ;
105 # define MEMORY_SHORT "\0"
106
107 #if defined(__linux__)
108 # include <malloc.h>
109 #elif defined(__FreeBSD__) && !defined(__DragonFly__)
110 # include <stdlib.h>
111 # include <malloc_np.h>
112 #endif
113
show_malloc_stats(char * mesg)114 static void show_malloc_stats(char *mesg) /*-- lifted from Rick R --*/
115 {
116 #if defined(__linux__)
117 INFO_message("Memory usage: %s",mesg) ;
118 malloc_stats();
119 #elif defined(__FreeBSD__) && !defined(__DragonFly__)
120 INFO_message("Memory usage: %s",mesg) ;
121 malloc_stats_print(NULL, NULL, "g");
122 #endif
123 /* nothing for MacOS X, as Apple doesn't supply such functions :( */
124 }
125
126 #endif /* memory checking possibilities --------------------------------------*/
127
128 /* Backup (nilpotent) definitions for the MEMORY_ macros */
129
130 #ifndef MEMORY_CHECK
131 # define MEMORY_CHECK(mm) /* nada */
132 #endif
133 #ifndef MEMORY_SHORT
134 # define MEMORY_SHORT "\0"
135 #endif
136 /*----------------------------------------------------------------------------*/
137
138 /*..........................................................................*/
139 /** Note that the functions needed only in 3dQwarp (4000+ lines of code)
140 are only compiled if macro ALLOW_QWARP is #define-d -- see 3dQwarp.c.
141 Search ahead for string Q1 for the table of contents for this code.
142 *//*........................................................................*/
143
144 #ifdef ALLOW_QWARP
145 #include "thd_incorrelate.c" /* for the 3dQwarp cost (INCOR) functions */
146 #endif
147
148 /* macro to free a pointer if it's not NULL */
149
150 #undef FREEIFNN
151 #define FREEIFNN(x) do{ if((x)!=NULL){ free((void *)(x)); (x)=NULL;} } while(0)
152
153 /*--- minimum num grid points in a given direction ---*/
154 /*--- also is minimum patch size for 3dQwarp funcs ---*/
155
156 #undef NGMIN
157 #define NGMIN 5 /* if Hngmin goes this small, things become VERY slow!! */
158 #undef NGMINS
159 #define NGMINS "5" /* string version of the above -- must match! */
160
161 #undef NGMIN_Q
162 #define NGMIN_Q 7 /* smallest grid allowed for quintic warp */
163
164 /*--- Note the basis[345] warps are not used by ordinary mortals ---*/
165 /*--- and are only enabled if ALLOW_BASIS5 is #define-d, below ---*/
166
167 #undef ALLOW_BASIS5 /*--- (dis)allow use of the 'basis5' functions ---*/
168
169 #undef NGMIN_PLUS_3 /* smallest grid for basis5 warp */
170 #define NGMIN_PLUS_3 11
171
172 #undef NGMAX_PLUS_3
173 #define NGMAX_PLUS_3 23 /* largest grid for basis5 warp (-5final) */
174
175 #undef NGMIN_PLUS_2 /* smallest grid for basis4 warp */
176 #define NGMIN_PLUS_2 9
177
178 #undef NGMAX_PLUS_2
179 #define NGMAX_PLUS_2 23 /* largest grid for basis4 warp (-4final) */
180
181 #undef NGMIN_PLUS_1
182 #define NGMIN_PLUS_1 7 /* smallest grid for basis3 warp */
183
184 #undef NGMAX_PLUS_1
185 #define NGMAX_PLUS_1 23 /* largest grid for basis3 warp (-3final) */
186
187 #undef NVOXMAX_PLUS
188 #define NVOXMAX_PLUS 12168 /* 23^3+1 */
189
190 /*-*/
191 /*- for 3dQwarp -verb, strings to print describing the warps being worked on -*/
192 /*-*/
193
194 #define WARP_CODE_STRING(wc) \
195 ( (wc == MRI_QUINTIC) ? "quint81" \
196 : (wc == MRI_QUINTIC_LITE) ? "quint30" \
197 : (wc == MRI_SINCC) ? "sincomp" \
198 : (wc == MRI_CUBIC_PLUS_1 ) ? "cubic30" \
199 : (wc == MRI_CUBIC_PLUS_2 ) ? "cubic60" \
200 : (wc == MRI_CUBIC_PLUS_3 ) ? "cubc105" \
201 : (wc == MRI_CUBIC_LITE ) ? "cubic12" : "cubic24" )
202
203 /*- define if incremental warp is sinc, quintic, or cubic -*/
204
205 #define WARP_IS_SINCC(wc) (wc == MRI_SINCC)
206 #define WARP_IS_QUINTIC(wc) ( (wc == MRI_QUINTIC) || (wc == MRI_QUINTIC_LITE) )
207 #define WARP_IS_CUBIC(wc) ( !WARP_IS_QUINTIC(wc) && !WARP_IS_SINCC(wc) )
208
209 /*--- 'lite' warps are the default now ---*/
210 /*--- only reasons to turn these off is testing or backwards compatibility ---*/
211
212 static int Huse_cubic_lite = 1 ; /* Dec 2018 */
213 static int Huse_quintic_lite = 1 ; /* set these for the LITE warp functions */
214
215 /*----- control verbosity of mri_nwarp functions -----*/
216
217 static int verb_nww = 0 ;
NwarpCalcRPN_verb(int i)218 void NwarpCalcRPN_verb(int i){ verb_nww = i; }
219
220 /*----- other verbosity (mostly for Qwarp, far far below) -----*/
221
222 static int Hverb = 1 ;
223
224 /*---------------------------------------------------------------------------*/
225 /*###### Blocks of code indicated in the Table of Contents below #####*/
226 /*###### are each surrounded by #if 1 ... #endif pairs, to make it #####*/
227 /*###### easy to skip between the beginning and end of code blocks. #####*/
228 /*###### (At least, easy using vi and the % key for match-jumping.) #####*/
229 /*---------------------------------------------------------------------------*/
230
231 #if 1 /*(TOC)*/
232 /*===========================================================================*/
233 /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
234 /*----------------------------------------------------------------------------
235 ------- Table of Contents -- e.g., search forward for the string (C4) --------
236
237 (C1) Prolegomenon, Definitions, and Background
238 (C2) Functions for computing external slopes for a warp [DELETED]
239 (C3) Functions for elementary operations on warps
240 (C4) Functions for extending or truncating a warp grid size
241 (C5) Functions to compute various 'norms' (size estimates) of a warp
242 (C6) Functions that do conversions between index warps and dataset warps
243 (C7) Functions to compute auxiliary functions of an index warp, including
244 stuff for the 3dQwarp penalties, and the 3dNwarpFuncs output
245 (C8) Functions for interpolating all 3 components of an index warp at once
246 (C9) Functions to carry out warp compositions
247 (C10) Functions to invert an index warp in its entirety
248 (C11) Functions to compute the 'square root' of a index warp [USEFUL?]
249 (C12) Functions for taking stuff from 3dAllineate -nwarp output and
250 producing a warp (obsolete, or nearly so -- DO NOT USE!)
251 (C13) Functions for interpolating images
252 (C14) Functions used in 3dNwarpXYZ.c to warp a small number of points given
253 by x,y,z triples, rather than warping a whole grid at once
254 (C15) Functions to warp a dataset, given a warp defined by a dataset
255 (C16) Reverse Polish Notation warp calculator (3dNwarpCalc program) [DELETED]
256 (C17) Functions for reading warps and inverting/catenating them right away
257 (for the '-nwarp' input of various 3dNwarp programs)
258
259 The following functions are only compiled if ALLOW_QWARP is #define-d.
260 Note that these functions deal ONLY with the 'small wiggly' part of
261 an index warp, not the affine part.
262
263 (Q1) Introduction to 3dQwarp, and Global variables for 3dQwarp-ing
264 (includes an outline of the sequence of function calls for 3dQwarp)
265 (Q2) Basis functions (cubic and quintic) for warping
266 (Q3) Functions to create a patch warp from basis functions and parameters
267 (Q4) Evaluate the incrementally warped source image at the combination
268 of the current global warp and the local patch warp
269 (Q5) Functions to evaluate the warp penalty and cost function
270 (Q6) Functions to setup global variables for warp optimization process
271 (Q7) Function that actually optimizes one incremental patch warp
272 (Q8) Functions that drive the warp searching process by looping over
273 patches of shrinking sizes
274 (Q9) All the above functions copied and edited for plusminus warping,
275 which requires doubling up on the calculations in places as
276 two datasets are being warped now
277 -----------------------------------------------------------------------------*/
278 /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
279 /*===========================================================================*/
280
281 #endif /*(TOC)*/ /*##########################################################*/
282
283 #if 1
284 /*===========================================================================*/
285 /*---------- (C1) Prolegomenon -----------------------------------------------
286
287 This file contains the AFNI functions for nonlinear warping, which are
288 wrapped up in various ways in the programs 3dQwarp.c and 3dNwarp*.c;
289 as mentioned earlier, this file is intended to be #include-d into a
290 program, although it is also compiled into the AFNI libmri. The 3dQwarp
291 functions are only compiled if macro ALLOW_QWARP is #define-d before this
292 file is #include-d, since they are only used in the 3dQwarp.c program and
293 they comprise about a lot of this file.
294
295 --------------- Definition of an Index Warp Struct ---------------------------
296
297 The functions herein mostly operate on IndexWarp3D structs. Such a struct
298 represents an "index warp in 3D" -- that is, it maps indexes and not
299 coordinates. Most functions that operate on these things start with
300 the string 'IW3D_'. The basic struct is pretty simple (from mri_nwarp.h):
301
302 typedef struct {
303 int nx , ny , nz ; -- Grid dimensions
304 float *xd , *yd , *zd ; -- Displacments (in index space)
305 float *hv , *je , *se ; -- Various auxiliary volumes
306 int use_amat ; -- Whether to use amat
307 mat44 amat , amati ; -- Affine component of warp (and its inverse)
308 -- stuff below here is for conversion to/from 3D dataset format --
309 mat44 cmat , imat ; -- cmat: i->x ; imat: x->i
310 char *geomstring ; -- grid geometry of 3D dataset
311 int view ; -- view/space of the dataset
312 } IndexWarp3D ;
313
314 nx, ny, nz = dimensions of the grid arrays (nz==1 is allowed)
315 xd, yd, zd = index displacements = arrays of size nx*ny*nz;
316 the voxel at 3D index (i,j,k) is mapped to
317 i + xd[i+j*nx+k*nx*ny]
318 j + yd[i+j*nx+k*nx*ny]
319 k + zd[i+j*nx+k*nx*ny]
320 hv = volume of the warped cube (hexahedron)
321 [i,i+1] X [j,j+1] X [k,k+1]
322 je = bulk volume distortion energy (part of 3dQwarp penalty)
323 se = shear and vorticity energy (other part of penalty);
324 --> hv, je, se are usually NULL, and are what gets calculated
325 in 3dNwarpFuncs via function IW3D_load_bsv()
326 amat = matrix-vector [M c] to modify displacements:
327 [ i + xd(i,j,k) ]
328 [M] [ j + yd(i,j,k) ] + [c]
329 [ k + zd(i,j,k) ]
330 when (i,j,k) is outside the (0..nx-1,0..ny-1,0..nz-1) grid,
331 (xd,yd,zd)=(0,0,0) and amat still supplies the warp.
332 amati = inverse of amat
333
334 The components below are for relating the IndexWarp3D
335 struct to a 'real' space 3D dataset:
336
337 cmat = matrix to get DICOM x,y,z coordinates from i,j,k
338 imat = inverse of cmat (convert coordinate to index)
339 geomstring = string to represent geometry of 3D dataset that
340 corresponds to this index warp; if the index warp
341 was created directly from a dataset via function
342 IW3D_from_dataset(), then that's when geomstring is
343 set, otherwise it will be set using function
344 IW3D_adopt_dataset() -- along with the other
345 dataset-ish stuff (cmat, imat, view).
346 view = view code for that dataset (should be upgraded to space)
347
348 There is also a struct IndexWarp3D_pair, which holds 2 warps, one the
349 forward warp and one the inverse warp via function IW3D_invert(). This
350 struct doesn't get used much, as it turns out. At one time, I thought
351 I would always keep the warp inverse around, at least for input and
352 output to datasets (cf. infra), but that didn't happen.
353
354 --------------- Warps stored as 3D Datasets -----------------------------------
355
356 Externally, warps are stored as 3D datasets, with (at least) 3 sub-bricks:
357 one for each of the x, y, and z displacements. A warp dataset is created
358 from an IndexWarp3D struct using function IW3D_to_dataset(), with the
359 geomstring component providing the key information about how to lay out
360 the 3D dataset grid. The xd, yd, zd index displacements are converted
361 to DICOM x,y,z displacments in mm using the cmat component. Note, however,
362 that the dataset itself may not be stored in DICOM RAI order, but that the
363 displacments always refer to DICOM ordered vectors. This can be a little
364 confusing, so beware.
365
366 Incidentally, storing a warp as a set of displacements has a number of
367 advantages over storing the actual displaced locations (i.e., indexes or
368 coordinates). For one, the identity warp is just a big collection of
369 zeros. For another, interpolation to a different grid is simpler.
370
371 -------------- Understanding Warps and Displacements --------------------------
372
373 Since warps are stored as displacements, the actual transformation of
374 (index) coordinates is
375
376 i -> i + xd[i,j,k] (loosely speaking)
377 j -> j + yd[i,j,k]
378 k -> k + zd[i,j,k]
379
380 so you will see various places where the addition of the original
381 coordinates is needed; for example, when composing 2 warps in function
382 IW3D_compose(), we have to compute B(A(x)), where the warps are defined
383 by A(x) = x + a(x) and B(x) = x + b(x), so B(A(x)) = x+a(x) + b(x+a(x))
384 -- and so b() must be interpolated at these non-integer index values --
385 which explains why the output warp is computed as a(x)+b(x+a(x)) -- the
386 double appearance of a(x) is required by this storage mechanism.
387
388 In a more complicated case, in inverting an index warp, the following
389 composition is needed in function IW3D_invert_newt(): B(2x-A(B(x))).
390 This computation is carried out by the following sequence of steps
391 A(x) = x + a(x) [by definition]
392 B(x) = x + b(x) [by definition]
393 A(B(x)) = x + b(x) + a(x+b(x))
394 2x - A(B(x)) = x - b(x) - a(x+b(x))
395 B( 2x - A(B(x)) ) = x - b(x) - a(x+b(x)) + b(x-b(x)-a(x+b(x)))
396 You should understand how this expansion works in order to be able to
397 understand any of the warp manipulation functions.
398
399 --------------- Forward and Inverse Warps -------------------------------------
400
401 A nonlinear warp stores the displacements (in indexes or DICOM mm) from a
402 base dataset grid to a source dataset grid. For computing the source dataset
403 warped to the base dataset grid, these displacements are needed, so that for
404 each grid point in the output (warped) dataset, the corresponding location in
405 the source dataset can be found. That is, this 'forward' warp is good for
406 finding where a given point in the base dataset maps to in the source dataset.
407 However, for finding where a given point in the source dataset maps to in the
408 base dataset, the 'inverse' warp is needed.
409
410 Another way of thinking about warps is to imagine the forward warp reaching
411 out and pulling the source back to the same location as the base dataset.
412 Don't think of it as pushing the source to the base! That is what the
413 inverse warp does, but since datasets live on a regular 3D grid, pushing
414 a source grid point to the base dataset will almost surely not end up on
415 a base dataset grid point, and so for most purposes 'pushing' isn't
416 desired.
417
418 --------------- OpenMP code ---------------------------------------------------
419
420 A large number of compute intensive functions use OpenMP to parallelize
421 the lengthy computations. This makes the source code somewhat more
422 complicated and less transparent than it could otherwise be. If you
423 need some tutorial on OpenMP, start with
424 https://afni.nimh.nih.gov/pub/dist/doc/misc/OpenMP.html
425
426 -----------------------------------------------------------------------------*/
427 /*===========================================================================*/
428
429 #endif /*(C1)*/ /*###########################################################*/
430
431 #if 0 /* THESE FUNCTIONS ARE DELETED [26 Apr 2021] - RWCox */
432 /*===========================================================================*/
433 /* (C2) Functions below are for computing the external slopes for a warp. */
434 /*===========================================================================*/
435
436 /*---------------------------------------------------------------------------*/
437 /* The following macros are for loading and unloading the extra slope
438 variables for extending a warp outside its domain of definition.
439 *//*-------------------------------------------------------------------------*/
440
441 /* declare 18 local variables to hold the external slopes */
442
443 #define ES_DECLARE_FLOATS \
444 float es_xd_xp=0.0f, es_xd_xm=0.0f, es_xd_yp=0.0f, es_xd_ym=0.0f, es_xd_zp=0.0f, es_xd_zm=0.0f, \
445 es_yd_xp=0.0f, es_yd_xm=0.0f, es_yd_yp=0.0f, es_yd_ym=0.0f, es_yd_zp=0.0f, es_yd_zm=0.0f, \
446 es_zd_xp=0.0f, es_zd_xm=0.0f, es_zd_yp=0.0f, es_zd_ym=0.0f, es_zd_zp=0.0f, es_zd_zm=0.0f
447
448 /* pack the 18 external slopes in a warp struct into an array */
449
450 #define ES_PACK(AA,eqq) \
451 do{ int qw=0 ; \
452 eqq[qw++] = AA->es_xd_xp; eqq[qw++] = AA->es_xd_xm; eqq[qw++] = AA->es_xd_yp; \
453 eqq[qw++] = AA->es_xd_ym; eqq[qw++] = AA->es_xd_zp; eqq[qw++] = AA->es_xd_zm; \
454 eqq[qw++] = AA->es_yd_xp; eqq[qw++] = AA->es_yd_xm; eqq[qw++] = AA->es_yd_yp; \
455 eqq[qw++] = AA->es_yd_ym; eqq[qw++] = AA->es_yd_zp; eqq[qw++] = AA->es_yd_zm; \
456 eqq[qw++] = AA->es_zd_xp; eqq[qw++] = AA->es_zd_xm; eqq[qw++] = AA->es_zd_yp; \
457 eqq[qw++] = AA->es_zd_ym; eqq[qw++] = AA->es_zd_zp; eqq[qw++] = AA->es_zd_zm; \
458 } while(0)
459
460 /* pack the 18 external slopes in local variables into an array */
461
462 #define ES_PACKVEC(eqq) \
463 do{ int qw=0 ; \
464 eqq[qw++] = es_xd_xp; eqq[qw++] = es_xd_xm; eqq[qw++] = es_xd_yp; \
465 eqq[qw++] = es_xd_ym; eqq[qw++] = es_xd_zp; eqq[qw++] = es_xd_zm; \
466 eqq[qw++] = es_yd_xp; eqq[qw++] = es_yd_xm; eqq[qw++] = es_yd_yp; \
467 eqq[qw++] = es_yd_ym; eqq[qw++] = es_yd_zp; eqq[qw++] = es_yd_zm; \
468 eqq[qw++] = es_zd_xp; eqq[qw++] = es_zd_xm; eqq[qw++] = es_zd_yp; \
469 eqq[qw++] = es_zd_ym; eqq[qw++] = es_zd_zp; eqq[qw++] = es_zd_zm; \
470 } while(0)
471
472 /* unpack the 18 local external slope variables from an array */
473
474 #define ES_UNPACKVEC(eqq) \
475 do{ int qw=0 ; \
476 es_xd_xp = eqq[qw++]; es_xd_xm = eqq[qw++]; es_xd_yp = eqq[qw++]; \
477 es_xd_ym = eqq[qw++]; es_xd_zp = eqq[qw++]; es_xd_zm = eqq[qw++]; \
478 es_yd_xp = eqq[qw++]; es_yd_xm = eqq[qw++]; es_yd_yp = eqq[qw++]; \
479 es_yd_ym = eqq[qw++]; es_yd_zp = eqq[qw++]; es_yd_zm = eqq[qw++]; \
480 es_zd_xp = eqq[qw++]; es_zd_xm = eqq[qw++]; es_zd_yp = eqq[qw++]; \
481 es_zd_ym = eqq[qw++]; es_zd_zp = eqq[qw++]; es_zd_zm = eqq[qw++]; \
482 } while(0)
483
484 /* unpack the 18 local external slope variables from a warp struct */
485
486 #define ES_WARP_TO_LOCAL(AA) \
487 do{ es_xd_xp=AA->es_xd_xp; es_xd_xm=AA->es_xd_xm; \
488 es_yd_xp=AA->es_yd_xp; es_yd_xm=AA->es_yd_xm; \
489 es_zd_xp=AA->es_zd_xp; es_zd_xm=AA->es_zd_xm; \
490 es_xd_yp=AA->es_xd_yp; es_xd_ym=AA->es_xd_ym; \
491 es_yd_yp=AA->es_yd_yp; es_yd_ym=AA->es_yd_ym; \
492 es_zd_yp=AA->es_zd_yp; es_zd_ym=AA->es_zd_ym; \
493 es_xd_zp=AA->es_xd_zp; es_xd_zm=AA->es_xd_zm; \
494 es_yd_zp=AA->es_yd_zp; es_yd_zm=AA->es_yd_zm; \
495 es_zd_zp=AA->es_zd_zp; es_zd_zm=AA->es_zd_zm; } while(0)
496
497 /*---------------------------------------------------------------------------*/
498 /* The following functions are for providing a linear extension to a warp,
499 based on the final 3..5 layers in the warp.
500 These macros compute the slope based on 3, 4, or 5 points.
501 *//*-------------------------------------------------------------------------*/
502
503 #define SLOPE_3(x0,x1,x2) ( -0.5f*(x0+x1+x2) \
504 + 0.5f*(x1+2.0f*x2) )
505
506 #define SLOPE_4(x0,x1,x2,x3) ( -0.3f*(x0+x1+x2+x3) \
507 +0.2f*(x1+2.0f*x2+3.0f*x3) )
508
509 #define SLOPE_5(x0,x1,x2,x3,x4) ( -0.2f*(x0+x1+x2+x3+x4) \
510 +0.1f*(x1+2.0f*x2+3.0f*x3+4.0f*x4) )
511
512 /*---------------------------------------------------------------------------*/
513 /* This function returns the MEAN slope from a collection of vectors;
514 that is, the slope is calculated using 3, 4, or 5 points
515 (one from each vector), and then these are averaged to give the output.
516 In this library, these vectors are formed from the outer layers of the
517 faces of the warp in function IW3D_load_external_slopes().
518 *//*-------------------------------------------------------------------------*/
519
520 float mean_slope( int nvec , int veclen , float **vec )
521 {
522 float ssum=0.0f ; int iv ;
523
524 if( nvec < 3 || veclen < 1 || vec == NULL ) return ssum ;
525
526 switch( nvec ){
527 case 3:
528 for( iv=0 ; iv < veclen ; iv++ )
529 ssum += SLOPE_3(vec[0][iv],vec[1][iv],vec[2][iv]) ;
530 break ;
531
532 case 4:
533 for( iv=0 ; iv < veclen ; iv++ )
534 ssum += SLOPE_4(vec[0][iv],vec[1][iv],vec[2][iv],vec[3][iv]) ;
535 break ;
536
537 default:
538 case 5:
539 for( iv=0 ; iv < veclen ; iv++ )
540 ssum += SLOPE_5(vec[0][iv],vec[1][iv],vec[2][iv],vec[3][iv],vec[4][iv]) ;
541 break ;
542 }
543
544 return (ssum/veclen) ;
545 }
546
547 /*---------------------------------------------------------------------------*/
548 /* Initialize the external slopes in a warp, to zero (of course). */
549
550 void IW3D_zero_external_slopes( IndexWarp3D *AA )
551 {
552 if( AA == NULL ) return ;
553 AA->use_es = 0 ;
554 AA->es_xd_xp = AA->es_xd_xm = AA->es_xd_yp = AA->es_xd_ym = AA->es_xd_zp = AA->es_xd_zm
555 = AA->es_yd_xp = AA->es_yd_xm = AA->es_yd_yp = AA->es_yd_ym = AA->es_yd_zp = AA->es_yd_zm
556 = AA->es_zd_xp = AA->es_zd_xm = AA->es_zd_yp = AA->es_zd_ym = AA->es_zd_zp = AA->es_zd_zm = 0.0f ;
557 return ;
558 }
559
560 /*----------------------------------------------------------------------------*/
561 /* Compute the external slopes in a warp, using the mean_slope() function
562 above applied to each face, extracting the vectors from the face's layers.
563 Code for deciphering the slope names, where 'Q' is 'x' or 'y' or 'z'
564 es = external slope
565 Qd = Q direction displacment
566 Qm = along the Q face in the minus direction
567 Qp = along the Q face in the plus direction
568 Note that the slopes are always in the positive direction of the coordinate
569 being changes, even at the minus faces. For example, es_xd_xm > 0 means
570 that for the x-index being negative (outside the 'xm' face), then the
571 x-displacement ('xd') will get a negative addition of es_xd_xm * x-index
572 (multiplying the positive slope times the negative index).
573
574 This code is pretty simple, just repetitive (6 faces times 3 displacments).
575 *//*--------------------------------------------------------------------------*/
576
577 void IW3D_load_external_slopes( IndexWarp3D *AA )
578 {
579 int nx,ny,nz,nxy , veclen,nvec , pp,qq,rr,ss , nbig ;
580 float **vec , *dar ;
581
582 ENTRY("IW3D_load_external_slopes") ;
583
584 if( AA == NULL ) EXRETURN ;
585
586 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; /* dimensions */
587
588 /* make space for the vectors to extract */
589
590 nbig = MAX(nx,ny) ; nbig = MAX(nbig,nz) ; nbig = nbig*nbig ;
591 vec = (float **)malloc(sizeof(float *)*5) ;
592 for( pp=0 ; pp < 5 ; pp++ ) vec[pp] = (float *)malloc(sizeof(float)*nbig) ;
593
594 #define DD(i,j,k) dar[(i)+(j)*nx+(k)*nxy] /* access dar[i,j,k] */
595
596 /* x=0 and x=nx-1 faces */
597
598 /* how many surface slabs to use (5 or 4 or 3) */
599
600 nvec = nx/3 ; if( nvec > 5 ) nvec = 5 ; else if( nvec < 3 ) nvec = 0 ;
601 veclen = ny*nz ; /* size of the x faces */
602
603 dar = AA->xd ; /* extract x displacements at x=0 face */
604 for( pp=0 ; pp < nvec ; pp++ ){
605 for( ss=qq=0 ; qq < ny ; qq++ ){
606 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(pp,qq,rr) ;
607 }}
608 AA->es_xd_xm = mean_slope( nvec , veclen , vec ) ; /* x minus face */
609
610 for( pp=0 ; pp < nvec ; pp++ ){ /* at x=nx-1 face */
611 for( ss=qq=0 ; qq < ny ; qq++ ){
612 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(nx-1-pp,qq,rr) ;
613 }}
614 /* below, we have to negate the mean_slope() output since
615 we loaded the faces in the order of decreasing x-index */
616 AA->es_xd_xp = - mean_slope( nvec , veclen , vec ) ; /* x plus face */
617
618 /** below here, edited copies for all combinations of
619 displacement vectors (xd,yd,zd) and face (xm,xp,ym,yp,zm,zp) **/
620
621 dar = AA->yd ; /* y displacements */
622 for( pp=0 ; pp < nvec ; pp++ ){
623 for( ss=qq=0 ; qq < ny ; qq++ ){
624 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(pp,qq,rr) ;
625 }}
626 AA->es_yd_xm = mean_slope( nvec , veclen , vec ) ; /* x minus face */
627
628 for( pp=0 ; pp < nvec ; pp++ ){
629 for( ss=qq=0 ; qq < ny ; qq++ ){
630 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(nx-1-pp,qq,rr) ;
631 }}
632 AA->es_yd_xp = - mean_slope( nvec , veclen , vec ) ; /* x plus face */
633
634 dar = AA->zd ; /* z displacments */
635 for( pp=0 ; pp < nvec ; pp++ ){
636 for( ss=qq=0 ; qq < ny ; qq++ ){
637 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(pp,qq,rr) ;
638 }}
639 AA->es_zd_xm = mean_slope( nvec , veclen , vec ) ; /* x minus face */
640
641 for( pp=0 ; pp < nvec ; pp++ ){
642 for( ss=qq=0 ; qq < ny ; qq++ ){
643 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(nx-1-pp,qq,rr) ;
644 }}
645 AA->es_zd_xp = - mean_slope( nvec , veclen , vec ) ; /* x plus face */
646
647 /* repeat the above for the y=0 and y=ny-1 faces */
648
649 nvec = ny/3 ; if( nvec > 5 ) nvec = 5 ; else if( nvec < 3 ) nvec = 0 ;
650 veclen = nz*nx ;
651
652 dar = AA->xd ;
653 for( pp=0 ; pp < nvec ; pp++ ){
654 for( ss=qq=0 ; qq < nz ; qq++ ){
655 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,pp,qq) ;
656 }}
657 AA->es_xd_ym = mean_slope( nvec , veclen , vec ) ;
658
659 for( pp=0 ; pp < nvec ; pp++ ){
660 for( ss=qq=0 ; qq < nz ; qq++ ){
661 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,ny-1-pp,qq) ;
662 }}
663 AA->es_xd_yp = - mean_slope( nvec , veclen , vec ) ;
664
665 dar = AA->yd ;
666 for( pp=0 ; pp < nvec ; pp++ ){
667 for( ss=qq=0 ; qq < nz ; qq++ ){
668 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,pp,qq) ;
669 }}
670 AA->es_yd_ym = mean_slope( nvec , veclen , vec ) ;
671
672 for( pp=0 ; pp < nvec ; pp++ ){
673 for( ss=qq=0 ; qq < nz ; qq++ ){
674 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,ny-1-pp,qq) ;
675 }}
676 AA->es_yd_yp = - mean_slope( nvec , veclen , vec ) ;
677
678 dar = AA->zd ;
679 for( pp=0 ; pp < nvec ; pp++ ){
680 for( ss=qq=0 ; qq < nz ; qq++ ){
681 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,pp,qq) ;
682 }}
683 AA->es_zd_ym = mean_slope( nvec , veclen , vec ) ;
684
685 for( pp=0 ; pp < nvec ; pp++ ){
686 for( ss=qq=0 ; qq < nz ; qq++ ){
687 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,ny-1-pp,qq) ;
688 }}
689 AA->es_zd_yp = - mean_slope( nvec , veclen , vec ) ;
690
691 /* and for the z=0 and z=nz-1 faces */
692
693 nvec = nz/3 ; if( nvec > 5 ) nvec = 5 ; else if( nvec < 3 ) nvec = 0 ;
694 veclen = nx*ny ;
695
696 dar = AA->xd ;
697 for( pp=0 ; pp < nvec ; pp++ ){
698 for( ss=qq=0 ; qq < nx ; qq++ ){
699 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,pp) ;
700 }}
701 AA->es_xd_zm = mean_slope( nvec , veclen , vec ) ;
702
703 for( pp=0 ; pp < nvec ; pp++ ){
704 for( ss=qq=0 ; qq < nx ; qq++ ){
705 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,nz-1-pp) ;
706 }}
707 AA->es_xd_zp = - mean_slope( nvec , veclen , vec ) ;
708
709 dar = AA->yd ;
710 for( pp=0 ; pp < nvec ; pp++ ){
711 for( ss=qq=0 ; qq < nx ; qq++ ){
712 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,pp) ;
713 }}
714 AA->es_yd_zm = mean_slope( nvec , veclen , vec ) ;
715
716 for( pp=0 ; pp < nvec ; pp++ ){
717 for( ss=qq=0 ; qq < nx ; qq++ ){
718 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,nz-1-pp) ;
719 }}
720 AA->es_yd_zp = - mean_slope( nvec , veclen , vec ) ;
721
722 dar = AA->zd ;
723 for( pp=0 ; pp < nvec ; pp++ ){
724 for( ss=qq=0 ; qq < nx ; qq++ ){
725 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,pp) ;
726 }}
727 AA->es_zd_zm = mean_slope( nvec , veclen , vec ) ;
728
729 for( pp=0 ; pp < nvec ; pp++ ){
730 for( ss=qq=0 ; qq < nx ; qq++ ){
731 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,nz-1-pp) ;
732 }}
733 AA->es_zd_zp = - mean_slope( nvec , veclen , vec ) ;
734
735 #undef DD
736
737 /* trash the workspace vectors */
738
739 for( pp=0 ; pp < 5 ; pp++ ) free(vec[pp]) ;
740 free(vec) ;
741
742 AA->use_es = 1 ;
743 EXRETURN ;
744 }
745
746 /*---------------------------------------------------------------------------*/
747 /* Very similar to above, but for warps stored in a dataset; here, the
748 external slopes (in mm per voxel) are stored in the output vector.
749 This is used in the 3dNwarpXYZ.c functions defined below in (C14),
750 but not elsewhere.
751 *//*-------------------------------------------------------------------------*/
752
753 floatvec * THD_nwarp_external_slopes( THD_3dim_dataset *dset_nwarp )
754 {
755 int nx,ny,nz,nxy , veclen,nvec , pp,qq,rr,ss , nbig ;
756 float **vec , *dar ;
757 floatvec *esv=NULL ;
758 ES_DECLARE_FLOATS ;
759
760 ENTRY("THD_nwarp_external_slopes") ;
761
762 if( dset_nwarp == NULL || DSET_NVALS(dset_nwarp) < 3 ) RETURN(NULL) ;
763 if( DSET_BRICK_TYPE(dset_nwarp,0) != MRI_float ) RETURN(NULL) ;
764 DSET_load(dset_nwarp) ; if( !DSET_LOADED(dset_nwarp) ) RETURN(NULL) ;
765
766 nx = DSET_NX(dset_nwarp); ny = DSET_NY(dset_nwarp); nz = DSET_NZ(dset_nwarp); nxy = nx*ny;
767
768 /* make space for the vectors to extract */
769
770 nbig = MAX(nx,ny) ; nbig = MAX(nbig,nz) ; nbig = nbig*nbig ;
771 vec = (float **)malloc(sizeof(float *)*5) ;
772 for( pp=0 ; pp < 5 ; pp++ ) vec[pp] = (float *)malloc(sizeof(float)*nbig) ;
773
774 #define DD(i,j,k) dar[(i)+(j)*nx+(k)*nxy]
775
776 /* x=0 and x=nx-1 faces */
777
778 nvec = nx/3 ; if( nvec > 5 ) nvec = 5 ; else if( nvec < 3 ) nvec = 0 ;
779 veclen = ny*nz ;
780
781 dar = (float *)DSET_ARRAY(dset_nwarp,0) ; /* x displacements */
782 for( pp=0 ; pp < nvec ; pp++ ){
783 for( ss=qq=0 ; qq < ny ; qq++ ){
784 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(pp,qq,rr) ;
785 }}
786 es_xd_xm = mean_slope( nvec , veclen , vec ) ; /* x minus face */
787
788 for( pp=0 ; pp < nvec ; pp++ ){
789 for( ss=qq=0 ; qq < ny ; qq++ ){
790 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(nx-1-pp,qq,rr) ;
791 }}
792 es_xd_xp = - mean_slope( nvec , veclen , vec ) ; /* x plus face */
793
794 dar = (float *)DSET_ARRAY(dset_nwarp,1) ; /* y displacements */
795 for( pp=0 ; pp < nvec ; pp++ ){
796 for( ss=qq=0 ; qq < ny ; qq++ ){
797 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(pp,qq,rr) ;
798 }}
799 es_yd_xm = mean_slope( nvec , veclen , vec ) ; /* x minus face */
800
801 for( pp=0 ; pp < nvec ; pp++ ){
802 for( ss=qq=0 ; qq < ny ; qq++ ){
803 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(nx-1-pp,qq,rr) ;
804 }}
805 es_yd_xp = - mean_slope( nvec , veclen , vec ) ; /* x plus face */
806
807 dar = (float *)DSET_ARRAY(dset_nwarp,2) ; /* z displacements */
808 for( pp=0 ; pp < nvec ; pp++ ){
809 for( ss=qq=0 ; qq < ny ; qq++ ){
810 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(pp,qq,rr) ;
811 }}
812 es_zd_xm = mean_slope( nvec , veclen , vec ) ; /* x minus face */
813
814 for( pp=0 ; pp < nvec ; pp++ ){
815 for( ss=qq=0 ; qq < ny ; qq++ ){
816 for( rr=0 ; rr < nz ; rr++ ) vec[pp][ss++] = DD(nx-1-pp,qq,rr) ;
817 }}
818 es_zd_xp = - mean_slope( nvec , veclen , vec ) ; /* x plus face */
819
820 /* repeat the above for the y=0 and y=ny-1 faces */
821
822 nvec = ny/3 ; if( nvec > 5 ) nvec = 5 ; else if( nvec < 3 ) nvec = 0 ;
823 veclen = nz*nx ;
824
825 dar = (float *)DSET_ARRAY(dset_nwarp,0) ;
826 for( pp=0 ; pp < nvec ; pp++ ){
827 for( ss=qq=0 ; qq < nz ; qq++ ){
828 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,pp,qq) ;
829 }}
830 es_xd_ym = mean_slope( nvec , veclen , vec ) ;
831
832 for( pp=0 ; pp < nvec ; pp++ ){
833 for( ss=qq=0 ; qq < nz ; qq++ ){
834 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,ny-1-pp,qq) ;
835 }}
836 es_xd_yp = - mean_slope( nvec , veclen , vec ) ;
837
838 dar = (float *)DSET_ARRAY(dset_nwarp,1) ;
839 for( pp=0 ; pp < nvec ; pp++ ){
840 for( ss=qq=0 ; qq < nz ; qq++ ){
841 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,pp,qq) ;
842 }}
843 es_yd_ym = mean_slope( nvec , veclen , vec ) ;
844
845 for( pp=0 ; pp < nvec ; pp++ ){
846 for( ss=qq=0 ; qq < nz ; qq++ ){
847 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,ny-1-pp,qq) ;
848 }}
849 es_yd_yp = - mean_slope( nvec , veclen , vec ) ;
850
851 dar = (float *)DSET_ARRAY(dset_nwarp,2) ;
852 for( pp=0 ; pp < nvec ; pp++ ){
853 for( ss=qq=0 ; qq < nz ; qq++ ){
854 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,pp,qq) ;
855 }}
856 es_zd_ym = mean_slope( nvec , veclen , vec ) ;
857
858 for( pp=0 ; pp < nvec ; pp++ ){
859 for( ss=qq=0 ; qq < nz ; qq++ ){
860 for( rr=0 ; rr < nx ; rr++ ) vec[pp][ss++] = DD(rr,ny-1-pp,qq) ;
861 }}
862 es_zd_yp = - mean_slope( nvec , veclen , vec ) ;
863
864 /* and for the z=0 and z=nz-1 faces */
865
866 nvec = nz/3 ; if( nvec > 5 ) nvec = 5 ; else if( nvec < 3 ) nvec = 0 ;
867 veclen = nx*ny ;
868
869 dar = (float *)DSET_ARRAY(dset_nwarp,0) ;
870 for( pp=0 ; pp < nvec ; pp++ ){
871 for( ss=qq=0 ; qq < nx ; qq++ ){
872 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,pp) ;
873 }}
874 es_xd_zm = mean_slope( nvec , veclen , vec ) ;
875
876 for( pp=0 ; pp < nvec ; pp++ ){
877 for( ss=qq=0 ; qq < nx ; qq++ ){
878 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,nz-1-pp) ;
879 }}
880 es_xd_zp = - mean_slope( nvec , veclen , vec ) ;
881
882 dar = (float *)DSET_ARRAY(dset_nwarp,1) ;
883 for( pp=0 ; pp < nvec ; pp++ ){
884 for( ss=qq=0 ; qq < nx ; qq++ ){
885 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,pp) ;
886 }}
887 es_yd_zm = mean_slope( nvec , veclen , vec ) ;
888
889 for( pp=0 ; pp < nvec ; pp++ ){
890 for( ss=qq=0 ; qq < nx ; qq++ ){
891 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,nz-1-pp) ;
892 }}
893 es_yd_zp = - mean_slope( nvec , veclen , vec ) ;
894
895 dar = (float *)DSET_ARRAY(dset_nwarp,2) ;
896 for( pp=0 ; pp < nvec ; pp++ ){
897 for( ss=qq=0 ; qq < nx ; qq++ ){
898 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,pp) ;
899 }}
900 es_zd_zm = mean_slope( nvec , veclen , vec ) ;
901
902 for( pp=0 ; pp < nvec ; pp++ ){
903 for( ss=qq=0 ; qq < nx ; qq++ ){
904 for( rr=0 ; rr < ny ; rr++ ) vec[pp][ss++] = DD(qq,rr,nz-1-pp) ;
905 }}
906 es_zd_zp = - mean_slope( nvec , veclen , vec ) ;
907
908 #undef DD
909
910 for( pp=0 ; pp < 5 ; pp++ ) free(vec[pp]) ;
911 free(vec) ;
912
913 MAKE_floatvec(esv,18) ; ES_PACKVEC(esv->ar) ;
914 RETURN(esv) ;
915 }
916
917 /* End of external slope calculation stuff */
918 /*===========================================================================*/
919
920 #else
921
922 #define THD_nwarp_external_slopes(xxx) NULL
923 #define ES_PACK(AA,eqq) /*nada*/
924 #define ES_PACKVEC(eqq) /*nada*/
925 #define ES_UNPACKVEC(eqq) /*nada*/
926 #define ES_WARP_TO_LOCAL(AA) /*nada*/
927
928 #define ES_DECLARE_FLOATS \
929 float es_xd_xp=0.0f, es_xd_xm=0.0f, es_xd_yp=0.0f, es_xd_ym=0.0f, es_xd_zp=0.0f, es_xd_zm=0.0f, \
930 es_yd_xp=0.0f, es_yd_xm=0.0f, es_yd_yp=0.0f, es_yd_ym=0.0f, es_yd_zp=0.0f, es_yd_zm=0.0f, \
931 es_zd_xp=0.0f, es_zd_xm=0.0f, es_zd_yp=0.0f, es_zd_ym=0.0f, es_zd_zp=0.0f, es_zd_zm=0.0f
932
933 #endif /*(C2)*/ /*###########################################################*/
934
935 #if 1
936 /*===========================================================================*/
937 /* (C3) Functions below are for elementary operations on warps;
938 creation, destruction, copying, scaling, etc. Plus warp pair stuff.
939 These functions operate on the displacements (xd,yd,zd) = the
940 "wiggly" part of the warp, mostly not on the affine part (amat). */
941 /*===========================================================================*/
942
943 /* access 3D array far[i,j,k] where ni, nij are pre-computed access factors */
944
945 #undef FSUB
946 #define FSUB(far,i,j,k,ni,nij) far[(i)+(j)*(ni)+(k)*(nij)]
947
948 /*---------------------------------------------------------------------------*/
949 /* Creation of a warp, ex nihilo! */
950
IW3D_create(int nx,int ny,int nz)951 IndexWarp3D * IW3D_create( int nx , int ny , int nz )
952 {
953 IndexWarp3D *AA ;
954
955 ENTRY("IW3D_create") ;
956
957 if( nx < NGMIN && ny < NGMIN && nz < NGMIN ) RETURN(NULL) ;
958
959 AA = (IndexWarp3D *)calloc(1,sizeof(IndexWarp3D)) ;
960
961 AA->nx = nx ; AA->ny = ny ; AA->nz = nz ;
962
963 AA->xd = (float *)calloc(nx*ny*nz,sizeof(float)) ; /* zero filled */
964 AA->yd = (float *)calloc(nx*ny*nz,sizeof(float)) ; /* displacements */
965 AA->zd = (float *)calloc(nx*ny*nz,sizeof(float)) ;
966
967 AA->hv = NULL ; /* to be filled in later, maybe */
968 AA->je = NULL ; /* to be filled in later, maybe */
969 AA->se = NULL ; /* to be filled in later, maybe */
970
971 LOAD_IDENT_MAT44(AA->cmat) ; /* dataset geometry currently unknown */
972 LOAD_IDENT_MAT44(AA->imat) ;
973 LOAD_IDENT_MAT44(AA->amat) ; LOAD_IDENT_MAT44(AA->amati) ; AA->use_amat = 0 ;
974 AA->geomstring = NULL ;
975 AA->view = VIEW_ORIGINAL_TYPE ;
976
977 RETURN(AA) ;
978 }
979
980 /*---------------------------------------------------------------------------*/
981 /* Into the valley of death rode the warp!
982 mallocs to the left of them, callocs to the right!
983 *//*-------------------------------------------------------------------------*/
984
IW3D_destroy(IndexWarp3D * AA)985 void IW3D_destroy( IndexWarp3D *AA )
986 {
987 ENTRY("IW3D_destroy") ;
988 if( AA != NULL ){
989 FREEIFNN(AA->xd); FREEIFNN(AA->yd); FREEIFNN(AA->zd);
990 FREEIFNN(AA->hv); FREEIFNN(AA->je); FREEIFNN(AA->se);
991 FREEIFNN(AA->geomstring) ;
992 free(AA);
993 }
994 EXRETURN ;
995 }
996
997 /*---------------------------------------------------------------------------*/
998 /* Fill a warp with zero displacments */
999
IW3D_zero_fill(IndexWarp3D * AA)1000 void IW3D_zero_fill( IndexWarp3D *AA )
1001 {
1002 size_t nbyt ;
1003
1004 ENTRY("IW3D_zero_fill") ;
1005
1006 if( AA == NULL ) EXRETURN ;
1007 nbyt = sizeof(float) * AA->nx * AA->ny * AA->nz ;
1008 if( AA->xd != NULL ) AAmemset( AA->xd , 0 , nbyt ) ;
1009 if( AA->yd != NULL ) AAmemset( AA->yd , 0 , nbyt ) ;
1010 if( AA->zd != NULL ) AAmemset( AA->zd , 0 , nbyt ) ;
1011 if( AA->hv != NULL ) AAmemset( AA->hv , 0 , nbyt ) ;
1012 if( AA->je != NULL ) AAmemset( AA->je , 0 , nbyt ) ;
1013 if( AA->se != NULL ) AAmemset( AA->se , 0 , nbyt ) ;
1014 EXRETURN ;
1015 }
1016
1017 /*----------------------------------------------------------------------------*/
1018 /* return 1 if warp is filled with all zero displacements, 0 otherwise */
1019
IW3D_is_zero(IndexWarp3D * AA)1020 int IW3D_is_zero( IndexWarp3D *AA )
1021 {
1022 int ii , nvox ; float *xd, *yd, *zd ;
1023 if( AA == NULL ) return 0 ;
1024 xd = AA->xd ; if( xd == NULL ) return 0 ;
1025 yd = AA->yd ; if( yd == NULL ) return 0 ;
1026 zd = AA->zd ; if( zd == NULL ) return 0 ;
1027 nvox = AA->nx * AA->ny * AA->nz ;
1028 for( ii=0 ; ii < nvox ; ii++ )
1029 if( xd[ii] != 0.0f || yd[ii] != 0.0f || zd[ii] != 0.0f ) return 0 ;
1030 return 1 ;
1031 }
1032
1033 /*----------------------------------------------------------------------------*/
1034 /* Make the geometry fields of an index warp match that of a dataset. */
1035
IW3D_adopt_dataset(IndexWarp3D * AA,THD_3dim_dataset * dset)1036 void IW3D_adopt_dataset( IndexWarp3D *AA , THD_3dim_dataset *dset )
1037 {
1038 mat44 cmat , imat ; char *gstr ;
1039
1040 ENTRY("IW3D_adopt_dataset") ;
1041
1042 if( AA == NULL || !ISVALID_DSET(dset) ) EXRETURN ;
1043
1044 /* check for grid mismatch error */
1045
1046 if( DSET_NX(dset) != AA->nx || DSET_NY(dset) != AA->ny || DSET_NZ(dset) != AA->nz ){
1047 ERROR_message("IW3D_adopt_dataset: grid mismatch\n"
1048 " AA(%d,%d,%d) doesn't match dataset %s(%d,%d,%d)" ,
1049 AA->nx , AA->ny , AA->nz ,
1050 DSET_NX(dset) , DSET_NY(dset) , DSET_NZ(dset) ) ;
1051 EXRETURN ;
1052 }
1053
1054 if( !ISVALID_MAT44(dset->daxes->ijk_to_dicom) ) /* get this matrix */
1055 THD_daxes_to_mat44(dset->daxes) ; /* if it's not OK */
1056
1057 DSET_CHECKAXES_REAL(dset) ; /* check both ijk_to_dicom matrices are same */
1058
1059 cmat = dset->daxes->ijk_to_dicom ; /* takes ijk to xyz */
1060 imat = MAT44_INV(cmat) ; /* takes xyz to ijk */
1061
1062 AA->cmat = cmat ; AA->imat = imat ;
1063 gstr = EDIT_get_geometry_string(dset) ;
1064 if( gstr != NULL ) AA->geomstring = gstr ;
1065 else AA->geomstring = NULL ; /* should not happen */
1066 AA->view = dset->view_type ;
1067
1068 #if 0
1069 INFO_message("IW3D_adopt_dataset geomstring = %s",AA->geomstring) ;
1070 DUMP_MAT44("IW3D_adopt_dataset cmat",cmat) ;
1071 #endif
1072
1073 EXRETURN ;
1074 }
1075
1076 /*---------------------------------------------------------------------------*/
1077 /* Warp pairs aren't used much; at one time, I thought they'd be used more
1078 (keep a copy of the inverse warp along with the forward warp at all times),
1079 but that just turned out not to happen.
1080 *//*-------------------------------------------------------------------------*/
1081
IW3D_pair_insert(IndexWarp3D * AA,IndexWarp3D * BB)1082 IndexWarp3D_pair * IW3D_pair_insert( IndexWarp3D *AA , IndexWarp3D *BB )
1083 {
1084 IndexWarp3D_pair *PP ;
1085
1086 PP = (IndexWarp3D_pair *)malloc(sizeof(IndexWarp3D_pair)) ;
1087 PP->fwarp = AA ; /* forward warp */
1088 PP->iwarp = BB ; /* inverse warp */
1089 return PP ;
1090 }
1091
1092 /*---------------------------------------------------------------------------*/
1093 /* Compute the inverse warp and insert it into the pair */
1094
IW3D_pair_invertify(IndexWarp3D_pair * PP)1095 void IW3D_pair_invertify( IndexWarp3D_pair *PP )
1096 {
1097 ENTRY("IW3D_pair_invertify") ;
1098 if( PP == NULL || PP->fwarp == NULL ) EXRETURN ;
1099 IW3D_destroy( PP->iwarp ) ;
1100 PP->iwarp = IW3D_invert( PP->fwarp , NULL , MRI_QUINTIC ) ;
1101 EXRETURN ;
1102 }
1103
1104 /*---------------------------------------------------------------------------*/
1105 /* Swap the forward and inverse warp pointers */
1106
IW3D_pair_swapify(IndexWarp3D_pair * PP)1107 void IW3D_pair_swapify( IndexWarp3D_pair *PP )
1108 {
1109 IndexWarp3D *AA ;
1110 if( PP == NULL ) return ;
1111 AA = PP->fwarp ; PP->fwarp = PP->iwarp ; PP->iwarp = AA ;
1112 return ;
1113 }
1114
1115 /*---------------------------------------------------------------------------*/
1116 /* Delete a warp pair from the universe */
1117
IW3D_pair_destroy(IndexWarp3D_pair * PP)1118 void IW3D_pair_destroy( IndexWarp3D_pair *PP )
1119 {
1120 if( PP != NULL ){
1121 IW3D_destroy(PP->fwarp) ;
1122 IW3D_destroy(PP->iwarp) ;
1123 free(PP) ;
1124 }
1125 return ;
1126 }
1127
1128 /*---------------------------------------------------------------------------*/
1129 /* A 'vacant' warp doesn't contain pointers to displacements;
1130 presumably, these will be supplied later (or the warp is a placeholder).
1131 *//*-------------------------------------------------------------------------*/
1132
IW3D_create_vacant(int nx,int ny,int nz)1133 IndexWarp3D * IW3D_create_vacant( int nx , int ny , int nz )
1134 {
1135 IndexWarp3D *AA ;
1136
1137 ENTRY("IW3D_create_vacant") ;
1138
1139 if( nx < NGMIN && ny < NGMIN && nz < NGMIN ) RETURN(NULL) ;
1140
1141 AA = (IndexWarp3D *)calloc(1,sizeof(IndexWarp3D)) ;
1142 AA->nx = nx ; AA->ny = ny ; AA->nz = nz ;
1143 AA->xd = NULL ; AA->yd = NULL ; AA->zd = NULL ;
1144 AA->hv = NULL ; AA->je = NULL ; AA->se = NULL ;
1145 LOAD_IDENT_MAT44(AA->cmat) ;
1146 LOAD_IDENT_MAT44(AA->imat) ;
1147 LOAD_IDENT_MAT44(AA->amat) ; LOAD_IDENT_MAT44(AA->amati) ; AA->use_amat = 0 ;
1148 AA->geomstring = NULL ;
1149 AA->view = VIEW_ORIGINAL_TYPE ;
1150
1151 RETURN(AA) ;
1152 }
1153
1154 /*---------------------------------------------------------------------------*/
1155 /* Create a copy of a dataset, but with zero-filled displacements */
1156
IW3D_empty_copy(IndexWarp3D * AA)1157 IndexWarp3D * IW3D_empty_copy( IndexWarp3D *AA )
1158 {
1159 IndexWarp3D *BB ; int nxyz ;
1160
1161 ENTRY("IW3D_empty_copy") ;
1162
1163 if( AA == NULL ) RETURN(NULL) ;
1164
1165 BB = IW3D_create( AA->nx , AA->ny , AA->nz ) ;
1166
1167 BB->cmat = AA->cmat ; BB->imat = AA->imat ;
1168 LOAD_IDENT_MAT44(AA->amat) ; LOAD_IDENT_MAT44(AA->amati) ; AA->use_amat = 0 ;
1169
1170 if( AA->geomstring != NULL )
1171 BB->geomstring = strdup(AA->geomstring) ;
1172
1173 BB->view = AA->view ;
1174
1175 RETURN(BB) ;
1176 }
1177
1178 /*---------------------------------------------------------------------------*/
1179 /* Make a copy, scaling displacements by fac. */
1180
IW3D_copy(IndexWarp3D * AA,float fac)1181 IndexWarp3D * IW3D_copy( IndexWarp3D *AA , float fac )
1182 {
1183 IndexWarp3D *BB ; int nxyz ;
1184
1185 ENTRY("IW3D_copy") ;
1186
1187 if( AA == NULL ) RETURN(NULL) ;
1188
1189 BB = IW3D_empty_copy(AA) ; /* all zero displacements */
1190
1191 nxyz = AA->nx * AA->ny * AA->nz ;
1192
1193 if( fac == 1.0f ){
1194 AAmemcpy( BB->xd , AA->xd , sizeof(float)*nxyz ) ;
1195 AAmemcpy( BB->yd , AA->yd , sizeof(float)*nxyz ) ;
1196 AAmemcpy( BB->zd , AA->zd , sizeof(float)*nxyz ) ;
1197 } else if( fac != 0.0f ){
1198 int qq ;
1199 for( qq=0 ; qq < nxyz ; qq++ ){
1200 BB->xd[qq] = fac * AA->xd[qq] ;
1201 BB->yd[qq] = fac * AA->yd[qq] ;
1202 BB->zd[qq] = fac * AA->zd[qq] ;
1203 }
1204 }
1205 BB->amat = AA->amat ;
1206 BB->amati = AA->amati ;
1207 BB->use_amat = AA->use_amat ;
1208
1209 RETURN(BB) ;
1210 }
1211
1212 /*---------------------------------------------------------------------------*/
1213 /* Same as IW3D_copy(), but for pairs -- this probably makes no sense */
1214
IW3D_pair_copy(IndexWarp3D_pair * AA,float fac)1215 IndexWarp3D_pair * IW3D_pair_copy( IndexWarp3D_pair *AA , float fac )
1216 {
1217 IndexWarp3D_pair *BB ;
1218
1219 if( AA == NULL ) return NULL ;
1220
1221 BB = (IndexWarp3D_pair *)malloc(sizeof(IndexWarp3D_pair)) ;
1222 BB->fwarp = IW3D_copy( AA->fwarp , fac ) ;
1223 BB->iwarp = IW3D_copy( AA->iwarp , fac ) ;
1224 return BB ;
1225 }
1226
1227 /*---------------------------------------------------------------------------*/
1228 /* Scale displacements by fac in-place -- not like IW3D_copy() */
1229
IW3D_scale(IndexWarp3D * AA,float fac)1230 void IW3D_scale( IndexWarp3D *AA , float fac )
1231 {
1232 int nxyz , qq ;
1233
1234 ENTRY("IW3D_scale") ;
1235
1236 if( AA == NULL || fac == 1.0f ) EXRETURN ;
1237
1238 nxyz = AA->nx * AA->ny * AA->nz ;
1239
1240 for( qq=0 ; qq < nxyz ; qq++ ){
1241 AA->xd[qq] *= fac ;
1242 AA->yd[qq] *= fac ;
1243 AA->zd[qq] *= fac ;
1244 }
1245
1246 EXRETURN ;
1247 }
1248
1249 /*---------------------------------------------------------------------------*/
1250 /* Scale displacements by 3 separate factors (in-place). */
1251
IW3D_3scale(IndexWarp3D * AA,float xfac,float yfac,float zfac)1252 void IW3D_3scale( IndexWarp3D *AA , float xfac, float yfac, float zfac )
1253 {
1254 int nxyz , qq ;
1255
1256 ENTRY("IW3D_3scale") ;
1257
1258 if( AA == NULL ) EXRETURN ;
1259
1260 nxyz = AA->nx * AA->ny * AA->nz ;
1261
1262 for( qq=0 ; qq < nxyz ; qq++ ){
1263 AA->xd[qq] *= xfac ;
1264 AA->yd[qq] *= yfac ;
1265 AA->zd[qq] *= zfac ;
1266 }
1267
1268 EXRETURN ;
1269 }
1270
1271 /*---------------------------------------------------------------------------*/
1272 /* Sum two warp displacements, with factors thrown in for fun. ????? */
1273
IW3D_sum(IndexWarp3D * AA,float Afac,IndexWarp3D * BB,float Bfac)1274 IndexWarp3D * IW3D_sum( IndexWarp3D *AA, float Afac, IndexWarp3D *BB, float Bfac )
1275 {
1276 IndexWarp3D *CC ; int nxyz , qq ;
1277
1278 ENTRY("IW3D_sum") ;
1279
1280 if( AA == NULL && BB == NULL ) RETURN(NULL) ;
1281
1282 if( AA == NULL || Afac == 0.0f ){ /* is one input not used? */
1283 CC = IW3D_copy( BB , Bfac ) ; RETURN(CC) ;
1284 } else if( BB == NULL || Bfac == 0.0f ){
1285 CC = IW3D_copy( AA , Afac ) ; RETURN(CC) ;
1286 }
1287
1288 nxyz = AA->nx * AA->ny * AA->nz ;
1289
1290 if( BB->nx * BB->ny * BB->nz != nxyz ) RETURN(NULL) ; /* bad inputs! */
1291
1292 CC = IW3D_empty_copy(AA) ; /* all zero displacements */
1293
1294 for( qq=0 ; qq < nxyz ; qq++ ){
1295 CC->xd[qq] = Afac * AA->xd[qq] + Bfac * BB->xd[qq] ;
1296 CC->yd[qq] = Afac * AA->yd[qq] + Bfac * BB->yd[qq] ;
1297 CC->zd[qq] = Afac * AA->zd[qq] + Bfac * BB->zd[qq] ;
1298 }
1299
1300 RETURN(CC) ;
1301 }
1302
1303 #endif /*(C3)*/ /*###########################################################*/
1304
1305 #if 1
1306 /*===========================================================================*/
1307 /* (C4) The functions below extend or truncate a warp grid. */
1308 /*===========================================================================*/
1309
1310 /*---------------------------------------------------------------------------*/
1311 /* Extend (and/or truncate) an index warp.
1312 Extension outside the original grid is via linear extrapolation,
1313 unless zpad != 0, in which case extensions of the displacements outside
1314 the original grid are just set to zero. ?????
1315 *//*-------------------------------------------------------------------------*/
1316
IW3D_extend(IndexWarp3D * AA,int nxbot,int nxtop,int nybot,int nytop,int nzbot,int nztop,int zpad)1317 IndexWarp3D * IW3D_extend( IndexWarp3D *AA , int nxbot , int nxtop ,
1318 int nybot , int nytop ,
1319 int nzbot , int nztop , int zpad )
1320 {
1321 IndexWarp3D *BB ;
1322 int nxold,nyold,nzold , nxnew,nynew,nznew ;
1323
1324 ENTRY("IW3D_extend") ;
1325
1326 if( AA == NULL ) RETURN(NULL) ;
1327
1328 nxold = AA->nx ; nyold = AA->ny ; nzold = AA->nz ;
1329 nxnew = nxold + nxbot + nxtop ; if( nxnew < 1 ) RETURN(NULL) ;
1330 nynew = nyold + nybot + nytop ; if( nynew < 1 ) RETURN(NULL) ;
1331 nznew = nzold + nzbot + nztop ; if( nznew < 1 ) RETURN(NULL) ;
1332
1333 BB = IW3D_create_vacant( nxnew , nynew , nznew ) ;
1334
1335 /* here, any extension is zero-padded by EDIT_volpad */
1336
1337 if( AA->xd != NULL )
1338 BB->xd = (float *)EDIT_volpad( nxbot,nxtop, nybot,nytop, nzbot,nztop,
1339 nxold,nyold,nzold , MRI_float , AA->xd ) ;
1340 if( AA->yd != NULL )
1341 BB->yd = (float *)EDIT_volpad( nxbot,nxtop, nybot,nytop, nzbot,nztop,
1342 nxold,nyold,nzold , MRI_float , AA->yd ) ;
1343 if( AA->zd != NULL )
1344 BB->zd = (float *)EDIT_volpad( nxbot,nxtop, nybot,nytop, nzbot,nztop,
1345 nxold,nyold,nzold , MRI_float , AA->zd ) ;
1346
1347 /*----------------------------------------------------------------------------------*/
1348 #if 0
1349 #undef AR
1350 #undef BR
1351 #define AR(qd,i,j,k) AA->qd[(i)+(j)*nxold+(k)*nxyold] /* (i,j,k) in AA array */
1352 #define BR(qd,i,j,k) BB->qd[(i)+(j)*nxnew+(k)*nxynew] /* (i,j,k) in BB array */
1353
1354 /* Use external slopes to extend in a nonzero way outside the box [Apr 2014] */
1355
1356 if( !zpad && (nxbot > 0 || nxtop > 0 || nybot > 0 || nytop > 0 || nzbot > 0 || nztop > 0) ){
1357 int nxyold=nxold*nyold, nxynew=nxnew*nynew, nxo1=nxold-1, nyo1=nyold-1, nzo1=nzold-1 ;
1358
1359 IW3D_load_external_slopes(AA) ;
1360
1361 AFNI_OMP_START ;
1362 #pragma omp parallel if( nznew > 16 )
1363 { int ii,jj,kk, iq,jq,kq, di,dj,dk ;
1364 float Exx,Exy,Exz, Eyx,Eyy,Eyz, Ezx,Ezy,Ezz ;
1365 for( kk=0 ; kk < nznew ; kk++ ){
1366 kq = kk-nzbot ; dk = 0 ; Exz = Eyz = Ezz = 0.0f ;
1367 if( kq < 0 ) { dk = kq ; kq = 0 ; Exz=AA->es_xd_zm*dk; Eyz=AA->es_yd_zm*dk; Ezz=AA->es_zd_zm*dk; }
1368 else if( kq > nzo1 ){ dk = kq-nzo1; kq=nzo1; Exz=AA->es_xd_zp*dk; Eyz=AA->es_yd_zp*dk; Ezz=AA->es_zd_zp*dk; }
1369 for( jj=0 ; jj < nynew ; jj++ ){
1370 jq = jj-nybot ; dj = 0 ; Exy = Eyy = Ezy = 0.0f ;
1371 if( jq < 0 ) { dj = jq ; jq = 0 ; Exy=AA->es_xd_ym*dj; Eyy=AA->es_yd_ym*dj; Ezy=AA->es_zd_ym*dj; }
1372 else if( jq > nyo1 ){ dj = jq-nyo1; jq=nyo1; Exy=AA->es_xd_yp*dj; Eyy=AA->es_yd_yp*dj; Ezy=AA->es_zd_yp*dj; }
1373 for( ii=0 ; ii < nxnew ; ii++ ){
1374 iq = ii-nybot ; di = 0 ; Exx = Eyx = Ezx = 0.0f ;
1375 if( iq < 0 ) { di = iq ; iq = 0 ; Exx=AA->es_xd_xm*di; Eyx=AA->es_yd_xm*di; Ezx=AA->es_zd_xm*di; }
1376 else if( iq > nxo1 ){ di = iq-nxo1; iq=nxo1; Exx=AA->es_xd_xp*di; Eyx=AA->es_yd_xp*di; Ezx=AA->es_zd_xp*di; }
1377 if( di || dj || dk ){
1378 BR(xd,ii,jj,kk) = AR(xd,iq,jq,kq) + Exx + Exy + Exz ;
1379 BR(yd,ii,jj,kk) = AR(yd,iq,jq,kq) + Eyx + Eyy + Eyz ;
1380 BR(zd,ii,jj,kk) = AR(zd,iq,jq,kq) + Ezx + Ezy + Ezz ;
1381 }
1382 } } }
1383 }
1384 AFNI_OMP_END ;
1385 }
1386 #undef AR
1387 #undef BR
1388 #endif
1389 /*----------------------------------------------------------------------------------*/
1390
1391 /* the extended warp requires a modified geometry string,
1392 done in the most brute force way anyone could ever imagine:
1393 make a dataset (header only) = qset
1394 zero pad it (header only) = adset
1395 the new index warp adopts adset's geometry
1396 throw these temporary datasets away as untouchable trash */
1397
1398 if( AA->geomstring != NULL ){
1399 THD_3dim_dataset *qset , *adset ;
1400 qset = EDIT_geometry_constructor(AA->geomstring,"TweedleDum") ;
1401 adset = THD_zeropad( qset , nxbot,nxtop , nybot,nytop , nzbot,nztop ,
1402 "TweedleDee" , ZPAD_IJK | ZPAD_EMPTY ) ;
1403 IW3D_adopt_dataset( BB , adset ) ;
1404 DSET_delete(adset) ; DSET_delete(qset) ; /* the trash */
1405 }
1406
1407 BB->view = AA->view ;
1408 RETURN(BB) ;
1409 }
1410
1411 /*---------------------------------------------------------------------------*/
1412 /* This function used to be more impressive, but now just calls the
1413 one above to do its work -- but there is no extrapolation (zpad==1).
1414 *//*-------------------------------------------------------------------------*/
1415
IW3D_zeropad(IndexWarp3D * AA,int nxbot,int nxtop,int nybot,int nytop,int nzbot,int nztop)1416 IndexWarp3D * IW3D_zeropad( IndexWarp3D *AA , int nxbot , int nxtop ,
1417 int nybot , int nytop ,
1418 int nzbot , int nztop )
1419 {
1420 return IW3D_extend( AA, nxbot,nxtop,nybot,nytop,nzbot,nztop , 1 ) ;
1421 }
1422
1423 /*---------------------------------------------------------------------------*/
1424 /* Extend (and/or truncate) a dataset that represents a warp;
1425 this is done the brute force way, by conversion to an index warp,
1426 extension of that, and then conversion back to a dataset struct.
1427 Note that there is no option for zero-padded extension.
1428 *//*-------------------------------------------------------------------------*/
1429
THD_nwarp_extend(THD_3dim_dataset * dset_nwarp,int nxbot,int nxtop,int nybot,int nytop,int nzbot,int nztop)1430 THD_3dim_dataset * THD_nwarp_extend( THD_3dim_dataset *dset_nwarp ,
1431 int nxbot , int nxtop ,
1432 int nybot , int nytop ,
1433 int nzbot , int nztop )
1434 {
1435 IndexWarp3D *AA , *BB ;
1436 THD_3dim_dataset *qset ;
1437
1438 ENTRY("THD_nwarp_extend") ;
1439
1440 if( dset_nwarp == NULL || DSET_NVALS(dset_nwarp) < 3 ){
1441 ERROR_message("Warp extend(%d,%d,%d,%d,%d,%d) fails: input warp %s invalid",
1442 nxbot,nxtop,nybot,nytop,nzbot,nztop ,
1443 ISVALID_DSET(dset_nwarp) ? DSET_BRIKNAME(dset_nwarp) : "NULL" ) ;
1444 RETURN(NULL) ;
1445 }
1446 DSET_load(dset_nwarp) ;
1447 if( !DSET_LOADED(dset_nwarp) ){
1448 ERROR_message("Warp extend(%d,%d,%d,%d,%d,%d) fails: can't load warp dataset %s from disk",
1449 nxbot,nxtop,nybot,nytop,nzbot,nztop ,
1450 DSET_BRIKNAME(dset_nwarp) ) ;
1451 RETURN(NULL) ;
1452 }
1453
1454 AA = IW3D_from_dataset( dset_nwarp , 0 , 0 ) ;
1455 DSET_unload(dset_nwarp) ;
1456 if( AA == NULL ){
1457 ERROR_message("Warp extend(%d,%d,%d,%d,%d,%d) fails: can't convert dataset %s to IW3D format",
1458 nxbot,nxtop,nybot,nytop,nzbot,nztop ,
1459 DSET_BRIKNAME(dset_nwarp) ) ;
1460 RETURN(NULL) ;
1461 }
1462
1463 BB = IW3D_extend( AA , nxbot,nxtop,nybot,nytop,nzbot,nztop , 0 ) ;
1464 IW3D_destroy(AA) ;
1465 if( BB == NULL ){
1466 ERROR_message("Warp extend(%d,%d,%d,%d,%d,%d) fails: can't extend IW3D format of dataset %s" ,
1467 nxbot,nxtop,nybot,nytop,nzbot,nztop ,
1468 DSET_BRIKNAME(dset_nwarp) ) ;
1469 RETURN(NULL) ;
1470 }
1471
1472 qset = IW3D_to_dataset( BB , "ExtendedWarp" ) ;
1473 IW3D_destroy(BB) ;
1474 if( qset == NULL ){
1475 ERROR_message("Warp extend(%d,%d,%d,%d,%d,%d) fails: can't convert IW3D format of %s back to dataset" ,
1476 nxbot,nxtop,nybot,nytop,nzbot,nztop ,
1477 DSET_BRIKNAME(dset_nwarp) ) ;
1478 }
1479 RETURN(qset) ;
1480 }
1481
1482 /*---------------------------------------------------------------------------*/
1483 /* Find the bounding box for the warp, which is defined 2 ways:
1484 A) the box that includes all displacments > dthr
1485 B) the box that includes the displaced locations of all voxels
1486 with displacment > dthr
1487 It is possible (if unlikely) that the box will actually be bigger
1488 than the warp grid, due to condition B.
1489 *//*-------------------------------------------------------------------------*/
1490
IW3D_warpbox(IndexWarp3D * AA,float fac,float dthr)1491 int_sextet IW3D_warpbox( IndexWarp3D *AA , float fac , float dthr )
1492 {
1493 int qq , nx,ny,nz,nxy,nxyz , ii,jj,kk ;
1494 float di,dj,dk , dx,dy,dz,dq , *xda,*yda,*zda ;
1495 MRI_IMAGE *qim ; byte *qmm ;
1496 int ibot,itop,jbot,jtop,kbot,ktop ; int_sextet sext={0,0,0,0,0,0} ;
1497 float xbot,xtop,ybot,ytop,zbot,ztop ;
1498
1499 if( AA == NULL ) return sext ;
1500
1501 if( fac == 0.0f ) fac = 1.0f ;
1502 if( dthr == 0.0f ) dthr = 0.09999f ;
1503 dthr = dthr*dthr ;
1504
1505 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
1506
1507 xbot = ybot = zbot = 999999.9f ;
1508 xtop = ytop = ztop = -999999.9f ;
1509
1510 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
1511
1512 qim = mri_new_vol( nx,ny,nz , MRI_byte ) ; /* mask of above threshold */
1513 qmm = MRI_BYTE_PTR(qim) ; /* displacments */
1514
1515 for( qq=0 ; qq < nxyz ; qq++ ){
1516
1517 dx = fabsf(fac * xda[qq]) ;
1518 dy = fabsf(fac * yda[qq]) ;
1519 dz = fabsf(fac * zda[qq]) ; dq = dx*dx+dy*dy+dz*dz ;
1520
1521 qmm[qq] = ( dq > dthr ) ; /* mask that displacement is "big" or not */
1522
1523 if( qmm[qq] == 0 ) continue ; /* not in mask ==> dont check */
1524
1525 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
1526
1527 di = ii - dx ; if( di < xbot ) xbot = di ;
1528 di = ii + dx ; if( di > xtop ) xtop = di ;
1529
1530 dj = jj - dy ; if( dj < ybot ) ybot = dj ;
1531 dj = jj + dy ; if( dj > ytop ) ytop = dj ;
1532
1533 dk = kk - dz ; if( dk < zbot ) zbot = dk ;
1534 dk = kk + dz ; if( dk > ztop ) ztop = dk ;
1535 }
1536
1537 /* box from mask of "big" displacments */
1538
1539 MRI_autobbox_byte( qim , &ibot,&itop , &jbot,&jtop , &kbot,&ktop ) ;
1540 mri_free(qim) ;
1541
1542 /* extend it if some displacment goes farther than the above box */
1543
1544 if( xbot < ibot ) ibot = (int)floorf(xbot) ;
1545 if( ybot < jbot ) jbot = (int)floorf(ybot) ;
1546 if( zbot < kbot ) kbot = (int)floorf(zbot) ;
1547 if( xtop > itop ) itop = (int)ceilf (xtop) ;
1548 if( ytop > jtop ) jtop = (int)ceilf (ytop) ;
1549 if( ztop > ktop ) ktop = (int)ceilf (ztop) ;
1550
1551 sext.a = ibot ; sext.b = itop ;
1552 sext.c = jbot ; sext.d = jtop ;
1553 sext.e = kbot ; sext.f = ktop ;
1554
1555 return sext ;
1556 }
1557
1558 /*---------------------------------------------------------------------------*/
1559 /* Mistaken duplicate of the above [29 Mar 2021] */
1560
IW3D_bounding_box(IndexWarp3D * AA,float thresh)1561 int_sextet IW3D_bounding_box( IndexWarp3D *AA , float thresh )
1562 {
1563 #if 1
1564 return IW3D_warpbox( AA , 1.0f , thresh ) ;
1565 #else
1566 MRI_IMAGE *bim ; byte *bar ; int_sextet sbox={0,0,0,0,0,0} ;
1567 int nx,ny,nz , nxy,nxyz , ii , aa=0,bb=0,cc=0,dd=0,ee=0,ff=0 ;
1568 float val , *xd,*yd,*zd ;
1569
1570 ENTRY("IW3D_bounding_box") ;
1571
1572 if( AA == NULL ) RETURN(sbox) ;
1573
1574 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
1575 xd = AA->xd ; yd = AA->yd ; zd = AA->zd ;
1576
1577 bim = mri_new_vol( nx,ny,nz , MRI_byte ) ;
1578 bar = MRI_BYTE_PTR(bim) ;
1579
1580 if( thresh < 0.0f ) thresh = 0.099999f ;
1581 thresh = thresh*thresh ;
1582
1583 for( ii=0 ; ii < nxyz ; ii++ ){
1584 val = xd[ii]*xd[ii] + yd[ii]*yd[ii] + zd[ii]*zd[ii] ;
1585 bar[ii] = (val > thresh) ;
1586 }
1587
1588 MRI_autobbox_byte( bim , &aa,&bb,&cc,&dd,&ee,&ff ) ;
1589 sbox.a = aa ; sbox.b = bb ; sbox.c = cc ;
1590 sbox.d = dd ; sbox.e = ee ; sbox.f = ff ;
1591
1592 mri_free(bim) ; RETURN(sbox) ;
1593 #endif
1594 }
1595
1596 /*---------------------------------------------------------------------------*/
1597 /* Zero out an index warp outside of its bounding box [29 Mar 2021] */
1598
IW3D_bounding_box_clear(IndexWarp3D * AA,float thresh)1599 void IW3D_bounding_box_clear( IndexWarp3D *AA , float thresh )
1600 {
1601 float *xd,*yd,*zd ;
1602 int_sextet sbox ;
1603 int nx,ny,nz,nxy,nxyz , ii,jj,kk,qq , ibot,itop , jbot,jtop , kbot,ktop ;
1604
1605 ENTRY("IW3D_bounding_box_clear") ;
1606
1607 if( AA == NULL ) EXRETURN ;
1608
1609 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
1610 xd = AA->xd ; yd = AA->yd ; zd = AA->zd ;
1611
1612 sbox = IW3D_bounding_box( AA , thresh ) ;
1613 ibot = sbox.a ; itop = sbox.b ;
1614 jbot = sbox.c ; jtop = sbox.d ;
1615 kbot = sbox.e ; ktop = sbox.f ;
1616 if( ibot==0 && itop==0 && jbot==0 && jtop==0 && kbot==0 && ktop==0 ) EXRETURN ;
1617
1618 for( qq=0 ; qq < nxyz ; qq++ ){
1619 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
1620 if( !( ii >= ibot && ii <= itop &&
1621 jj >= jbot && jj <= jtop &&
1622 kk >= kbot && kk <= ktop ) ){ xd[qq] = yd[qq] = zd[qq] = 0.0f ; }
1623 }
1624
1625 EXRETURN ;
1626 }
1627
1628 #endif /*(C4)*/ /*###########################################################*/
1629
1630 #if 1
1631 /*===========================================================================*/
1632 /* (C5) The functions below compute various 'norms' (size
1633 estimates) of an index warp. These comprise pretty simple code. */
1634 /*===========================================================================*/
1635
1636 /*---------------------------------------------------------------------------*/
1637 /* If BB == NULL, just the L1 norm of AA. Otherwise, the norm of AA-BB. */
1638
IW3D_normL1(IndexWarp3D * AA,IndexWarp3D * BB)1639 float IW3D_normL1( IndexWarp3D *AA , IndexWarp3D *BB )
1640 {
1641 int qq , nxyz ; float sum , *xda,*yda,*zda ;
1642
1643 if( AA == NULL ){
1644 if( BB == NULL ) return 0.0f ;
1645 AA = BB ; BB = NULL ;
1646 }
1647
1648 nxyz = AA->nx * AA->ny * AA->nz ;
1649 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
1650
1651 sum = 0.0f ;
1652 if( BB == NULL || BB->nx != AA->nx || BB->ny != AA->ny || BB->nz != AA->nz ){
1653 for( qq=0 ; qq < nxyz ; qq++ )
1654 sum += fabsf(xda[qq])+fabsf(yda[qq])+fabsf(zda[qq]) ;
1655 } else {
1656 float *xdb=BB->xd , *ydb = BB->yd , *zdb = BB->zd ;
1657 for( qq=0 ; qq < nxyz ; qq++ )
1658 sum += fabsf(xda[qq]-xdb[qq])+fabsf(yda[qq]-ydb[qq])+fabsf(zda[qq]-zdb[qq]) ;
1659 }
1660
1661 return (sum/nxyz) ;
1662 }
1663
1664 /*---------------------------------------------------------------------------*/
1665 /* If BB == NULL, just the L2 norm of AA. Otherwise, the norm of AA-BB. */
1666
IW3D_normL2(IndexWarp3D * AA,IndexWarp3D * BB)1667 float IW3D_normL2( IndexWarp3D *AA , IndexWarp3D *BB )
1668 {
1669 int qq , nxyz ; float sum , *xda,*yda,*zda ;
1670
1671 if( AA == NULL ){
1672 if( BB == NULL ) return 0.0f ;
1673 AA = BB ; BB = NULL ;
1674 }
1675
1676 nxyz = AA->nx * AA->ny * AA->nz ;
1677 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
1678
1679 sum = 0.0f ;
1680 if( BB == NULL || BB->nx != AA->nx || BB->ny != AA->ny || BB->nz != AA->nz ){
1681 for( qq=0 ; qq < nxyz ; qq++ )
1682 sum += SQR(xda[qq])+SQR(yda[qq])+SQR(zda[qq]) ;
1683 } else {
1684 float *xdb=BB->xd , *ydb = BB->yd , *zdb = BB->zd ;
1685 for( qq=0 ; qq < nxyz ; qq++ )
1686 sum += SQR(xda[qq]-xdb[qq])+SQR(yda[qq]-ydb[qq])+SQR(zda[qq]-zdb[qq]) ;
1687 }
1688
1689 return sqrtf(sum/nxyz) ;
1690 }
1691
1692 /*---------------------------------------------------------------------------*/
1693 /* If BB == NULL, the L-infinity norm of AA. Otherwise, the norm of AA-BB. */
1694
IW3D_normLinf(IndexWarp3D * AA,IndexWarp3D * BB)1695 float IW3D_normLinf( IndexWarp3D *AA , IndexWarp3D *BB )
1696 {
1697 int qq , nxyz ; float vmax,val , *xda,*yda,*zda ;
1698
1699 if( AA == NULL ){
1700 if( BB == NULL ) return 0.0f ;
1701 AA = BB ; BB = NULL ;
1702 }
1703
1704 nxyz = AA->nx * AA->ny * AA->nz ;
1705 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
1706
1707 vmax = 0.0f ;
1708 if( BB == NULL || BB->nx != AA->nx || BB->ny != AA->ny || BB->nz != AA->nz ){
1709 for( qq=0 ; qq < nxyz ; qq++ ){
1710 val = SQR(xda[qq])+SQR(yda[qq])+SQR(zda[qq]) ;
1711 if( val > vmax ) vmax = val ;
1712 }
1713 } else {
1714 float *xdb=BB->xd , *ydb = BB->yd , *zdb = BB->zd ;
1715 for( qq=0 ; qq < nxyz ; qq++ ){
1716 val = SQR(xda[qq]-xdb[qq])+SQR(yda[qq]-ydb[qq])+SQR(zda[qq]-zdb[qq]) ;
1717 if( val > vmax ) vmax = val ;
1718 }
1719 }
1720
1721 return sqrtf(vmax) ;
1722 }
1723
1724 /*---------------------------------------------------------------------------*/
1725 /* Find the max displacements in 3D from a warp stored as a dataset. */
1726
THD_nwarp_maxdisp(THD_3dim_dataset * nset)1727 float_triple THD_nwarp_maxdisp( THD_3dim_dataset *nset )
1728 {
1729 float_triple dxyz = {0.0f,0.0f,0.f} ;
1730 float dx=0.0f,dy=0.0f,dz=0.0f ;
1731 float *xd,*yd,*zd , val ;
1732 int nvox , ii ;
1733
1734 ENTRY("THD_nwarp_maxdisp") ;
1735
1736 if( !ISVALID_DSET(nset) ) RETURN(dxyz) ;
1737 if( DSET_NVALS(nset) < 3 ) RETURN(dxyz) ;
1738 if( DSET_BRICK_TYPE(nset,0) != MRI_float ) RETURN(dxyz) ;
1739 DSET_load(nset) ; if( !DSET_LOADED(nset) ) RETURN(dxyz) ;
1740
1741 xd = (float *)DSET_ARRAY(nset,0) ;
1742 yd = (float *)DSET_ARRAY(nset,1) ;
1743 zd = (float *)DSET_ARRAY(nset,2) ;
1744 nvox = DSET_NVOX(nset) ;
1745 for( ii=0 ; ii < nvox ; ii++ ){
1746 val = fabsf(xd[ii]) ; if( val > dx ) dx = val ;
1747 val = fabsf(yd[ii]) ; if( val > dy ) dy = val ;
1748 val = fabsf(zd[ii]) ; if( val > dz ) dz = val ;
1749 }
1750 dxyz.a = dx ; dxyz.b = dy ; dxyz.c = dz ; RETURN(dxyz) ;
1751 }
1752
1753 /*===========================================================================*/
1754 #if 0
1755 /*----------------------------------------------------------------------------*/
1756 /* smooth a warp locally (obviously not implemented!) */
1757
1758 #define M7 0.142857143f /* 1/7 */
1759 #define M28 0.035714286f /* 1/28 */
1760 #define M84 0.011904762f /* 1/84 */
1761
1762 void IW3D_7smooth( IndexWarp3D *AA )
1763 {
1764 }
1765 /*----------------------------------------------------------------------------*/
1766 #endif
1767 /*============================================================================*/
1768
1769 #endif /*(C5)*/ /*############################################################*/
1770
1771 #if 1
1772 /*===========================================================================*/
1773 /* (C6) Functions below do conversions between index warps and dataset warps */
1774 /*===========================================================================*/
1775
1776 /*----------------------------------------------------------------------------*/
1777 /* Convert a 3D dataset of displacments in mm to an index warp.
1778 empty != 0 ==> displacements will be all zero
1779 ivs != 0 ==> extract sub-bricks [ivs..ivs+2] for the displacments
1780 Normal usage has empty = ivs = 0.
1781 See IW3D_to_dataset() for the reverse operation. ?????
1782 *//*--------------------------------------------------------------------------*/
1783
IW3D_from_dataset(THD_3dim_dataset * dset,int empty,int ivs)1784 IndexWarp3D * IW3D_from_dataset( THD_3dim_dataset *dset , int empty , int ivs )
1785 {
1786 IndexWarp3D *AA ;
1787 MRI_IMAGE *xim , *yim , *zim ;
1788 mat44 cmat , imat ;
1789 int nx,ny,nz , nxyz , ii ;
1790 float *xar,*yar,*zar , *xda,*yda,*zda ;
1791 char *gstr ;
1792
1793 ENTRY("IW3D_from_dataset") ;
1794
1795 if( !ISVALID_DSET(dset) || ivs < 0 ) RETURN(NULL) ;
1796
1797 /* check if dataset is properly constituted, if needed */
1798
1799 if( !empty ){
1800 if( DSET_NVALS(dset) < 3+ivs ) RETURN(NULL) ;
1801 if( !DSET_LOADED(dset) ){
1802 DSET_load(dset) ; if( !DSET_LOADED(dset) ) RETURN(NULL) ;
1803 }
1804 }
1805
1806 if( !ISVALID_MAT44(dset->daxes->ijk_to_dicom) )
1807 THD_daxes_to_mat44(dset->daxes) ;
1808
1809 /* coordinate transformation, needed to take mm displacements
1810 in the dataset to voxel index displacments in the index warp */
1811
1812 cmat = dset->daxes->ijk_to_dicom ; /* takes ijk to xyz */
1813 imat = MAT44_INV(cmat) ; /* takes xyz to ijk */
1814
1815 nx = DSET_NX(dset); ny = DSET_NY(dset); nz = DSET_NZ(dset); nxyz = nx*ny*nz;
1816
1817 AA = IW3D_create(nx,ny,nz) ; /* the output of this function */
1818
1819 AA->cmat = cmat ; AA->imat = imat ;
1820 gstr = EDIT_get_geometry_string(dset) ;
1821 if( gstr != NULL ) AA->geomstring = gstr ;
1822 else AA->geomstring = NULL ; /* should never happen! */
1823
1824 AA->view = dset->view_type ;
1825
1826 if( !empty ){
1827 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
1828 xim = THD_extract_float_brick(ivs+0,dset) ; xar = MRI_FLOAT_PTR(xim) ;
1829 yim = THD_extract_float_brick(ivs+1,dset) ; yar = MRI_FLOAT_PTR(yim) ;
1830 zim = THD_extract_float_brick(ivs+2,dset) ; zar = MRI_FLOAT_PTR(zim) ;
1831 DSET_unload(dset) ;
1832
1833 /* convert displacments in DICOM xyz mm to index displacements */
1834
1835 AFNI_OMP_START ;
1836 #pragma omp parallel if( nxyz > 1111 )
1837 { int ii ;
1838 #pragma omp for
1839 for( ii=0 ; ii < nxyz ; ii++ ){ /* convert mm to index displacements */
1840 MAT33_VEC( imat , xar[ii],yar[ii],zar[ii] , xda[ii],yda[ii],zda[ii] ) ;
1841 }
1842 }
1843 AFNI_OMP_END ;
1844
1845 mri_free(zim) ; mri_free(yim) ; mri_free(xim) ;
1846 }
1847
1848 RETURN(AA) ;
1849 }
1850
1851 #if 0
1852 /*----------------------------------------------------------------------------*/
1853 /* Create a warp pair from a single dataset (not used anywhere, I think).
1854 Uses sub-bricks 0-2 for the forward warp, and 3-5 for the inverse warp.
1855 If sub-bricks 3-5 aren't there, then the inverse warp is created.
1856 *//*--------------------------------------------------------------------------*/
1857
1858 IndexWarp3D_pair * IW3D_pair_from_dataset( THD_3dim_dataset *dset )
1859 {
1860 IndexWarp3D_pair *PP ;
1861
1862 ENTRY("IW3D_pair_from_dataset") ;
1863
1864 if( !ISVALID_DSET(dset) ) RETURN(NULL) ;
1865
1866 if( DSET_NVALS(dset) < 3 ) RETURN(NULL) ;
1867 DSET_load(dset) ; if( !DSET_LOADED(dset) ) RETURN(NULL) ;
1868
1869 PP = (IndexWarp3D_pair *)malloc(sizeof(IndexWarp3D_pair)) ;
1870 PP->iwarp = NULL ;
1871
1872 PP->fwarp = IW3D_from_dataset( dset , 0 , 0 ) ;
1873 if( PP->fwarp == NULL ){
1874 IW3D_pair_destroy(PP) ; RETURN(NULL) ;
1875 }
1876
1877 if( DSET_NVALS(dset) >= 6 )
1878 PP->iwarp = IW3D_from_dataset( dset , 0 , 3 ) ;
1879 if( PP->iwarp == NULL )
1880 PP->iwarp = IW3D_invert( PP->fwarp , NULL , MRI_LINEAR ) ;
1881
1882 RETURN(PP) ;
1883 }
1884 #endif
1885
1886 /*----------------------------------------------------------------------------*/
1887 /* Convert an index warp to a 3D dataset of spatial displacmements in mm.
1888 See IW3D_from_dataset() for the reverse operation.
1889 See IW3D_to_index_dataset() to convert index displacmeents without
1890 scaling to mm.
1891 *//*--------------------------------------------------------------------------*/
1892
1893 static int save_aux_volumes = 0 ; /* user sets this to get auxiliary volumes */
1894
IW3D_to_dataset(IndexWarp3D * AA,char * prefix)1895 THD_3dim_dataset * IW3D_to_dataset( IndexWarp3D *AA , char *prefix )
1896 {
1897 THD_3dim_dataset *dset ;
1898 float *xar,*yar,*zar,*har,*jar,*sar , *xda,*yda,*zda,*hva,*jea,*sea , hfac ;
1899 mat44 cmat , imat ;
1900 int ii , nxyz ;
1901
1902 ENTRY("IW3D_to_dataset") ;
1903
1904 if( AA == NULL ) RETURN(NULL) ;
1905
1906 /* we need the geometry string to know how to contstruct the dataset */
1907
1908 if( AA->geomstring == NULL ){
1909 char *gstr = EDIT_imat_to_geometry_string(AA->imat,AA->nx,AA->ny,AA->nz) ;
1910 if( gstr == NULL ) RETURN(NULL) ; /* should not transpire */
1911 AA->geomstring = gstr ;
1912 }
1913
1914 STATUS("create dataset") ;
1915 dset = EDIT_geometry_constructor( AA->geomstring , prefix ) ;
1916 DSET_CHECKAXES_REAL(dset) ;
1917
1918 EDIT_dset_items( dset ,
1919 ADN_nvals , (save_aux_volumes) ? 6 : 3 ,
1920 ADN_datum_all , MRI_float ,
1921 ADN_view_type , AA->view ,
1922 NULL ) ;
1923 EDIT_BRICK_LABEL( dset , 0 , "x_delta" ) ;
1924 EDIT_BRICK_LABEL( dset , 1 , "y_delta" ) ;
1925 EDIT_BRICK_LABEL( dset , 2 , "z_delta" ) ;
1926 if( save_aux_volumes ){
1927 EDIT_BRICK_LABEL( dset , 3 , "hexvol" ) ;
1928 EDIT_BRICK_LABEL( dset , 4 , "BulkEn" ) ;
1929 EDIT_BRICK_LABEL( dset , 5 , "ShearEn" ) ;
1930 }
1931
1932 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ; /* index displacment arrays */
1933 nxyz = AA->nx * AA->ny * AA->nz ;
1934 cmat = AA->cmat ; imat = AA->imat ;
1935
1936 #if 0
1937 DUMP_MAT44("IW3D_to_dataset cmat",cmat) ;
1938 DUMP_MAT44("IW3D_to_dataset dset ijk_to_dicom",dset->daxes->ijk_to_dicom) ;
1939 DUMP_MAT44("IW3D_to_dataset dset ijk_to_dicom_real",dset->daxes->ijk_to_dicom_real) ;
1940 #endif
1941
1942 xar = (float *)malloc(sizeof(float)*nxyz) ; /* output arrays for dataset */
1943 yar = (float *)malloc(sizeof(float)*nxyz) ;
1944 zar = (float *)malloc(sizeof(float)*nxyz) ;
1945 if( save_aux_volumes ){
1946 har = (float *)malloc(sizeof(float)*nxyz) ;
1947 jar = (float *)malloc(sizeof(float)*nxyz) ;
1948 sar = (float *)malloc(sizeof(float)*nxyz) ;
1949
1950 STATUS("load hexvol") ;
1951 (void)IW3D_load_hexvol(AA,NULL) ; /* compute the aux data into */
1952 STATUS("load energy") ; /* the warp structure */
1953 (void)IW3D_load_energy(AA) ;
1954 STATUS("done with aux volumes") ;
1955 jea = AA->je ; sea = AA->se ;
1956 } else {
1957 har = jar = sar = jea = sea = NULL ;
1958 }
1959 hva = AA->hv ; hfac = MAT44_DET(cmat) ; hfac = fabsf(hfac) ;
1960
1961 STATUS("transform to displacements in mm") ;
1962 AFNI_OMP_START ;
1963 #pragma omp parallel if( nxyz > 1111 )
1964 { int ii ;
1965 #pragma omp for
1966 for( ii=0 ; ii < nxyz ; ii++ ){
1967 MAT33_VEC( cmat , xda[ii],yda[ii],zda[ii] , xar[ii],yar[ii],zar[ii] ) ;
1968 if( save_aux_volumes ){
1969 har[ii] = hfac * hva[ii] ; jar[ii] = jea[ii] ; sar[ii] = sea[ii] ;
1970 }
1971 }
1972 }
1973 AFNI_OMP_END ;
1974
1975 STATUS("substitute bricks") ;
1976 EDIT_substitute_brick( dset , 0 , MRI_float , xar ) ;
1977 EDIT_substitute_brick( dset , 1 , MRI_float , yar ) ;
1978 EDIT_substitute_brick( dset , 2 , MRI_float , zar ) ;
1979 if( save_aux_volumes ){
1980 EDIT_substitute_brick( dset , 3 , MRI_float , har ) ;
1981 EDIT_substitute_brick( dset , 4 , MRI_float , jar ) ;
1982 EDIT_substitute_brick( dset , 5 , MRI_float , sar ) ;
1983 }
1984
1985 STATUS("done") ;
1986 RETURN(dset) ;
1987 }
1988
1989 /*----------------------------------------------------------------------------*/
1990 /* Convert an index warp to a 3D dataset of index displacements. [05 Mar 2021]
1991 *//*--------------------------------------------------------------------------*/
1992
IW3D_to_index_dataset(IndexWarp3D * AA,char * prefix)1993 THD_3dim_dataset * IW3D_to_index_dataset( IndexWarp3D *AA , char *prefix )
1994 {
1995 THD_3dim_dataset *qset ;
1996 MRI_IMAGE *qim ; MRI_IMARR *imar ; float *qar ;
1997 int nx,ny,nz ; size_t nbytes ;
1998
1999 ENTRY("IW3D_to_index_dataset") ;
2000
2001 if( AA == NULL ) RETURN(NULL) ;
2002
2003 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ;
2004 nbytes = sizeof(float) * nx*ny*nz ;
2005
2006 INIT_IMARR(imar) ;
2007
2008 qim = mri_new_vol( nx,ny,nz , MRI_float ) ; qar = MRI_FLOAT_PTR(qim) ;
2009 memcpy( qar , AA->xd , nbytes ) ;
2010 ADDTO_IMARR( imar , qim ) ;
2011
2012 qim = mri_new_vol( nx,ny,nz , MRI_float ) ; qar = MRI_FLOAT_PTR(qim) ;
2013 memcpy( qar , AA->yd , nbytes ) ;
2014 ADDTO_IMARR( imar , qim ) ;
2015
2016 qim = mri_new_vol( nx,ny,nz , MRI_float ) ; qar = MRI_FLOAT_PTR(qim) ;
2017 memcpy( qar , AA->zd , nbytes ) ;
2018 ADDTO_IMARR( imar , qim ) ;
2019
2020 qset = THD_imarr_to_dataset( imar , prefix ) ;
2021
2022 DESTROY_IMARR(imar) ;
2023 RETURN(qset) ;
2024 }
2025
2026 #endif /*(C6)*/ /*############################################################*/
2027
2028 #if 1
2029 /*============================================================================*/
2030 /* (C7) Functions below compute auxiliary functions of an index warp,
2031 including stuff for the 3dQwarp penalties, and the 3dNwarpFuncs
2032 stuff (which are closely related to the 3dQwarp penalties). */
2033 /*============================================================================*/
2034
2035 /*===========================================================================*/
2036 /* The function below was put here for computation of the eigenvalues of the
2037 strain tensor, but wasn't actually needed -- since all that is needed
2038 is the sum of their squares, which is more easily computed as the trace
2039 of the square of the matrix. Left in here as a curiosity.
2040 (And remember Ziad's maxim: NEVER throw code away!)
2041 *//*-------------------------------------------------------------------------*/
2042 #if 0
2043 /*---------------------------------------------------------------------------*/
2044 /* Return the 3 eigenvalues of a 3x3 symmetric matrix.
2045 - Input matrix is [ a[0] a[1] a[2] ]
2046 [ a[1] a[3] a[4] ]
2047 [ a[2] a[4] a[5] ]
2048 - Method is direct solution of cubic characteristic equation
2049 - Output eigenvalues are not sorted
2050 -----------------------------------------------------------------------------*/
2051
2052 static INLINE double_triple eigval_sym3x3( double *a )
2053 {
2054 double aa,bb,cc,dd,ee,ff ;
2055 double a1,a2,a3 , qq,rr, qs,th ;
2056 double aba,abb,abc,abd,abe,abf , ann,anni ;
2057 double_triple eee={0.0,0.0,0.0} ;
2058
2059 if( a == NULL ) return eee ;
2060
2061 /*----- unload matrix into local variables -----*/
2062
2063 aa = a[0] ; bb = a[1] ; cc = a[2] ; /* matrix is [ aa bb cc ] */
2064 dd = a[3] ; ee = a[4] ; ff = a[5] ; /* [ bb dd ee ] */
2065 /* [ cc ee ff ] */
2066 aba = fabs(aa) ; abb = fabs(bb) ; abc = fabs(cc) ;
2067 abd = fabs(dd) ; abe = fabs(ee) ; abf = fabs(ff) ;
2068 ann = aba+abb+abc+abd+abe+abf ; /* matrix 'norm' */
2069
2070 if( ann == 0.0 ) return eee ; /* matrix is all zero! */
2071
2072 /*----- check for matrix that is essentially diagonal -----*/
2073
2074 #undef EPS
2075 #define EPS 1.e-8
2076
2077 if( abb+abc+abe == 0.0 ||
2078 ( EPS*aba > (abb+abc) && EPS*abd > (abb+abe) && EPS*abf > (abc+abe) ) ){
2079
2080 eee.a = aa ; eee.b = dd ; eee.c = ff ; return eee ;
2081 }
2082
2083 /*-- Scale matrix so abs sum is 1; unscale e[i] on output --*/
2084
2085 anni = 1.0 / ann ; /* ann != 0, from above */
2086 aa *= anni ; bb *= anni ; cc *= anni ;
2087 dd *= anni ; ee *= anni ; ff *= anni ;
2088
2089 /*----- not diagonal ==> must solve cubic polynomial for eigenvalues -----*/
2090 /* the cubic polynomial is x**3 + a1*x**2 + a2*x + a3 = 0 */
2091
2092 a1 = -(aa+dd+ff) ;
2093 a2 = (aa*ff+aa*dd+dd*ff - bb*bb-cc*cc-ee*ee) ;
2094 a3 = ( aa*(ee*ee-dd*ff) + bb*(bb*ff-cc*ee) + cc*(cc*dd-bb*ee) ) ;
2095
2096 /*-- Rewrite classical formula for qq as a sum of squares --*/
2097 /*-- [to ensure that it will not be negative by roundoff] --*/
2098 #if 0
2099 qq = (a1*a1 - 3.0*a2) / 9.0 ; /* classical formula */
2100 #else
2101 qq = ( 0.5 * ( SQR(dd-aa) + SQR(ff-aa) + SQR(ff-dd) )
2102 + 3.0 * ( bb*bb + cc*cc + ee*ee ) ) / 9.0 ;
2103 #endif
2104 rr = (2.0*a1*a1*a1 - 9.0*a1*a2 + 27.0*a3) / 54.0 ;
2105
2106 if( qq <= 0.0 ){ /*** This should never happen!!! ***/
2107 qs = qq = rr = 0.0 ;
2108 } else {
2109 qs = sqrt(qq) ; rr = rr / (qs*qq) ; qs *= 2.0 ;
2110 if( rr < -1.0 ) rr = -1.0 ; else if( rr > 1.0 ) rr = 1.0 ;
2111 }
2112 th = acos(rr) ; a1 /= 3.0 ;
2113
2114 eee.a = -ann * ( qs * cos( th /3.0 ) + a1 ) ;
2115 eee.b = -ann * ( qs * cos( (th+2.0*PI)/3.0 ) + a1 ) ;
2116 eee.c = -ann * ( qs * cos( (th+4.0*PI)/3.0 ) + a1 ) ;
2117
2118 return eee ;
2119 }
2120 #endif
2121
2122 /*----------------------------------------------------------------------------*/
2123 /* macros to ease the computation of volume of a hexahedron (distorted cube) */
2124
2125 #undef DA
2126 #undef DB
2127 #undef DC
2128 #define DA(p,q) (p.a-q.a) /* difference in a direction */
2129 #define DB(p,q) (p.b-q.b) /* difference in b direction */
2130 #define DC(p,q) (p.c-q.c) /* difference in c direction */
2131
2132 #undef TRIPROD /* triple product */
2133 #define TRIPROD(ax,ay,az,bx,by,bz,cx,cy,cz) ( (ax)*((by)*(cz)-(bz)*(cy)) \
2134 +(bx)*((cy)*(az)-(cz)*(ay)) \
2135 +(cx)*((ay)*(bz)-(az)*(by)) )
2136 /* 1D index from 3D indexes */
2137 #undef IJK
2138 #define IJK(i,j,k) ((i)+(j)*nx+(k)*nxy)
2139
2140 /* set components of the xx triple */
2141 #undef C2F
2142 #define C2F(p,q,r,xx) ( (xx).a = (p) , (xx).b = (q) , (xx).c = (r) )
2143
2144 /* add xyz displacements into abc components of xx triple */
2145 #undef D2F
2146 #define D2F(pqr,xx) ( (xx).a+=xda[pqr], (xx).b+=yda[pqr], (xx).c+=zda[pqr] )
2147
2148 /* load xyz displacements into abc components of xx triple */
2149 #undef E2F
2150 #define E2F(pqr,xx) ( (xx).a =xda[pqr], (xx).b =yda[pqr], (xx).c =zda[pqr] )
2151
2152 /*----------------------------------------------------------------------------*/
2153 /* Compute the bulk and shear energies from DISPLACEMENTS at corners.
2154 Loosely based on http://en.wikipedia.org/wiki/Neo-Hookean_solid.
2155 d000 is the 3D displacement at corner 000, etc.
2156 where corner lmn (each index l,m,n is 0 or 1) refers to the
2157 corner offset by l in the x direction,
2158 by m in the y direction,
2159 and by n in the z direction, from the 000 corner.
2160 This function is used in computation of the 3dQwarp penalty;
2161 see function IW3D_load_energy() below.
2162 *//*--------------------------------------------------------------------------*/
2163
hexahedron_energy(float_triple d000,float_triple d100,float_triple d010,float_triple d110,float_triple d001,float_triple d101,float_triple d011,float_triple d111)2164 static INLINE float_pair hexahedron_energy( float_triple d000, float_triple d100,
2165 float_triple d010, float_triple d110,
2166 float_triple d001, float_triple d101,
2167 float_triple d011, float_triple d111 )
2168 {
2169 float fxx,fxy,fxz, fyx,fyy,fyz, fzx,fzy,fzz ;
2170 float II , JJ , VV , jcb ;
2171 float_pair en ;
2172
2173 /* Load 3x3 strain matrix (differential displacements);
2174 the 0.5 factor take the average of 2 first differences
2175 relative to the oppposite (000 and 111) corners of
2176 the hexahedron; for example,
2177 fxy = derivative of y displacement with respect to x,
2178 computed by averaging 1st differences in the
2179 x direction at the 000 corner = DB(d100,d000)
2180 and at the 111 corner = DB(d111,d011)
2181 since the differences in the corner indexes in the
2182 DB() macro are in the first index = x direction code.
2183 Note that this matrix will not necessarily be symmetric!! */
2184
2185 fxx = ( DA(d100,d000) + DA(d111,d011) ) * 0.5f + 1.0f ;
2186 fxy = ( DB(d100,d000) + DB(d111,d011) ) * 0.5f ;
2187 fxz = ( DC(d100,d000) + DC(d111,d011) ) * 0.5f ;
2188
2189 fyx = ( DA(d010,d000) + DA(d111,d101) ) * 0.5f ;
2190 fyy = ( DB(d010,d000) + DB(d111,d101) ) * 0.5f + 1.0f ;
2191 fyz = ( DC(d010,d000) + DC(d111,d101) ) * 0.5f ;
2192
2193 fzx = ( DA(d001,d000) + DA(d111,d110) ) * 0.5f ;
2194 fzy = ( DB(d001,d000) + DB(d111,d110) ) * 0.5f ;
2195 fzz = ( DC(d001,d000) + DC(d111,d110) ) * 0.5f + 1.0f ;
2196
2197 /* determinant = bulk volume (1==unchanged) */
2198
2199 JJ = TRIPROD( fxx,fxy,fxz, fyx,fyy,fyz, fzx,fzy,fzz ) ;
2200 if( JJ < 0.1f ) JJ = 0.1f ; else if( JJ > 10.0f ) JJ = 10.0f ; /* limits */
2201
2202 /* trace of matrix square = sum of eigenvalue squares of matrix,
2203 which in turn gives the shear energy of the displacement tensor */
2204
2205 II = ( fxx*fxx + fyy*fyy + fzz*fzz
2206 + fxy*fxy + fyx*fyx + fxz*fxz
2207 + fzx*fzx + fyz*fyz + fzy*fzy ) ;
2208
2209 /* "vorticity" penalty added in, for fun (to avoid warp "twistiness") */
2210
2211 fxx = fyz - fzy ; fyy = fxz - fzx ; fzz = fxy - fyx ;
2212 VV = 2.0f*( fxx*fxx + fyy*fyy + fzz*fzz ) ;
2213
2214 jcb = cbrtf(JJ*JJ) ; /* J^(2/3) */
2215
2216 /* sum of shear and vorticity penalties, scaled to be unitless */
2217
2218 II = (II + VV) / jcb - 3.0f ; if( II < 0.0f ) II = 0.0f ;
2219
2220 /* compute energies (penalties) into the two
2221 components of the output -- will be summed later */
2222
2223 jcb = (JJ-1.0f/JJ) ; en.a = 0.333f*jcb*jcb ; en.b = II ;
2224 return en ;
2225 }
2226
2227 /*----------------------------------------------------------------------------*/
2228 /* Load the deformation energies for all voxels, using hexahedron_energy();
2229 return value is the sum of all the energies (AKA the warp penalty).
2230 There are two optional deformation energy arrays stored inside an
2231 index warp:
2232 je = Jacobian energy = energy of bulk compression or dilation
2233 se = Shear energy = energy of shearing and twisting
2234 The penalty for 3dQwarp is computed from each voxel energy E by
2235 max(je-1,0)^4 + max(se-1,0)^4
2236 In this way, small deformations are penalty-free, and then voxel-wise
2237 energies over 1 become heavily penalized. Search below for the "HPEN_"
2238 variables and "Hpen_" functions to see exactly how these values are used.
2239 *//*--------------------------------------------------------------------------*/
2240
2241 static float Hpen_cut = 1.0f; /* voxel-wise penalties below this are excluded */
2242
IW3D_load_energy(IndexWarp3D * AA)2243 float IW3D_load_energy( IndexWarp3D *AA )
2244 {
2245 float enout=0.0f ;
2246 float *xda, *yda , *zda , *jea,*sea , jetop,setop ;
2247 int nx,ny,nz , nxy,nxyz , ii ;
2248
2249 ENTRY("IW3D_load_energy") ;
2250
2251 if( AA == NULL ) RETURN(enout) ;
2252
2253 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
2254
2255 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ; /* displacement arrays */
2256
2257 STATUS("get/create je/se arrays") ;
2258 jea = AA->je; if( jea == NULL ) jea = AA->je = (float *)calloc(nxyz,sizeof(float));
2259 sea = AA->se; if( sea == NULL ) sea = AA->se = (float *)calloc(nxyz,sizeof(float));
2260
2261 STATUS("set dhhar -> 0") ;
2262 AAmemset( dhaar , 0 , sizeof(double)*nthmax ) ; /* initialize per thread sums */
2263
2264 STATUS("start the work (=energy; get it?)") ;
2265 AFNI_OMP_START ;
2266 #pragma omp parallel
2267 { float_triple x0,x1,x2,x3,x4,x5,x6,x7 ; float_pair en ;
2268 int ii,jj,kk , ip,jp,kp , ijk , qq , ith=0 ; float esum=0.0f, ev ;
2269 #pragma omp for
2270 for( qq=0 ; qq < nxyz ; qq++ ){
2271 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
2272 ip = ii+1 ; jp = jj+1 ; kp = kk+1 ;
2273 if( ip == nx ) ip-- ; if( jp == ny ) jp-- ; if( kp == nz ) kp-- ;
2274 ijk = IJK(ip,jj,kk) ; E2F(ijk,x1) ; /* load displacements at */
2275 ijk = IJK(ii,jp,kk) ; E2F(ijk,x2) ; /* each of the 8 corners */
2276 ijk = IJK(ip,jp,kk) ; E2F(ijk,x3) ;
2277 ijk = IJK(ii,jj,kp) ; E2F(ijk,x4) ;
2278 ijk = IJK(ip,jj,kp) ; E2F(ijk,x5) ;
2279 ijk = IJK(ii,jp,kp) ; E2F(ijk,x6) ;
2280 ijk = IJK(ip,jp,kp) ; E2F(ijk,x7) ;
2281 ijk = qq ; E2F(ijk,x0) ; /* the 000 corner */
2282 en = hexahedron_energy(x0,x1,x2,x3,x4,x5,x6,x7); jea[qq] = en.a; sea[qq] = en.b;
2283 ev = jea[qq]-Hpen_cut ; if( ev > 0.0f ) esum += (ev*ev)*(ev*ev) ;
2284 ev = sea[qq]-Hpen_cut ; if( ev > 0.0f ) esum += (ev*ev)*(ev*ev) ;
2285 }
2286 #ifdef USE_OMP
2287 ith = omp_get_thread_num() ;
2288 #endif
2289 dhaar[ith] = (double)esum ; /* per thread save of sum */
2290 }
2291 AFNI_OMP_END ;
2292 STATUS("work is done") ;
2293
2294 for( ii=0 ; ii < nthmax ; ii++ ) enout += dhaar[ii] ; /* final summation */
2295 RETURN(enout) ;
2296 }
2297
2298 /*----------------------------------------------------------------------------*/
2299 /* Compute the bulk, shear, and vorticity deformations, from DISPLACEMENTS
2300 at corners, for one voxel.
2301 *//*--------------------------------------------------------------------------*/
2302
hexahedron_bsv(float dx,float dy,float dz,float_triple d000,float_triple d100,float_triple d010,float_triple d110,float_triple d001,float_triple d101,float_triple d011,float_triple d111)2303 static INLINE float_triple hexahedron_bsv( float dx, float dy, float dz ,
2304 float_triple d000 , float_triple d100 ,
2305 float_triple d010 , float_triple d110 ,
2306 float_triple d001 , float_triple d101 ,
2307 float_triple d011 , float_triple d111 )
2308 {
2309 float fxx,fxy,fxz, fyx,fyy,fyz, fzx,fzy,fzz , jcb ;
2310 float_triple bsv ;
2311
2312 if( dx <= 0.0f ) dx = 1.0f ;
2313 if( dy <= 0.0f ) dy = 1.0f ;
2314 if( dz <= 0.0f ) dz = 1.0f ;
2315
2316 /* load strain matrix */
2317
2318 fxx = ( DA(d100,d000) + DA(d111,d011) ) * 0.5f + 1.0f ;
2319 fxy = ( DB(d100,d000) + DB(d111,d011) ) * 0.5f * (dy/dx) ;
2320 fxz = ( DC(d100,d000) + DC(d111,d011) ) * 0.5f * (dz/dx) ;
2321
2322 fyx = ( DA(d010,d000) + DA(d111,d101) ) * 0.5f * (dx/dy) ;
2323 fyy = ( DB(d010,d000) + DB(d111,d101) ) * 0.5f + 1.0f ;
2324 fyz = ( DC(d010,d000) + DC(d111,d101) ) * 0.5f * (dz/dy) ;
2325
2326 fzx = ( DA(d001,d000) + DA(d111,d110) ) * 0.5f * (dx/dz) ;
2327 fzy = ( DB(d001,d000) + DB(d111,d110) ) * 0.5f * (dy/dz) ;
2328 fzz = ( DC(d001,d000) + DC(d111,d110) ) * 0.5f + 1.0f ;
2329
2330 /* determinant = bulk volume (1=unchanged) */
2331
2332 jcb = TRIPROD( fxx,fxy,fxz, fyx,fyy,fyz, fzx,fzy,fzz ) ;
2333 bsv.a = jcb - 1.0f ;
2334 jcb = cbrtf(jcb*jcb) ;
2335
2336 /* trace of matrix square = shear energy */
2337
2338 bsv.b = ( fxx*fxx + fyy*fyy + fzz*fzz
2339 + fxy*fxy + fyx*fyx + fxz*fxz
2340 + fzx*fzx + fyz*fyz + fzy*fzy ) / jcb - 3.0f ;
2341 if( bsv.b < 0.0f ) bsv.b = 0.0f ;
2342
2343 /* "vorticity" energy */
2344
2345 fxx = fyz - fzy ; fyy = fxz - fzx ; fzz = fxy - fyx ;
2346 bsv.c = ( fxx*fxx + fyy*fyy + fzz*fzz ) / jcb ;
2347
2348 return bsv ;
2349 }
2350
2351 /*----------------------------------------------------------------------------*/
2352 /* Load the deformation values for all voxels [26 Jul 2013] */
2353
IW3D_load_bsv(IndexWarp3D * AA,float dx,float dy,float dz,float * bb,float * ss,float * vv)2354 void IW3D_load_bsv( IndexWarp3D *AA , float dx,float dy,float dz ,
2355 float *bb , float *ss , float *vv )
2356 {
2357 float enout=0.0f ;
2358 float *xda, *yda , *zda , *jea,*sea , jetop,setop ;
2359 int nx,ny,nz , nxy,nxyz , ii ;
2360
2361 ENTRY("IW3D_load_bsv") ;
2362
2363 if( AA == NULL ) EXRETURN ;
2364 if( bb == NULL && ss == NULL && vv == NULL ) EXRETURN ;
2365
2366 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
2367
2368 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
2369
2370 AFNI_OMP_START ;
2371 #pragma omp parallel
2372 { float_triple x0,x1,x2,x3,x4,x5,x6,x7 ; float_triple bsv ;
2373 int ii,jj,kk , ip,jp,kp , ijk , qq ; float esum=0.0f, ev ;
2374 #pragma omp for
2375 for( qq=0 ; qq < nxyz ; qq++ ){
2376 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
2377 ip = ii+1 ; jp = jj+1 ; kp = kk+1 ;
2378 if( ip == nx ) ip-- ; if( jp == ny ) jp-- ; if( kp == nz ) kp-- ;
2379 ijk = IJK(ip,jj,kk) ; E2F(ijk,x1) ;
2380 ijk = IJK(ii,jp,kk) ; E2F(ijk,x2) ;
2381 ijk = IJK(ip,jp,kk) ; E2F(ijk,x3) ;
2382 ijk = IJK(ii,jj,kp) ; E2F(ijk,x4) ;
2383 ijk = IJK(ip,jj,kp) ; E2F(ijk,x5) ;
2384 ijk = IJK(ii,jp,kp) ; E2F(ijk,x6) ;
2385 ijk = IJK(ip,jp,kp) ; E2F(ijk,x7) ;
2386 ijk = qq ; E2F(ijk,x0) ;
2387 bsv = hexahedron_bsv(dx,dy,dz,x0,x1,x2,x3,x4,x5,x6,x7);
2388 if( bb != NULL ) bb[qq] = bsv.a ;
2389 if( ss != NULL ) ss[qq] = bsv.b ;
2390 if( vv != NULL ) vv[qq] = bsv.c ;
2391 }
2392 }
2393 AFNI_OMP_END ;
2394
2395 EXRETURN ;
2396 }
2397
2398 /*----------------------------------------------------------------------------*/
2399 /* sum up the warp penalties */
2400
2401 #ifdef USE_OMP
HPEN_addup(int njs,float * je,float * se)2402 double HPEN_addup( int njs , float *je , float *se ) /* parallelized version */
2403 {
2404 double esum ; int qq ; /* these are function global variables */
2405
2406 AAmemset( dhaar , 0 , sizeof(double)*nthmax ) ;
2407 #pragma omp parallel
2408 { int ii , ith ; double ev , dh=0.0 ; /* these are per-thread variables */
2409 #pragma omp for
2410 for( ii=0 ; ii < njs ; ii++ ){
2411 ev = je[ii]-Hpen_cut ; if( ev > 0.0 ) dh += (ev*ev)*(ev*ev) ;
2412 ev = se[ii]-Hpen_cut ; if( ev > 0.0 ) dh += (ev*ev)*(ev*ev) ;
2413 }
2414 ith = omp_get_thread_num() ; dhaar[ith] = dh ; /* dhaar = temp array */
2415 }
2416
2417 for( esum=0.0,qq=0 ; qq < nthmax ; qq++ ) esum += dhaar[qq] ;
2418 return esum ;
2419 }
2420
2421 #else /*---------------------------------------------------------------------*/
2422
HPEN_addup(int njs,float * je,float * se)2423 double HPEN_addup( int njs , float *je , float *se ) /* simpler version */
2424 {
2425 int ii ; double ev , esum=0.0 ;
2426 for( ii=0 ; ii < njs ; ii++ ){
2427 ev = je[ii]-Hpen_cut ; if( ev > 0.0 ) esum += (ev*ev)*(ev*ev) ;
2428 ev = se[ii]-Hpen_cut ; if( ev > 0.0 ) esum += (ev*ev)*(ev*ev) ;
2429 }
2430 return esum ;
2431 }
2432 #endif
2433
2434 /*----------------------------------------------------------------------------*/
2435 #ifndef HAVE_HEXVOL /* this function exists in some other places in AFNI-land */
2436 #define HAVE_HEXVOL
2437 /*----------------------------------------------------------------------------*/
2438 /* Volume of a hexahedron (distorted cube) given by 8 corners.
2439 Looking down from the top, the bottom plane points are numbered so:
2440 2 -- 3
2441 | | and the top plane is similar (add 4 to each index),
2442 0 -- 1 with point #(i+4) 'above' point #i.
2443 *//*--------------------------------------------------------------------------*/
2444
hexahedron_volume(float_triple x0,float_triple x1,float_triple x2,float_triple x3,float_triple x4,float_triple x5,float_triple x6,float_triple x7)2445 static INLINE float hexahedron_volume( float_triple x0 , float_triple x1 ,
2446 float_triple x2 , float_triple x3 ,
2447 float_triple x4 , float_triple x5 ,
2448 float_triple x6 , float_triple x7 )
2449 {
2450 float xa,ya,za , xb,yb,zb , xc,yc,zc , vol ;
2451
2452 xa = DA(x7,x1)+DA(x6,x0); ya = DB(x7,x1)+DB(x6,x0); za = DC(x7,x1)+DC(x6,x0);
2453 xb = DA(x7,x2) ; yb = DB(x7,x2) ; zb = DC(x7,x2) ;
2454 xc = DA(x3,x0) ; yc = DB(x3,x0) ; zc = DC(x3,x0) ;
2455 vol = TRIPROD(xa,ya,za,xb,yb,zb,xc,yc,zc) ;
2456 xa = DA(x6,x0) ; ya = DB(x6,x0) ; za = DC(x6,x0) ;
2457 xb = DA(x7,x2)+DA(x5,x0); yb = DB(x7,x2)+DB(x5,x0); zb = DC(x7,x2)+DC(x5,x0);
2458 xc = DA(x7,x4) ; yc = DB(x7,x4) ; zc = DC(x7,x4) ;
2459 vol += TRIPROD(xa,ya,za,xb,yb,zb,xc,yc,zc) ;
2460 xa = DA(x7,x1) ; ya = DB(x7,x1) ; za = DC(x7,x1) ;
2461 xb = DA(x5,x0) ; yb = DB(x5,x0) ; zb = DC(x5,x0) ;
2462 xc = DA(x7,x4)+DA(x3,x0); yc = DB(x7,x4)+DB(x3,x0); zc = DC(x7,x4)+DC(x3,x0);
2463 vol += TRIPROD(xa,ya,za,xb,yb,zb,xc,yc,zc) ;
2464 return (0.08333333f*vol) ;
2465 }
2466 #endif /* HAVE_HEXVOL */
2467
2468 #undef TRIPROD
2469 #undef DA
2470 #undef DB
2471 #undef DC
2472
2473 /*---------------------------------------------------------------------------*/
2474 /* Load the volumes of each hexahedral element in the displaced grid.
2475 An undistorted voxel will get volume 1, since AA is a unitless warp. */
2476
IW3D_load_hexvol(IndexWarp3D * AA,float * hv)2477 float IW3D_load_hexvol( IndexWarp3D *AA , float *hv )
2478 {
2479 float *xda, *yda , *zda , *hva , top,bot ;
2480 int nx,ny,nz , nxy,nxyz , ii ;
2481 float hvm = 0.0f ;
2482
2483 ENTRY("IW3D_load_hexvol") ;
2484
2485 if( AA == NULL ) RETURN(hvm) ;
2486
2487 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
2488
2489 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
2490
2491 hva = hv ;
2492 if( hva == NULL ){
2493 hva = AA->hv ;
2494 if( hva == NULL ) hva = AA->hv = (float *)calloc(nxyz,sizeof(float)) ;
2495 }
2496
2497 AFNI_OMP_START ;
2498 #pragma omp parallel
2499 { float_triple x0,x1,x2,x3,x4,x5,x6,x7 ;
2500 int ii,jj,kk , ip,jp,kp , ijk , qq ;
2501 #pragma omp for
2502 for( qq=0 ; qq < nxyz ; qq++ ){
2503 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
2504 ip = ii+1 ; jp = jj+1 ; kp = kk+1 ;
2505 C2F(ii,jj,kk,x0); C2F(ip,jj,kk,x1); C2F(ii,jp,kk,x2); C2F(ip,jp,kk,x3);
2506 C2F(ii,jj,kp,x4); C2F(ip,jj,kp,x5); C2F(ii,jp,kp,x6); C2F(ip,jp,kp,x7);
2507 if( ip == nx ) ip-- ; if( jp == ny ) jp-- ; if( kp == nz ) kp-- ;
2508 ijk = IJK(ip,jj,kk) ; D2F(ijk,x1) ;
2509 ijk = IJK(ii,jp,kk) ; D2F(ijk,x2) ;
2510 ijk = IJK(ip,jp,kk) ; D2F(ijk,x3) ;
2511 ijk = IJK(ii,jj,kp) ; D2F(ijk,x4) ;
2512 ijk = IJK(ip,jj,kp) ; D2F(ijk,x5) ;
2513 ijk = IJK(ii,jp,kp) ; D2F(ijk,x6) ;
2514 ijk = IJK(ip,jp,kp) ; D2F(ijk,x7) ;
2515 ijk = qq ; D2F(ijk,x0) ;
2516 hva[qq] = hexahedron_volume(x0,x1,x2,x3,x4,x5,x6,x7) ;
2517 }
2518 }
2519 AFNI_OMP_END ;
2520
2521 RETURN(hvm) ;
2522 }
2523
2524 #if 0
2525 /*---------------------------------------------------------------------------*/
2526 /* Load hexahedron volumes over just PART of the warp (never used). */
2527 void IW3D_load_hexvol_box( IndexWarp3D *AA ,
2528 int ibot,int itop, int jbot,int jtop, int kbot,int ktop )
2529 {
2530 float *xda, *yda , *zda , *hva , top,bot ;
2531 int nx,ny,nz , nxy , ii , nbx,nby,nbz,nbxy,nbxyz ;
2532
2533 if( AA == NULL ) return ;
2534
2535 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ;
2536
2537 nbx = (itop-ibot+1) ;
2538 nby = (jtop-jbot+1) ; nbxy = nbx *nby ;
2539 nbz = (ktop-kbot+1) ; nbxyz = nbxy*nbz ;
2540
2541 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
2542
2543 hva = AA->hv ;
2544 if( hva == NULL ) hva = AA->hv = (float *)calloc(nxy*nz,sizeof(float)) ;
2545
2546 AFNI_OMP_START ;
2547 #pragma omp parallel
2548 { float_triple x0,x1,x2,x3,x4,x5,x6,x7 ;
2549 int ii,jj,kk , ip,jp,kp , ijk , qq ;
2550 #pragma omp for
2551 for( qq=0 ; qq < nbxyz ; qq++ ){
2552 ii = qq * nbx ; kk = qq / nbxy ; jj = (qq-kk*nbxy) / nbx ;
2553 ii += ibot ; jj += jbot ; kk += kbot ;
2554 ip = ii+1 ; jp = jj+1 ; kp = kk+1 ;
2555 C2F(ii,jj,kk,x0); C2F(ip,jj,kk,x1); C2F(ii,jp,kk,x2); C2F(ip,jp,kk,x3);
2556 C2F(ii,jj,kp,x4); C2F(ip,jj,kp,x5); C2F(ii,jp,kp,x6); C2F(ip,jp,kp,x7);
2557 if( ip == nx ) ip-- ; if( jp == ny ) jp-- ; if( kp == nz ) kp-- ;
2558 ijk = IJK(ip,jj,kk) ; D2F(ijk,x1) ;
2559 ijk = IJK(ii,jp,kk) ; D2F(ijk,x2) ;
2560 ijk = IJK(ip,jp,kk) ; D2F(ijk,x3) ;
2561 ijk = IJK(ii,jj,kp) ; D2F(ijk,x4) ;
2562 ijk = IJK(ip,jj,kp) ; D2F(ijk,x5) ;
2563 ijk = IJK(ii,jp,kp) ; D2F(ijk,x6) ;
2564 ijk = IJK(ip,jp,kp) ; D2F(ijk,x7) ;
2565 ijk = IJK(ii,jj,kk) ; D2F(ijk,x0) ;
2566 hva[ijk] = hexahedron_volume(x0,x1,x2,x3,x4,x5,x6,x7) ;
2567 }
2568 } /* end of parallel code */
2569 AFNI_OMP_END ;
2570
2571 return ;
2572 }
2573 #endif
2574
2575 #undef C2F
2576 #undef D2F
2577 #undef E2F
2578
2579 #endif /*(C7)*/ /*############################################################*/
2580
2581 #if 1
2582 /*============================================================================*/
2583 /* (C8) The following functions are for interpolating all 3 components of an
2584 index warp at one time, and are recklessly adapted from mri_genalign_util.c;
2585 however, since Zhark wrote those, Zhark can steal them (right?). */
2586 /*============================================================================*/
2587
2588 #undef CLIP
2589 #define CLIP(mm,nn) if((mm) < 0)(mm)=0; else if((mm) > (nn))(mm)=(nn)
2590
2591 #undef QLIP
2592 #define QLIP(mm,nn) if( (mm) > (nn) ) (mm)=(nn)
2593
2594 #undef AJK
2595 #define AJK(aaa,j,k) ((aaa)+(j)*nx+(k)*nxy)
2596
2597 /*---------------------------------------------------------------------------*/
2598 /*! Interpolate displacements using linear method.
2599 nxx, nyy, nzz = grid dimensions of displacment arrays
2600 aar, bar, car = displacement arrays
2601 use_es = flag to use external slope array
2602 esar = external slope array
2603 npp = number of points at which to interpolate
2604 ip, jp, kp = float indexes at which to interpolate
2605 uar, var, war = output arrays [npp]
2606 *//*-------------------------------------------------------------------------*/
2607
IW3D_interp_linear(int nxx,int nyy,int nzz,float * aar,float * bar,float * car,int use_es,float * esar,int npp,float * ip,float * jp,float * kp,float * uar,float * var,float * war)2608 void IW3D_interp_linear( int nxx , int nyy , int nzz ,
2609 float *aar , float *bar , float *car ,
2610 int use_es , float *esar ,
2611 int npp, float *ip, float *jp, float *kp,
2612 float *uar , float *var , float *war )
2613 {
2614 /** note all variables are thread local (except function args) **/
2615 AFNI_OMP_START ;
2616 #pragma omp parallel if( npp > 1111 )
2617 {
2618 int nx=nxx, ny=nyy, nz=nzz, nxy=nx*ny, pp ;
2619 int nx1=nx-1,ny1=ny-1,nz1=nz-1 ;
2620 int nx2=nx-2,ny2=ny-2,nz2=nz-2 ;
2621 float fx,fy,fz , xx,yy,zz ;
2622 int ix_00,ix_p1 , jy_00,jy_p1 , kz_00,kz_p1 , ix,jy,kz ;
2623 float wt_00,wt_p1 ;
2624 float f_j00_k00, f_jp1_k00, f_j00_kp1, f_jp1_kp1, f_k00, f_kp1 ;
2625 float g_j00_k00, g_jp1_k00, g_j00_kp1, g_jp1_kp1, g_k00, g_kp1 ;
2626 float h_j00_k00, h_jp1_k00, h_j00_kp1, h_jp1_kp1, h_k00, h_kp1 ;
2627
2628 int ues=use_es ;
2629 ES_DECLARE_FLOATS ; float uex=0.0f,vex=0.0f,wex=0.0f ;
2630 float eex=0.0f,eey=0.0f,eez=0.0f , Exx=0.0f,Exy=0.0f,Exz=0.0f ,
2631 Eyx=0.0f,Eyy=0.0f,Eyz=0.0f , Ezx=0.0f,Ezy=0.0f,Ezz=0.0f ;
2632
2633 /* if using external slopes, load local variables with these slopes */
2634
2635 if( ues ) ES_UNPACKVEC(esar) ;
2636
2637 #pragma omp for
2638 for( pp=0 ; pp < npp ; pp++ ){ /* loop over output points */
2639 xx = ip[pp] ; yy = jp[pp] ; zz = kp[pp] ;
2640 if( !ues ){ /* not using external slopes */
2641 if( xx < 0.0f ){ ix = 0 ; fx = 0.0f ; } /* just truncate */
2642 else if( xx < nx1 ){ ix = (int)xx ; fx = xx-ix; } /* if out of range */
2643 else { ix = nx2 ; fx = 1.0f ; }
2644 if( yy < 0.0f ){ jy = 0 ; fy = 0.0f ; }
2645 else if( yy < ny1 ){ jy = (int)yy ; fy = yy-jy; }
2646 else { jy = ny2 ; fy = 1.0f ; }
2647 if( zz < 0.0f ){ kz = 0 ; fz = 0.0f ; }
2648 else if( zz < nz1 ){ kz = (int)zz ; fz = zz-kz; }
2649 else { kz = nz2 ; fz = 1.0f ; }
2650 } else { /* using external slopes */
2651 int aem=0 ; /* flag for if external slopes are needed */
2652 if( xx < 0.0f ){ eex = xx ; ix = 0 ; fx = 0.0f; aem++; Exx=es_xd_xm; Eyx=es_yd_xm; Ezx=es_zd_xm; }
2653 else if( xx < nx1 ){ eex = 0.0f ; ix = (int)xx; fx = xx-ix; }
2654 else { eex = xx-nx1; ix = nx2 ; fx = 1.0f; aem++; Exx=es_xd_xp; Eyx=es_yd_xp; Ezx=es_zd_xp; }
2655 if( yy < 0.0f ){ eey = yy ; jy = 0 ; fy = 0.0f; aem++; Exy=es_xd_ym; Eyy=es_yd_ym; Ezy=es_zd_ym; }
2656 else if( yy < ny1 ){ eey = 0.0f ; jy = (int)yy; fy = yy-jy; }
2657 else { eey = yy-ny1; jy = ny2 ; fy = 1.0f; aem++; Exy=es_xd_yp; Eyy=es_yd_yp; Ezy=es_zd_yp; }
2658 if( zz < 0.0f ){ eez = zz ; kz = 0 ; fz = 0.0f; aem++; Exz=es_xd_zm; Eyz=es_yd_zm; Ezz=es_zd_zm; }
2659 else if( zz < nz1 ){ eez = 0.0f ; kz = (int)zz; fz = zz-kz; }
2660 else { eez = zz-nz1; kz = nz2 ; fz = 1.0f; aem++; Exz=es_xd_zp; Eyz=es_yd_zp; Ezz=es_zd_zp; }
2661 if( aem ){
2662 uex = Exx*eex + Exy*eey + Exz*eez ; /* eex = how far past edge of warp, in x-direction (etc.) */
2663 vex = Eyx*eex + Eyy*eey + Eyz*eez ; /* Eab = external slope for 'a' displacement, */
2664 wex = Ezx*eex + Ezy*eey + Ezz*eez ; /* along the 'b' direction, for a and b = x or y or z */
2665 } else {
2666 uex = vex = wex = 0.0f ;
2667 }
2668 }
2669
2670 /* special case of no interpolation needed */
2671
2672 if( fabsf(fx)+fabsf(fy)+fabsf(fz) < 0.00222f ){ /* 06 Nov 2014 */
2673 uar[pp] = aar[IJK(ix,jy,kz)] + uex ;
2674 var[pp] = bar[IJK(ix,jy,kz)] + vex ;
2675 war[pp] = car[IJK(ix,jy,kz)] + wex ;
2676 continue ;
2677 }
2678
2679 ix_00 = ix ; ix_p1 = ix_00+1 ; /* at this point, we are 'fx' between indexes ix_00 and ix_p1 */
2680 jy_00 = jy ; jy_p1 = jy_00+1 ;
2681 kz_00 = kz ; kz_p1 = kz_00+1 ;
2682
2683 wt_00 = 1.0f-fx ; wt_p1 = fx ; /* weights for ix_00 and ix_p1 points */
2684
2685 #undef XINT /* linear interpolation macro in array 'aaa' at plane jk */
2686 #define XINT(aaa,j,k) wt_00*aaa[IJK(ix_00,j,k)]+wt_p1*aaa[IJK(ix_p1,j,k)]
2687
2688 /* interpolate to location ix+fx at each jy,kz level */
2689
2690 f_j00_k00 = XINT(aar,jy_00,kz_00) ; f_jp1_k00 = XINT(aar,jy_p1,kz_00) ;
2691 f_j00_kp1 = XINT(aar,jy_00,kz_p1) ; f_jp1_kp1 = XINT(aar,jy_p1,kz_p1) ;
2692 g_j00_k00 = XINT(bar,jy_00,kz_00) ; g_jp1_k00 = XINT(bar,jy_p1,kz_00) ;
2693 g_j00_kp1 = XINT(bar,jy_00,kz_p1) ; g_jp1_kp1 = XINT(bar,jy_p1,kz_p1) ;
2694 h_j00_k00 = XINT(car,jy_00,kz_00) ; h_jp1_k00 = XINT(car,jy_p1,kz_00) ;
2695 h_j00_kp1 = XINT(car,jy_00,kz_p1) ; h_jp1_kp1 = XINT(car,jy_p1,kz_p1) ;
2696
2697 /* interpolate to jy+fy at each kz level */
2698
2699 wt_00 = 1.0f-fy ; wt_p1 = fy ;
2700 f_k00 = wt_00 * f_j00_k00 + wt_p1 * f_jp1_k00 ;
2701 f_kp1 = wt_00 * f_j00_kp1 + wt_p1 * f_jp1_kp1 ;
2702 g_k00 = wt_00 * g_j00_k00 + wt_p1 * g_jp1_k00 ;
2703 g_kp1 = wt_00 * g_j00_kp1 + wt_p1 * g_jp1_kp1 ;
2704 h_k00 = wt_00 * h_j00_k00 + wt_p1 * h_jp1_k00 ;
2705 h_kp1 = wt_00 * h_j00_kp1 + wt_p1 * h_jp1_kp1 ;
2706
2707 /* interpolate to kz+fz to get output */
2708
2709 uar[pp] = (1.0f-fz) * f_k00 + fz * f_kp1 + uex ; /* note add-in of values */
2710 var[pp] = (1.0f-fz) * g_k00 + fz * g_kp1 + vex ; /* from external slope */
2711 war[pp] = (1.0f-fz) * h_k00 + fz * h_kp1 + wex ; /* calculation above */
2712 }
2713
2714 } /* end OpenMP */
2715 AFNI_OMP_END ;
2716
2717 return ;
2718 }
2719
2720 /*---------------------------------------------------------------------------*/
2721 /* Interpolation with weighted (tapered) sinc in 3D.
2722 ++ Taper function wtap(r) is defined to be 1 for 0 <= r <= WCUT
2723 and for WCUT < r < 1 is a raised cosine dropping down to wtap(r=1) = 0.
2724 This choice was made to keep the variance smoothing artifact low.
2725 ++ Radius of sinc window is WRAD, so the actual taper used is wtap(x/WRAD)
2726 *//*-------------------------------------------------------------------------*/
2727
2728 #undef WCUT
2729 #define WCUT 0.1f /* cutoff point for taper */
2730
2731 #undef WRAD
2732 #define WRAD 5.0001f /* width of sinc interpolation (float) */
2733
2734 #undef IRAD
2735 #define IRAD 5 /* width of sinc interpolation (int) */
2736
2737 #undef PIF
2738 #define PIF 3.1415927f /* PI in float */
2739
2740 /* sinc function = sin(PI*x)/(PI*x) -- x is always >= 0 */
2741
2742 #undef sinc
2743 #define sinc(x) ( (x>0.01f) ? sinf(PIF*(x))/(PIF*(x)) \
2744 : 1.0f - 1.6449341f*(x)*(x) )
2745
2746 /* Weight (taper) function, declining from wtap(WCUT)=1 to wtap(1)=0 */
2747 /* Note that the input to wtap will always be between WCUT and 1. */
2748
2749 #undef wtap
2750 #define wtap(x) ( 0.53836f+0.46164f*cosf(PIF*((x)-WCUT)/(1.0f-WCUT)) )
2751
2752 #undef AW
2753 #undef BW
2754 #undef CW
2755 #define AW(i) aarjk[iqq[i]]*wtt[i] /* access macros */
2756 #define BW(i) barjk[iqq[i]]*wtt[i] /* with weighting */
2757 #define CW(i) carjk[iqq[i]]*wtt[i]
2758
2759 /*---------------------------------------------------------------------------*/
2760 /*! Interpolate using wsinc5 method (slow and accurate);
2761 for usage and comments, see the _linear function above.
2762 *//*-------------------------------------------------------------------------*/
2763
IW3D_interp_wsinc5(int nxx,int nyy,int nzz,float * aar,float * bar,float * car,int use_es,float * esar,int npp,float * ip,float * jp,float * kp,float * uar,float * var,float * war)2764 void IW3D_interp_wsinc5( int nxx , int nyy , int nzz ,
2765 float *aar , float *bar , float *car ,
2766 int use_es , float *esar ,
2767 int npp, float *ip, float *jp, float *kp,
2768 float *uar , float *var , float *war )
2769 {
2770 ENTRY("IW3D_interp_wsinc5") ;
2771 AFNI_OMP_START ;
2772 #pragma omp parallel if( npp > 333 )
2773 {
2774 int nx=nxx, ny=nyy, nz=nzz, nxy=nx*ny, nxyz=nxy*nz,nxyz1=nxyz-1, pp, ix,jy,kz ;
2775 float xx,yy,zz , fx,fy,fz ;
2776 float *aarjk , *barjk , *carjk ;
2777 int nx1=nx-1,ny1=ny-1,nz1=nz-1 ;
2778 int nx2=nx-2,ny2=ny-2,nz2=nz-2 ;
2779
2780 float xw,yw,zw,rr , asum,bsum,csum,wsum,wfac,wt ;
2781 int iq,jq,kq,iqp , qq,jj,kk , ddi,ddj,ddk ;
2782 float xsin[2*IRAD] , ysin[2*IRAD] , zsin[2*IRAD] ;
2783 float wtt[2*IRAD] , ajk[2*IRAD][2*IRAD] , ak[2*IRAD] ;
2784 float bjk[2*IRAD][2*IRAD] , bk[2*IRAD] ;
2785 float cjk[2*IRAD][2*IRAD] , ck[2*IRAD] ;
2786 int iqq[2*IRAD] ;
2787
2788 int ues=use_es , outside ;
2789 ES_DECLARE_FLOATS ; float uex=0.0f,vex=0.0f,wex=0.0f ;
2790 float eex=0.0f,eey=0.0f,eez=0.0f , Exx=0.0f,Exy=0.0f,Exz=0.0f ,
2791 Eyx=0.0f,Eyy=0.0f,Eyz=0.0f , Ezx=0.0f,Ezy=0.0f,Ezz=0.0f ;
2792
2793 if( ues ) ES_UNPACKVEC(esar) ;
2794
2795 /*----- loop over points -----*/
2796
2797 #pragma omp for
2798 for( pp=0 ; pp < npp ; pp++ ){
2799 xx = ip[pp] ; yy = jp[pp] ; zz = kp[pp] ;
2800 if( !ues ){
2801 if( xx < 0.0f ){ ix = 0 ; fx = 0.0f ; outside = 1 ; }
2802 else if( xx < nx1 ){ ix = (int)xx ; fx = xx-ix; outside = 0 ; }
2803 else { ix = nx2 ; fx = 1.0f ; outside = 1 ; }
2804 if( yy < 0.0f ){ jy = 0 ; fy = 0.0f ; outside = 1 ; }
2805 else if( yy < ny1 ){ jy = (int)yy ; fy = yy-jy; outside = 0 ; }
2806 else { jy = ny2 ; fy = 1.0f ; outside = 1 ; }
2807 if( zz < 0.0f ){ kz = 0 ; fz = 0.0f ; outside = 1 ; }
2808 else if( zz < nz1 ){ kz = (int)zz ; fz = zz-kz; outside = 0 ; }
2809 else { kz = nz2 ; fz = 1.0f ; outside = 1 ; }
2810 } else {
2811 int aem=0 ;
2812 if( xx < 0.0f ){ eex = xx ; ix = 0 ; fx = 0.0f; aem++; Exx=es_xd_xm; Eyx=es_yd_xm; Ezx=es_zd_xm; }
2813 else if( xx < nx1 ){ eex = 0.0f ; ix = (int)xx; fx = xx-ix; }
2814 else { eex = xx-nx1; ix = nx2 ; fx = 1.0f; aem++; Exx=es_xd_xp; Eyx=es_yd_xp; Ezx=es_zd_xp; }
2815 if( yy < 0.0f ){ eey = yy ; jy = 0 ; fy = 0.0f; aem++; Exy=es_xd_ym; Eyy=es_yd_ym; Ezy=es_zd_ym; }
2816 else if( yy < ny1 ){ eey = 0.0f ; jy = (int)yy; fy = yy-jy; }
2817 else { eey = yy-ny1; jy = ny2 ; fy = 1.0f; aem++; Exy=es_xd_yp; Eyy=es_yd_yp; Ezy=es_zd_yp; }
2818 if( zz < 0.0f ){ eez = zz ; kz = 0 ; fz = 0.0f; aem++; Exz=es_xd_zm; Eyz=es_yd_zm; Ezz=es_zd_zm; }
2819 else if( zz < nz1 ){ eez = 0.0f ; kz = (int)zz; fz = zz-kz; }
2820 else { eez = zz-nz1; kz = nz2 ; fz = 1.0f; aem++; Exz=es_xd_zp; Eyz=es_yd_zp; Ezz=es_zd_zp; }
2821 if( aem ){
2822 uex = Exx*eex + Exy*eey + Exz*eez ; /* linear extrapolation outside box */
2823 vex = Eyx*eex + Eyy*eey + Eyz*eez ;
2824 wex = Ezx*eex + Ezy*eey + Ezz*eez ;
2825 } else {
2826 uex = vex = wex = 0.0f ;
2827 }
2828 }
2829
2830 /* special case of no interpolation needed */
2831
2832 if( fabsf(fx)+fabsf(fy)+fabsf(fz) < 0.00222f ){ /* 06 Nov 2014 */
2833 uar[pp] = aar[IJK(ix,jy,kz)] + uex ;
2834 var[pp] = bar[IJK(ix,jy,kz)] + vex ;
2835 war[pp] = car[IJK(ix,jy,kz)] + wex ;
2836 continue ;
2837 }
2838
2839 #if 0 /** this is the old (pre external slopes) method **/
2840 if( outside ){ /* use value at nearest edge point */
2841 qq = ix + jy*nx + kz*nxy ; CLIP(qq,nxyz1) ;
2842 uar[pp] = aar[qq] ; var[pp] = bar[qq] ; war[pp] = car[qq] ;
2843 continue ;
2844 }
2845 #endif
2846
2847 /*- x interpolations -*/
2848
2849 for( wsum=0.0f,qq=-IRAD+1 ; qq <= IRAD ; qq++ ){ /* get weights */
2850 xw = fabsf(fx - qq) ; wt = sinc(xw) ;
2851 xw /= WRAD ; if( xw > WCUT ) wt *= wtap(xw) ;
2852 wtt[qq+(IRAD-1)] = wt ; wsum += wt ;
2853 iq = ix+qq ; CLIP(iq,nx1) ; iqq[qq+(IRAD-1)] = iq ;
2854 }
2855 wfac = wsum ;
2856
2857 for( jj=-IRAD+1 ; jj <= IRAD ; jj++ ){ /* do interps for */
2858 jq = jy+jj ; CLIP(jq,ny1) ; /* each component */
2859 for( kk=-IRAD+1 ; kk <= IRAD ; kk++ ){
2860 kq = kz+kk ; CLIP(kq,nz1) ;
2861 aarjk = AJK(aar,jq,kq) ;
2862 barjk = AJK(bar,jq,kq) ;
2863 carjk = AJK(car,jq,kq) ;
2864 ajk[jj+(IRAD-1)][kk+(IRAD-1)] =
2865 AW(0)+AW(1)+AW(2)+AW(3)+AW(4)+AW(5)+AW(6)+AW(7)+AW(8)+AW(9) ;
2866 bjk[jj+(IRAD-1)][kk+(IRAD-1)] =
2867 BW(0)+BW(1)+BW(2)+BW(3)+BW(4)+BW(5)+BW(6)+BW(7)+BW(8)+BW(9) ;
2868 cjk[jj+(IRAD-1)][kk+(IRAD-1)] =
2869 CW(0)+CW(1)+CW(2)+CW(3)+CW(4)+CW(5)+CW(6)+CW(7)+CW(8)+CW(9) ;
2870 }
2871 }
2872
2873 /*- y interpolations -*/
2874
2875 for( wsum=0.0f,qq=-IRAD+1 ; qq <= IRAD ; qq++ ){ /* get weights */
2876 yw = fabsf(fy - qq) ; wt = sinc(yw) ;
2877 yw /= WRAD ; if( yw > WCUT ) wt *= wtap(yw) ;
2878 wtt[qq+(IRAD-1)] = wt ; wsum += wt ;
2879 }
2880 wfac *= wsum ;
2881
2882 for( kk=-IRAD+1 ; kk <= IRAD ; kk++ ){ /* interps */
2883 for( asum=bsum=csum=0.0f,jj=-IRAD+1 ; jj < IRAD ; jj+=2 ){ /* unrolled by 2 */
2884 asum += wtt[jj+(IRAD-1)]*ajk[jj+(IRAD-1)][kk+(IRAD-1)]
2885 +wtt[jj+ IRAD ]*ajk[jj+ IRAD ][kk+(IRAD-1)] ;
2886 bsum += wtt[jj+(IRAD-1)]*bjk[jj+(IRAD-1)][kk+(IRAD-1)]
2887 +wtt[jj+ IRAD ]*bjk[jj+ IRAD ][kk+(IRAD-1)] ;
2888 csum += wtt[jj+(IRAD-1)]*cjk[jj+(IRAD-1)][kk+(IRAD-1)]
2889 +wtt[jj+ IRAD ]*cjk[jj+ IRAD ][kk+(IRAD-1)] ;
2890 }
2891 ak[kk+(IRAD-1)] = asum ;
2892 bk[kk+(IRAD-1)] = bsum ;
2893 ck[kk+(IRAD-1)] = csum ;
2894 }
2895
2896 /*- z interpolation -*/
2897
2898 for( wsum=0.0f,qq=-IRAD+1 ; qq <= IRAD ; qq++ ){ /* get weights */
2899 zw = fabsf(fz - qq) ; wt = sinc(zw) ;
2900 zw /= WRAD ; if( zw > WCUT ) wt *= wtap(zw) ;
2901 wtt[qq+(IRAD-1)] = wt ; wsum += wt ;
2902 }
2903 wfac *= wsum ;
2904
2905 /* interps */
2906
2907 for( asum=bsum=csum=0.0f,kk=-IRAD+1 ; kk < IRAD ; kk+=2 ){ /* unrolled by 2 */
2908 asum += wtt[kk+(IRAD-1)] * ak[kk+(IRAD-1)] + wtt[kk+IRAD] * ak[kk+IRAD] ;
2909 bsum += wtt[kk+(IRAD-1)] * bk[kk+(IRAD-1)] + wtt[kk+IRAD] * bk[kk+IRAD] ;
2910 csum += wtt[kk+(IRAD-1)] * ck[kk+(IRAD-1)] + wtt[kk+IRAD] * ck[kk+IRAD] ;
2911 }
2912
2913 uar[pp] = asum / wfac + uex ;
2914 var[pp] = bsum / wfac + vex ;
2915 war[pp] = csum / wfac + wex ;
2916
2917 } /* whew! */
2918
2919 } /* end OpenMP */
2920 AFNI_OMP_END ;
2921
2922 EXRETURN ;
2923 }
2924
2925 /*---------------------------------------------------------------------------*/
2926 /* define quintic interpolation polynomials (Lagrange) */
2927
2928 #undef Q_M2
2929 #undef Q_M1
2930 #undef Q_00
2931 #undef Q_P1
2932 #undef Q_P2
2933 #undef Q_P3
2934 #define Q_M2(x) (x*(x*x-1.0f)*(2.0f-x)*(x-3.0f)*0.008333333f)
2935 #define Q_M1(x) (x*(x*x-4.0f)*(x-1.0f)*(x-3.0f)*0.041666667f)
2936 #define Q_00(x) ((x*x-4.0f)*(x*x-1.0f)*(3.0f-x)*0.083333333f)
2937 #define Q_P1(x) (x*(x*x-4.0f)*(x+1.0f)*(x-3.0f)*0.083333333f)
2938 #define Q_P2(x) (x*(x*x-1.0f)*(x+2.0f)*(3.0f-x)*0.041666667f)
2939 #define Q_P3(x) (x*(x*x-1.0f)*(x*x-4.0f)*0.008333333f)
2940
2941 /*---------------------------------------------------------------------------*/
2942 /*! Interpolate using quintic method -- see _linear function for relevant
2943 comments; this is between linear and wsinc5 in accuracy and speed.
2944 *//*-------------------------------------------------------------------------*/
2945
IW3D_interp_quintic(int nxx,int nyy,int nzz,float * aar,float * bar,float * car,int use_es,float * esar,int npp,float * ip,float * jp,float * kp,float * uar,float * var,float * war)2946 void IW3D_interp_quintic( int nxx , int nyy , int nzz ,
2947 float *aar , float *bar , float *car ,
2948 int use_es , float *esar ,
2949 int npp, float *ip, float *jp, float *kp,
2950 float *uar , float *var , float *war )
2951 {
2952 AFNI_OMP_START ;
2953 #pragma omp parallel if( npp > 666 )
2954 {
2955 int nx=nxx , ny=nyy , nz=nzz , nxy=nx*ny , pp ;
2956 float xx,yy,zz , fx,fy,fz ;
2957 int nx1=nx-1,ny1=ny-1,nz1=nz-1, ix,jy,kz ;
2958 int nx2=nx-2,ny2=ny-2,nz2=nz-2 ;
2959 int ix_m2,ix_m1,ix_00,ix_p1,ix_p2,ix_p3 ; /* interpolation indices */
2960 int jy_m2,jy_m1,jy_00,jy_p1,jy_p2,jy_p3 ; /* (input image) */
2961 int kz_m2,kz_m1,kz_00,kz_p1,kz_p2,kz_p3 ;
2962
2963 float wt_m2,wt_m1,wt_00,wt_p1,wt_p2,wt_p3 ; /* interpolation weights */
2964
2965 float f_jm2_km2, f_jm1_km2, f_j00_km2, f_jp1_km2, f_jp2_km2, f_jp3_km2,
2966 f_jm2_km1, f_jm1_km1, f_j00_km1, f_jp1_km1, f_jp2_km1, f_jp3_km1,
2967 f_jm2_k00, f_jm1_k00, f_j00_k00, f_jp1_k00, f_jp2_k00, f_jp3_k00,
2968 f_jm2_kp1, f_jm1_kp1, f_j00_kp1, f_jp1_kp1, f_jp2_kp1, f_jp3_kp1,
2969 f_jm2_kp2, f_jm1_kp2, f_j00_kp2, f_jp1_kp2, f_jp2_kp2, f_jp3_kp2,
2970 f_jm2_kp3, f_jm1_kp3, f_j00_kp3, f_jp1_kp3, f_jp2_kp3, f_jp3_kp3,
2971 f_km2 , f_km1 , f_k00 , f_kp1 , f_kp2 , f_kp3 ;
2972 float g_jm2_km2, g_jm1_km2, g_j00_km2, g_jp1_km2, g_jp2_km2, g_jp3_km2,
2973 g_jm2_km1, g_jm1_km1, g_j00_km1, g_jp1_km1, g_jp2_km1, g_jp3_km1,
2974 g_jm2_k00, g_jm1_k00, g_j00_k00, g_jp1_k00, g_jp2_k00, g_jp3_k00,
2975 g_jm2_kp1, g_jm1_kp1, g_j00_kp1, g_jp1_kp1, g_jp2_kp1, g_jp3_kp1,
2976 g_jm2_kp2, g_jm1_kp2, g_j00_kp2, g_jp1_kp2, g_jp2_kp2, g_jp3_kp2,
2977 g_jm2_kp3, g_jm1_kp3, g_j00_kp3, g_jp1_kp3, g_jp2_kp3, g_jp3_kp3,
2978 g_km2 , g_km1 , g_k00 , g_kp1 , g_kp2 , g_kp3 ;
2979 float h_jm2_km2, h_jm1_km2, h_j00_km2, h_jp1_km2, h_jp2_km2, h_jp3_km2,
2980 h_jm2_km1, h_jm1_km1, h_j00_km1, h_jp1_km1, h_jp2_km1, h_jp3_km1,
2981 h_jm2_k00, h_jm1_k00, h_j00_k00, h_jp1_k00, h_jp2_k00, h_jp3_k00,
2982 h_jm2_kp1, h_jm1_kp1, h_j00_kp1, h_jp1_kp1, h_jp2_kp1, h_jp3_kp1,
2983 h_jm2_kp2, h_jm1_kp2, h_j00_kp2, h_jp1_kp2, h_jp2_kp2, h_jp3_kp2,
2984 h_jm2_kp3, h_jm1_kp3, h_j00_kp3, h_jp1_kp3, h_jp2_kp3, h_jp3_kp3,
2985 h_km2 , h_km1 , h_k00 , h_kp1 , h_kp2 , h_kp3 ;
2986
2987 int ues=use_es ;
2988 ES_DECLARE_FLOATS ; float uex=0.0f,vex=0.0f,wex=0.0f ;
2989 float eex=0.0f,eey=0.0f,eez=0.0f , Exx=0.0f,Exy=0.0f,Exz=0.0f ,
2990 Eyx=0.0f,Eyy=0.0f,Eyz=0.0f , Ezx=0.0f,Ezy=0.0f,Ezz=0.0f ;
2991
2992 if( ues ) ES_UNPACKVEC(esar) ;
2993
2994 #pragma omp for
2995 for( pp=0 ; pp < npp ; pp++ ){
2996 xx = ip[pp] ; yy = jp[pp] ; zz = kp[pp] ;
2997 if( !ues ){
2998 if( xx < 0.0f ){ ix = 0 ; fx = 0.0f ; }
2999 else if( xx < nx1 ){ ix = (int)xx ; fx = xx-ix; }
3000 else { ix = nx2 ; fx = 1.0f ; }
3001 if( yy < 0.0f ){ jy = 0 ; fy = 0.0f ; }
3002 else if( yy < ny1 ){ jy = (int)yy ; fy = yy-jy; }
3003 else { jy = ny2 ; fy = 1.0f ; }
3004 if( zz < 0.0f ){ kz = 0 ; fz = 0.0f ; }
3005 else if( zz < nz1 ){ kz = (int)zz ; fz = zz-kz; }
3006 else { kz = nz2 ; fz = 1.0f ; }
3007 } else {
3008 int aem=0 ;
3009 if( xx < 0.0f ){ eex = xx ; ix = 0 ; fx = 0.0f; aem++; Exx=es_xd_xm; Eyx=es_yd_xm; Ezx=es_zd_xm; }
3010 else if( xx < nx1 ){ eex = 0.0f ; ix = (int)xx; fx = xx-ix; }
3011 else { eex = xx-nx1; ix = nx2 ; fx = 1.0f; aem++; Exx=es_xd_xp; Eyx=es_yd_xp; Ezx=es_zd_xp; }
3012 if( yy < 0.0f ){ eey = yy ; jy = 0 ; fy = 0.0f; aem++; Exy=es_xd_ym; Eyy=es_yd_ym; Ezy=es_zd_ym; }
3013 else if( yy < ny1 ){ eey = 0.0f ; jy = (int)yy; fy = yy-jy; }
3014 else { eey = yy-ny1; jy = ny2 ; fy = 1.0f; aem++; Exy=es_xd_yp; Eyy=es_yd_yp; Ezy=es_zd_yp; }
3015 if( zz < 0.0f ){ eez = zz ; kz = 0 ; fz = 0.0f; aem++; Exz=es_xd_zm; Eyz=es_yd_zm; Ezz=es_zd_zm; }
3016 else if( zz < nz1 ){ eez = 0.0f ; kz = (int)zz; fz = zz-kz; }
3017 else { eez = zz-nz1; kz = nz2 ; fz = 1.0f; aem++; Exz=es_xd_zp; Eyz=es_yd_zp; Ezz=es_zd_zp; }
3018 if( aem ){
3019 uex = Exx*eex + Exy*eey + Exz*eez ;
3020 vex = Eyx*eex + Eyy*eey + Eyz*eez ;
3021 wex = Ezx*eex + Ezy*eey + Ezz*eez ;
3022 } else {
3023 uex = vex = wex = 0.0f ;
3024 }
3025 }
3026
3027 /* special case of no interpolation needed */
3028
3029 if( fabsf(fx)+fabsf(fy)+fabsf(fz) < 0.00222f ){ /* 06 Nov 2014 */
3030 uar[pp] = aar[IJK(ix,jy,kz)] + uex ;
3031 var[pp] = bar[IJK(ix,jy,kz)] + vex ;
3032 war[pp] = car[IJK(ix,jy,kz)] + wex ;
3033 continue ;
3034 }
3035
3036 /* compute indexes from which to interpolate (-2,-1,0,+1,+2,+3),
3037 but clipped to lie inside input image volume */
3038
3039 ix_m1 = ix-1 ; ix_00 = ix ; ix_p1 = ix+1 ; ix_p2 = ix+2 ;
3040 CLIP(ix_m1,nx1) ; CLIP(ix_00,nx1) ; CLIP(ix_p1,nx1) ; CLIP(ix_p2,nx1) ;
3041 ix_m2 = ix-2 ; ix_p3 = ix+3 ;
3042 CLIP(ix_m2,nx1) ; CLIP(ix_p3,nx1) ;
3043
3044 jy_m1 = jy-1 ; jy_00 = jy ; jy_p1 = jy+1 ; jy_p2 = jy+2 ;
3045 CLIP(jy_m1,ny1) ; CLIP(jy_00,ny1) ; CLIP(jy_p1,ny1) ; CLIP(jy_p2,ny1) ;
3046 jy_m2 = jy-2 ; jy_p3 = jy+3 ;
3047 CLIP(jy_m2,ny1) ; CLIP(jy_p3,ny1) ;
3048
3049 kz_m1 = kz-1 ; kz_00 = kz ; kz_p1 = kz+1 ; kz_p2 = kz+2 ;
3050 CLIP(kz_m1,nz1) ; CLIP(kz_00,nz1) ; CLIP(kz_p1,nz1) ; CLIP(kz_p2,nz1) ;
3051 kz_m2 = kz-2 ; kz_p3 = kz+3 ;
3052 CLIP(kz_m2,nz1) ; CLIP(kz_p3,nz1) ;
3053
3054 wt_m1 = Q_M1(fx) ; wt_00 = Q_00(fx) ; /* interpolation weights */
3055 wt_p1 = Q_P1(fx) ; wt_p2 = Q_P2(fx) ; /* in x-direction */
3056 wt_m2 = Q_M2(fx) ; wt_p3 = Q_P3(fx) ;
3057
3058 #undef XINT /* quintic interpolation in the x direction in array aaa */
3059 #define XINT(aaa,j,k) wt_m2*aaa[IJK(ix_m2,j,k)]+wt_m1*aaa[IJK(ix_m1,j,k)] \
3060 +wt_00*aaa[IJK(ix_00,j,k)]+wt_p1*aaa[IJK(ix_p1,j,k)] \
3061 +wt_p2*aaa[IJK(ix_p2,j,k)]+wt_p3*aaa[IJK(ix_p3,j,k)]
3062
3063 /* interpolate to location ix+fx at each jy,kz level */
3064
3065 f_jm2_km2 = XINT(aar,jy_m2,kz_m2) ; f_jm1_km2 = XINT(aar,jy_m1,kz_m2) ;
3066 f_j00_km2 = XINT(aar,jy_00,kz_m2) ; f_jp1_km2 = XINT(aar,jy_p1,kz_m2) ;
3067 f_jp2_km2 = XINT(aar,jy_p2,kz_m2) ; f_jp3_km2 = XINT(aar,jy_p3,kz_m2) ;
3068 f_jm2_km1 = XINT(aar,jy_m2,kz_m1) ; f_jm1_km1 = XINT(aar,jy_m1,kz_m1) ;
3069 f_j00_km1 = XINT(aar,jy_00,kz_m1) ; f_jp1_km1 = XINT(aar,jy_p1,kz_m1) ;
3070 f_jp2_km1 = XINT(aar,jy_p2,kz_m1) ; f_jp3_km1 = XINT(aar,jy_p3,kz_m1) ;
3071 f_jm2_k00 = XINT(aar,jy_m2,kz_00) ; f_jm1_k00 = XINT(aar,jy_m1,kz_00) ;
3072 f_j00_k00 = XINT(aar,jy_00,kz_00) ; f_jp1_k00 = XINT(aar,jy_p1,kz_00) ;
3073 f_jp2_k00 = XINT(aar,jy_p2,kz_00) ; f_jp3_k00 = XINT(aar,jy_p3,kz_00) ;
3074 f_jm2_kp1 = XINT(aar,jy_m2,kz_p1) ; f_jm1_kp1 = XINT(aar,jy_m1,kz_p1) ;
3075 f_j00_kp1 = XINT(aar,jy_00,kz_p1) ; f_jp1_kp1 = XINT(aar,jy_p1,kz_p1) ;
3076 f_jp2_kp1 = XINT(aar,jy_p2,kz_p1) ; f_jp3_kp1 = XINT(aar,jy_p3,kz_p1) ;
3077 f_jm2_kp2 = XINT(aar,jy_m2,kz_p2) ; f_jm1_kp2 = XINT(aar,jy_m1,kz_p2) ;
3078 f_j00_kp2 = XINT(aar,jy_00,kz_p2) ; f_jp1_kp2 = XINT(aar,jy_p1,kz_p2) ;
3079 f_jp2_kp2 = XINT(aar,jy_p2,kz_p2) ; f_jp3_kp2 = XINT(aar,jy_p3,kz_p2) ;
3080 f_jm2_kp3 = XINT(aar,jy_m2,kz_p3) ; f_jm1_kp3 = XINT(aar,jy_m1,kz_p3) ;
3081 f_j00_kp3 = XINT(aar,jy_00,kz_p3) ; f_jp1_kp3 = XINT(aar,jy_p1,kz_p3) ;
3082 f_jp2_kp3 = XINT(aar,jy_p2,kz_p3) ; f_jp3_kp3 = XINT(aar,jy_p3,kz_p3) ;
3083
3084 g_jm2_km2 = XINT(bar,jy_m2,kz_m2) ; g_jm1_km2 = XINT(bar,jy_m1,kz_m2) ;
3085 g_j00_km2 = XINT(bar,jy_00,kz_m2) ; g_jp1_km2 = XINT(bar,jy_p1,kz_m2) ;
3086 g_jp2_km2 = XINT(bar,jy_p2,kz_m2) ; g_jp3_km2 = XINT(bar,jy_p3,kz_m2) ;
3087 g_jm2_km1 = XINT(bar,jy_m2,kz_m1) ; g_jm1_km1 = XINT(bar,jy_m1,kz_m1) ;
3088 g_j00_km1 = XINT(bar,jy_00,kz_m1) ; g_jp1_km1 = XINT(bar,jy_p1,kz_m1) ;
3089 g_jp2_km1 = XINT(bar,jy_p2,kz_m1) ; g_jp3_km1 = XINT(bar,jy_p3,kz_m1) ;
3090 g_jm2_k00 = XINT(bar,jy_m2,kz_00) ; g_jm1_k00 = XINT(bar,jy_m1,kz_00) ;
3091 g_j00_k00 = XINT(bar,jy_00,kz_00) ; g_jp1_k00 = XINT(bar,jy_p1,kz_00) ;
3092 g_jp2_k00 = XINT(bar,jy_p2,kz_00) ; g_jp3_k00 = XINT(bar,jy_p3,kz_00) ;
3093 g_jm2_kp1 = XINT(bar,jy_m2,kz_p1) ; g_jm1_kp1 = XINT(bar,jy_m1,kz_p1) ;
3094 g_j00_kp1 = XINT(bar,jy_00,kz_p1) ; g_jp1_kp1 = XINT(bar,jy_p1,kz_p1) ;
3095 g_jp2_kp1 = XINT(bar,jy_p2,kz_p1) ; g_jp3_kp1 = XINT(bar,jy_p3,kz_p1) ;
3096 g_jm2_kp2 = XINT(bar,jy_m2,kz_p2) ; g_jm1_kp2 = XINT(bar,jy_m1,kz_p2) ;
3097 g_j00_kp2 = XINT(bar,jy_00,kz_p2) ; g_jp1_kp2 = XINT(bar,jy_p1,kz_p2) ;
3098 g_jp2_kp2 = XINT(bar,jy_p2,kz_p2) ; g_jp3_kp2 = XINT(bar,jy_p3,kz_p2) ;
3099 g_jm2_kp3 = XINT(bar,jy_m2,kz_p3) ; g_jm1_kp3 = XINT(bar,jy_m1,kz_p3) ;
3100 g_j00_kp3 = XINT(bar,jy_00,kz_p3) ; g_jp1_kp3 = XINT(bar,jy_p1,kz_p3) ;
3101 g_jp2_kp3 = XINT(bar,jy_p2,kz_p3) ; g_jp3_kp3 = XINT(bar,jy_p3,kz_p3) ;
3102
3103 h_jm2_km2 = XINT(car,jy_m2,kz_m2) ; h_jm1_km2 = XINT(car,jy_m1,kz_m2) ;
3104 h_j00_km2 = XINT(car,jy_00,kz_m2) ; h_jp1_km2 = XINT(car,jy_p1,kz_m2) ;
3105 h_jp2_km2 = XINT(car,jy_p2,kz_m2) ; h_jp3_km2 = XINT(car,jy_p3,kz_m2) ;
3106 h_jm2_km1 = XINT(car,jy_m2,kz_m1) ; h_jm1_km1 = XINT(car,jy_m1,kz_m1) ;
3107 h_j00_km1 = XINT(car,jy_00,kz_m1) ; h_jp1_km1 = XINT(car,jy_p1,kz_m1) ;
3108 h_jp2_km1 = XINT(car,jy_p2,kz_m1) ; h_jp3_km1 = XINT(car,jy_p3,kz_m1) ;
3109 h_jm2_k00 = XINT(car,jy_m2,kz_00) ; h_jm1_k00 = XINT(car,jy_m1,kz_00) ;
3110 h_j00_k00 = XINT(car,jy_00,kz_00) ; h_jp1_k00 = XINT(car,jy_p1,kz_00) ;
3111 h_jp2_k00 = XINT(car,jy_p2,kz_00) ; h_jp3_k00 = XINT(car,jy_p3,kz_00) ;
3112 h_jm2_kp1 = XINT(car,jy_m2,kz_p1) ; h_jm1_kp1 = XINT(car,jy_m1,kz_p1) ;
3113 h_j00_kp1 = XINT(car,jy_00,kz_p1) ; h_jp1_kp1 = XINT(car,jy_p1,kz_p1) ;
3114 h_jp2_kp1 = XINT(car,jy_p2,kz_p1) ; h_jp3_kp1 = XINT(car,jy_p3,kz_p1) ;
3115 h_jm2_kp2 = XINT(car,jy_m2,kz_p2) ; h_jm1_kp2 = XINT(car,jy_m1,kz_p2) ;
3116 h_j00_kp2 = XINT(car,jy_00,kz_p2) ; h_jp1_kp2 = XINT(car,jy_p1,kz_p2) ;
3117 h_jp2_kp2 = XINT(car,jy_p2,kz_p2) ; h_jp3_kp2 = XINT(car,jy_p3,kz_p2) ;
3118 h_jm2_kp3 = XINT(car,jy_m2,kz_p3) ; h_jm1_kp3 = XINT(car,jy_m1,kz_p3) ;
3119 h_j00_kp3 = XINT(car,jy_00,kz_p3) ; h_jp1_kp3 = XINT(car,jy_p1,kz_p3) ;
3120 h_jp2_kp3 = XINT(car,jy_p2,kz_p3) ; h_jp3_kp3 = XINT(car,jy_p3,kz_p3) ;
3121
3122 /* interpolate to jy+fy at each kz level */
3123
3124 wt_m1 = Q_M1(fy) ; wt_00 = Q_00(fy) ; wt_p1 = Q_P1(fy) ;
3125 wt_p2 = Q_P2(fy) ; wt_m2 = Q_M2(fy) ; wt_p3 = Q_P3(fy) ;
3126
3127 f_km2 = wt_m2 * f_jm2_km2 + wt_m1 * f_jm1_km2 + wt_00 * f_j00_km2
3128 + wt_p1 * f_jp1_km2 + wt_p2 * f_jp2_km2 + wt_p3 * f_jp3_km2 ;
3129 f_km1 = wt_m2 * f_jm2_km1 + wt_m1 * f_jm1_km1 + wt_00 * f_j00_km1
3130 + wt_p1 * f_jp1_km1 + wt_p2 * f_jp2_km1 + wt_p3 * f_jp3_km1 ;
3131 f_k00 = wt_m2 * f_jm2_k00 + wt_m1 * f_jm1_k00 + wt_00 * f_j00_k00
3132 + wt_p1 * f_jp1_k00 + wt_p2 * f_jp2_k00 + wt_p3 * f_jp3_k00 ;
3133 f_kp1 = wt_m2 * f_jm2_kp1 + wt_m1 * f_jm1_kp1 + wt_00 * f_j00_kp1
3134 + wt_p1 * f_jp1_kp1 + wt_p2 * f_jp2_kp1 + wt_p3 * f_jp3_kp1 ;
3135 f_kp2 = wt_m2 * f_jm2_kp2 + wt_m1 * f_jm1_kp2 + wt_00 * f_j00_kp2
3136 + wt_p1 * f_jp1_kp2 + wt_p2 * f_jp2_kp2 + wt_p3 * f_jp3_kp2 ;
3137 f_kp3 = wt_m2 * f_jm2_kp3 + wt_m1 * f_jm1_kp3 + wt_00 * f_j00_kp3
3138 + wt_p1 * f_jp1_kp3 + wt_p2 * f_jp2_kp3 + wt_p3 * f_jp3_kp3 ;
3139
3140 g_km2 = wt_m2 * g_jm2_km2 + wt_m1 * g_jm1_km2 + wt_00 * g_j00_km2
3141 + wt_p1 * g_jp1_km2 + wt_p2 * g_jp2_km2 + wt_p3 * g_jp3_km2 ;
3142 g_km1 = wt_m2 * g_jm2_km1 + wt_m1 * g_jm1_km1 + wt_00 * g_j00_km1
3143 + wt_p1 * g_jp1_km1 + wt_p2 * g_jp2_km1 + wt_p3 * g_jp3_km1 ;
3144 g_k00 = wt_m2 * g_jm2_k00 + wt_m1 * g_jm1_k00 + wt_00 * g_j00_k00
3145 + wt_p1 * g_jp1_k00 + wt_p2 * g_jp2_k00 + wt_p3 * g_jp3_k00 ;
3146 g_kp1 = wt_m2 * g_jm2_kp1 + wt_m1 * g_jm1_kp1 + wt_00 * g_j00_kp1
3147 + wt_p1 * g_jp1_kp1 + wt_p2 * g_jp2_kp1 + wt_p3 * g_jp3_kp1 ;
3148 g_kp2 = wt_m2 * g_jm2_kp2 + wt_m1 * g_jm1_kp2 + wt_00 * g_j00_kp2
3149 + wt_p1 * g_jp1_kp2 + wt_p2 * g_jp2_kp2 + wt_p3 * g_jp3_kp2 ;
3150 g_kp3 = wt_m2 * g_jm2_kp3 + wt_m1 * g_jm1_kp3 + wt_00 * g_j00_kp3
3151 + wt_p1 * g_jp1_kp3 + wt_p2 * g_jp2_kp3 + wt_p3 * g_jp3_kp3 ;
3152
3153 h_km2 = wt_m2 * h_jm2_km2 + wt_m1 * h_jm1_km2 + wt_00 * h_j00_km2
3154 + wt_p1 * h_jp1_km2 + wt_p2 * h_jp2_km2 + wt_p3 * h_jp3_km2 ;
3155 h_km1 = wt_m2 * h_jm2_km1 + wt_m1 * h_jm1_km1 + wt_00 * h_j00_km1
3156 + wt_p1 * h_jp1_km1 + wt_p2 * h_jp2_km1 + wt_p3 * h_jp3_km1 ;
3157 h_k00 = wt_m2 * h_jm2_k00 + wt_m1 * h_jm1_k00 + wt_00 * h_j00_k00
3158 + wt_p1 * h_jp1_k00 + wt_p2 * h_jp2_k00 + wt_p3 * h_jp3_k00 ;
3159 h_kp1 = wt_m2 * h_jm2_kp1 + wt_m1 * h_jm1_kp1 + wt_00 * h_j00_kp1
3160 + wt_p1 * h_jp1_kp1 + wt_p2 * h_jp2_kp1 + wt_p3 * h_jp3_kp1 ;
3161 h_kp2 = wt_m2 * h_jm2_kp2 + wt_m1 * h_jm1_kp2 + wt_00 * h_j00_kp2
3162 + wt_p1 * h_jp1_kp2 + wt_p2 * h_jp2_kp2 + wt_p3 * h_jp3_kp2 ;
3163 h_kp3 = wt_m2 * h_jm2_kp3 + wt_m1 * h_jm1_kp3 + wt_00 * h_j00_kp3
3164 + wt_p1 * h_jp1_kp3 + wt_p2 * h_jp2_kp3 + wt_p3 * h_jp3_kp3 ;
3165
3166 /* interpolate to kz+fz to get output */
3167
3168 wt_m1 = Q_M1(fz) ; wt_00 = Q_00(fz) ; wt_p1 = Q_P1(fz) ;
3169 wt_p2 = Q_P2(fz) ; wt_m2 = Q_M2(fz) ; wt_p3 = Q_P3(fz) ;
3170
3171 uar[pp] = wt_m2 * f_km2 + wt_m1 * f_km1 + wt_00 * f_k00
3172 + wt_p1 * f_kp1 + wt_p2 * f_kp2 + wt_p3 * f_kp3 + uex ;
3173
3174 var[pp] = wt_m2 * g_km2 + wt_m1 * g_km1 + wt_00 * g_k00
3175 + wt_p1 * g_kp1 + wt_p2 * g_kp2 + wt_p3 * g_kp3 + vex ;
3176
3177 war[pp] = wt_m2 * h_km2 + wt_m1 * h_km1 + wt_00 * h_k00
3178 + wt_p1 * h_kp1 + wt_p2 * h_kp2 + wt_p3 * h_kp3 + wex ;
3179 }
3180
3181 } /* end OpenMP */
3182 AFNI_OMP_END ;
3183
3184 return ;
3185 }
3186
3187 /*---------------------------------------------------------------------------*/
3188 /* Generic interpolation of warp components, given icode specifying method. */
3189
IW3D_interp(int icode,int nxx,int nyy,int nzz,float * aar,float * bar,float * car,int use_es,float * esar,int npp,float * ip,float * jp,float * kp,float * uar,float * var,float * war)3190 void IW3D_interp( int icode ,
3191 int nxx , int nyy , int nzz ,
3192 float *aar , float *bar , float *car ,
3193 int use_es , float *esar ,
3194 int npp, float *ip, float *jp, float *kp,
3195 float *uar , float *var , float *war )
3196 {
3197 switch( icode ){
3198 case MRI_NN:
3199 case MRI_LINEAR:
3200 IW3D_interp_linear( nxx , nyy , nzz , aar , bar , car ,
3201 use_es , esar ,
3202 npp , ip , jp , kp , uar , var , war ) ;
3203 break ;
3204
3205 case MRI_CUBIC:
3206 case MRI_QUINTIC:
3207 IW3D_interp_quintic( nxx , nyy , nzz , aar , bar , car ,
3208 use_es , esar ,
3209 npp , ip , jp , kp , uar , var , war ) ;
3210 break ;
3211
3212 default:
3213 case MRI_WSINC5:
3214 IW3D_interp_wsinc5( nxx , nyy , nzz , aar , bar , car ,
3215 use_es , esar ,
3216 npp , ip , jp , kp , uar , var , war ) ;
3217 break ;
3218 }
3219
3220 return ;
3221 }
3222
3223 #endif /*(C8)*/ /*############################################################*/
3224
3225 #if 1
3226 /*============================================================================*/
3227 /* (C9) Functions below carry out warp compositions, */
3228 /* including where one of the 'warps' is just a matrix. */
3229 /*============================================================================*/
3230
3231 /*---------------------------------------------------------------------------*/
3232
3233 #undef NPER
3234 #define NPER 16777216 /* 64 Mbyte per temp float array */
3235
3236 /* determine size of a temp array for when we do things in segments */
3237
3238 #undef NALL
3239 #define NALL(nnn) (((nnn) > NPER+524288) ? NPER : (nnn))
3240
3241 /*---------------------------------------------------------------------------*/
3242 /* B(A(x)) where B = matrix, A = warp, icode = unused
3243 -- no interpolation is needed for this operation, since
3244 the matrix B can just be applied to the warp displacement from A
3245 -- 'w1m2' means 'warp #1, matrix #2' ?????
3246 *//*-------------------------------------------------------------------------*/
3247
IW3D_compose_w1m2(IndexWarp3D * AA,mat44 BB,int icode)3248 IndexWarp3D * IW3D_compose_w1m2( IndexWarp3D *AA , mat44 BB , int icode )
3249 {
3250 int nx,ny,nz,nxy,nxyz ;
3251 float *xda,*yda,*zda , *xdc,*ydc,*zdc ;
3252 IndexWarp3D *CC=NULL ;
3253 mat44 BI , BL ;
3254
3255 ENTRY("IW3D_compose_w1m2") ;
3256
3257 if( AA == NULL ) RETURN(CC) ;
3258
3259 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
3260
3261 BL = BB ; /* BL = local copy of B */
3262 BI = BL ; /* BI = B - I */
3263 BI.m[0][0] -= 1.0f ; BI.m[1][1] -= 1.0f ; BI.m[2][2] -= 1.0f ;
3264
3265 CC = IW3D_empty_copy(AA) ;
3266
3267 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
3268 xdc = CC->xd ; ydc = CC->yd ; zdc = CC->zd ;
3269
3270 /* In the following code:
3271 MAT33_VEC is used to apply the 3x3 matrix in BL to [xda,yda,zda],
3272 which ignores the shifts in the 4x4 matrix, but will take care
3273 of rotations (say)
3274 MAT44_VEC is used to apply the 4x4 matrix in BI = BL-I to the
3275 base location [ii,jj,kk], which will include the shifts and
3276 any bulk rotation of the grid; the identity matrix I is
3277 subtracted so that we just get displacements from [ii,jj,kk]
3278 I hope the above helps elucidate the slightly mysterious code below. */
3279
3280 AFNI_OMP_START ;
3281 #pragma omp parallel if( nxyz > 1111 )
3282 { int qq , ii,jj,kk ; float xb,yb,zb , xm,ym,zm ;
3283 #pragma omp for
3284 for( qq=0 ; qq < nxyz ; qq++ ){
3285 /* compute 3D indexes [ii,jj,kk] from 1D index qq */
3286 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3287 MAT33_VEC(BL,xda[qq],yda[qq],zda[qq],xb,yb,zb) ; /* B * dis(x) */
3288 MAT44_VEC(BI,ii ,jj ,kk ,xm,ym,zm) ; /* (B-I) * x */
3289 xdc[qq] = xb+xm ; ydc[qq] = yb+ym ; zdc[qq] = zb+zm ; /* add up */
3290 }
3291 }
3292 AFNI_OMP_END ;
3293
3294 RETURN(CC) ;
3295 }
3296
3297 /*---------------------------------------------------------------------------*/
3298 /* A(B(x)) where B = matrix, A = warp, icode = interpolation method;
3299 -- in this function, interplation IS necessary, since the
3300 displacement from B(x) will not be exactly on a grid point.
3301 -- 'm1w2' means 'matrix #1, warp #2' ?????
3302
3303 A(x) = x + delta(x)
3304 B(x) = B x [matrix multiply]
3305 A(B(x)) = Bx + delta(Bx) = x + (B-I)x + delta(Bx)
3306 So the displacements for A(B(x)) are (B-I)x + delta(Bx).
3307 In the loops below:
3308 Step 1 = compute Bx for each x=(ii,jj,kk)
3309 Step 2 = interpolate delta at the Bx locations = compute delta(Bx)
3310 Step 3 = add Bx-x to delta(Bx) to get the final displacments for m1w2
3311 *//*-------------------------------------------------------------------------*/
3312
IW3D_compose_m1w2(mat44 BB,IndexWarp3D * AA,int icode)3313 IndexWarp3D * IW3D_compose_m1w2( mat44 BB , IndexWarp3D *AA , int icode )
3314 {
3315 int nx,ny,nz,nxy,nxyz , nall , pp,qtop;
3316 float *xda,*yda,*zda , *xdc,*ydc,*zdc , *xq,*yq,*zq ;
3317 IndexWarp3D *CC=NULL ;
3318 mat44 BL ;
3319 float esar[18] ;
3320
3321 ENTRY("IW3D_compose_m1w2") ;
3322
3323 if( AA == NULL ) RETURN(CC) ;
3324
3325 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
3326
3327 BL = BB ; /* BL = local copy of B */
3328
3329 CC = IW3D_empty_copy(AA) ;
3330
3331 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
3332 xdc = CC->xd ; ydc = CC->yd ; zdc = CC->zd ;
3333
3334 nall = NALL(nxyz) ; /* allocation size for segments to work on */
3335
3336 xq = (float *)malloc(sizeof(float)*nall) ; /* indexes at which to */
3337 yq = (float *)malloc(sizeof(float)*nall) ; /* interpolate the AA warp */
3338 zq = (float *)malloc(sizeof(float)*nall) ;
3339
3340 for( pp=0 ; pp < nxyz ; pp+=nall ){ /* loop over segments */
3341
3342 qtop = MIN( nxyz , pp+nall ) ; /* process points from pp to qtop-1 */
3343
3344 /* step 1: Transform the [ii,jj,kk] index by the matrix;
3345 these are the Bx locations needed for the next step,
3346 stored in the temp arrays xq[], yq[], and zq[]. */
3347
3348 AFNI_OMP_START ;
3349 #pragma omp parallel if( qtop-pp > 1111 )
3350 { int qq , ii,jj,kk ;
3351 #pragma ivdep /* for Intel icc compiler */
3352 #pragma omp for
3353 for( qq=pp ; qq < qtop ; qq++ ){ /* get x=(ii,jj,kk), then */
3354 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ; /* compute xq */
3355 MAT44_VEC(BL,ii,jj,kk,xq[qq-pp],yq[qq-pp],zq[qq-pp]) ; /* yq and zd */
3356 }
3357 }
3358 AFNI_OMP_END ;
3359
3360 /* step 2: Interpolate A() warp index displacments at B(x) locations, and
3361 save in output displacement arrays xdc[], ydc[], and zdc[]. */
3362
3363 if( 0 /*AA->use_es*/ ) ES_PACK(AA,esar) ; /* load external slopes */
3364 IW3D_interp( icode, nx,ny,nz , xda , yda , zda ,
3365 0 /*AA->use_es*/ , esar ,
3366 qtop-pp , xq , yq , zq ,
3367 xdc+pp, ydc+pp, zdc+pp ) ;
3368
3369 /* step 3: Add in the B(x) displacments to get the total index
3370 displacment from each original position: B(x)-x + A(x+B(x)) */
3371
3372 AFNI_OMP_START ;
3373 #pragma omp parallel if( qtop-pp > 1111 )
3374 { int qq, ii,jj,kk ;
3375 #pragma omp for
3376 for( qq=pp ; qq < qtop ; qq++ ){
3377 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3378 xdc[qq] += xq[qq-pp] - ii ; /* subtract ii since we are storing */
3379 ydc[qq] += yq[qq-pp] - jj ; /* displacement from index ii (etc) */
3380 zdc[qq] += zq[qq-pp] - kk ;
3381 }
3382 }
3383 AFNI_OMP_END ;
3384
3385 } /* end of loop over segments of length NPER (or less) */
3386
3387 free(zq) ; free(yq) ; free(xq) ; /* tossa la trashola */
3388 RETURN(CC) ;
3389 }
3390
3391 /*---------------------------------------------------------------------------*/
3392 /* Compute B(A(x)) where both B and A are warps ????? */
3393
IW3D_compose(IndexWarp3D * AA,IndexWarp3D * BB,int icode)3394 IndexWarp3D * IW3D_compose( IndexWarp3D *AA , IndexWarp3D *BB , int icode )
3395 {
3396 int nx,ny,nz,nxy,nxyz , nall , pp,qtop;
3397 float *xda,*yda,*zda , *xq,*yq,*zq , *xdc,*ydc,*zdc ;
3398 IndexWarp3D *CC ;
3399 float esar[18] ;
3400
3401 ENTRY("IW3D_compose") ;
3402
3403 if( AA == NULL ){ CC = IW3D_copy(BB,1.0f) ; RETURN(CC) ; }
3404 else if( BB == NULL ){ CC = IW3D_copy(AA,1.0f) ; RETURN(CC) ; }
3405
3406 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
3407
3408 if( nx != BB->nx || ny != BB->ny || nz != BB->nz ) RETURN(NULL) ;
3409
3410 nall = NALL(nxyz) ; /* size of segments to process */
3411
3412 xq = (float *)malloc(sizeof(float)*nall) ; /* workspaces */
3413 yq = (float *)malloc(sizeof(float)*nall) ; /* for x+A(x) */
3414 zq = (float *)malloc(sizeof(float)*nall) ;
3415
3416 CC = IW3D_empty_copy(AA) ; /* output warp */
3417
3418 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ; /* input displacements */
3419 xdc = CC->xd ; ydc = CC->yd ; zdc = CC->zd ;
3420
3421 for( pp=0 ; pp < nxyz ; pp+=nall ){ /* loop over segments */
3422
3423 qtop = MIN( nxyz , pp+nall ) ; /* process points from pp to qtop-1 */
3424
3425 AFNI_OMP_START ;
3426 #pragma omp parallel if( qtop-pp > 1111 )
3427 { int qq , ii,jj,kk ;
3428 #pragma omp for
3429 for( qq=pp ; qq < qtop ; qq++ ){
3430 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3431 xq[qq-pp] = ii + xda[qq] ; /* x+A(x) warped indexes */
3432 yq[qq-pp] = jj + yda[qq] ;
3433 zq[qq-pp] = kk + zda[qq] ;
3434 }
3435 }
3436 AFNI_OMP_END ;
3437
3438 /* Interpolate B() warp index displacments at the x+A(x) locations */
3439
3440 if( 0 /*BB->use_es*/ ) ES_PACK(BB,esar) ;
3441 IW3D_interp( icode, nx,ny,nz , BB->xd, BB->yd, BB->zd ,
3442 0 /*BB->use_es*/ , esar ,
3443 qtop-pp , xq , yq , zq ,
3444 xdc+pp, ydc+pp, zdc+pp ) ;
3445
3446 /* Add in the A() displacments to get the total
3447 index displacment from each original position: A(x) + B(x+A(x)) */
3448
3449 AFNI_OMP_START ;
3450 #pragma omp parallel if( qtop-pp > 1111 )
3451 { int qq ;
3452 #pragma omp for
3453 for( qq=pp ; qq < qtop ; qq++ ){
3454 xdc[qq] += xda[qq] ; ydc[qq] += yda[qq] ; zdc[qq] += zda[qq] ;
3455 }
3456 }
3457 AFNI_OMP_END ;
3458
3459 } /* end of loop over segments of length NPER (or less) */
3460
3461 free(zq) ; free(yq) ; free(xq) ;
3462 RETURN(CC) ;
3463 }
3464
3465 #if 0 /* this code is never used, so is left out */
3466 /*---------------------------------------------------------------------------*/
3467 /* Compute A^(2^lev) , using linear interpolation only ????? */
3468
3469 IndexWarp3D * IW3D_2pow( IndexWarp3D *AA , int lev )
3470 {
3471 int nx,ny,nz,nxy,nxyz , nall , pp,qtop , ll ;
3472 float *xdb,*ydb,*zdb , *xq,*yq,*zq , *xdc,*ydc,*zdc ;
3473 IndexWarp3D *BB , *CC , *TT ;
3474 float esar[18] ;
3475
3476 ENTRY("IW3D_2pow") ;
3477
3478 if( AA == NULL ) RETURN(NULL) ; /* duh */
3479
3480 /* simple case of squaring */
3481
3482 if( lev == 1 ){ BB = IW3D_compose(AA,AA,MRI_LINEAR) ; RETURN(BB) ; }
3483
3484 BB = IW3D_copy(AA,1.0f) ; /* BB = AA (lev=0 result) */
3485
3486 if( lev <= 0 ) RETURN(BB) ;
3487
3488 nx = BB->nx ; ny = BB->ny ; nz = BB->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
3489
3490 nall = NALL(nxyz) ;
3491
3492 xq = (float *)malloc(sizeof(float)*nall) ; /* workspace */
3493 yq = (float *)malloc(sizeof(float)*nall) ;
3494 zq = (float *)malloc(sizeof(float)*nall) ;
3495
3496 CC = IW3D_empty_copy(BB) ;
3497
3498 /* input = BB ; compute CC = BB(BB(x)) ;
3499 then swap so output = BB ; wash, rinse, repeat */
3500
3501 for( ll=1 ; ll <= lev ; ll++ ){
3502
3503 xdb = BB->xd ; ydb = BB->yd ; zdb = BB->zd ;
3504 xdc = CC->xd ; ydc = CC->yd ; zdc = CC->zd ;
3505
3506 for( pp=0 ; pp < nxyz ; pp+=nall ){ /* loop over segments */
3507
3508 qtop = MIN( nxyz , pp+nall ) ; /* process points from pp to qtop-1 */
3509
3510 AFNI_OMP_START ;
3511 #pragma omp parallel if( qtop-pp > 1111 )
3512 { int qq , ii,jj,kk ;
3513 #pragma omp for
3514 for( qq=pp ; qq < qtop ; qq++ ){
3515 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3516 xq[qq-pp] = ii + xdb[qq] ; /* x+B(x) warped indexes */
3517 yq[qq-pp] = jj + ydb[qq] ;
3518 zq[qq-pp] = kk + zdb[qq] ;
3519 }
3520 }
3521 AFNI_OMP_END ;
3522
3523 /* Interpolate B() warp index displacments,
3524 at the B() locations, into the C() warp */
3525
3526 if( 0 /*BB->use_es*/ ) ES_PACK(BB,esar) ;
3527 IW3D_interp_linear( nx,ny,nz , xdb , ydb , zdb ,
3528 0 /*BB->use_es*/ , esar ,
3529 qtop-pp , xq , yq , zq ,
3530 xdc+pp, ydc+pp, zdc+pp ) ;
3531
3532 /* Add in the B() displacments to get the total
3533 index displacment from each original position: B(x) + B(x+B(x)) */
3534
3535 AFNI_OMP_START ;
3536 #pragma omp parallel if( qtop-pp > 1111 )
3537 { int qq ;
3538 #pragma omp for
3539 for( qq=pp ; qq < qtop ; qq++ ){
3540 xdc[qq] += xdb[qq] ; ydc[qq] += ydb[qq] ; zdc[qq] += zdb[qq] ;
3541 }
3542 }
3543 AFNI_OMP_END ;
3544
3545 } /* end of loop over segments of length NPER (or less) */
3546
3547 /* at this point, CC = BB(BB(x)) ;
3548 now swap them, to square BB again on next time thru loop */
3549
3550 TT = CC ; CC = BB ; BB = TT ;
3551 }
3552
3553 /* at the end, BB is the result, and CC is trash */
3554
3555 IW3D_destroy(CC) ; free(zq) ; free(yq) ; free(xq) ;
3556 RETURN(BB) ;
3557 }
3558 #endif
3559
3560 #endif /*(C9)*/ /*############################################################*/
3561
3562 #if 1
3563 /*============================================================================*/
3564 /* (C10) Functions to invert an index warp in its entirety */
3565 /*============================================================================*/
3566
3567 /*---------------------------------------------------------------------------*/
3568 /* Compute B( 2x - A(B(x)) ) = Newton step for computing Ainv(x),
3569 given an initial estimate of the inverse in B(x). Note that the fixed
3570 point of this iteration is
3571 B( 2x - A(B(x)) ) = B(x)
3572 2x - A(B(x)) = x
3573 x = A(B(X) -->> B(x) is inverse of A(x)
3574 -- actually, a damping factor is applied so that the output is actually
3575 C(x) = (1-inewtfac) * B(x) + inewtfac * B( 2x - A(B(x)) ) ;
3576 the purpose of this is to reduce any instabilities in the iteration.
3577 -- the calling function may adjust inewtfac up or down
3578 -- set inewtfix to 1 to avoid internal adjustment of inewtfac to
3579 start off small and increase when the iteration converges well
3580 -- this function computes one step of the iteration; IW3D_invert()
3581 drives this to produce the fixed point = warp inverse ?????
3582 *//*-------------------------------------------------------------------------*/
3583
3584 static float inewtfac = 0.5f ; /* damping factor for iteration */
3585 static int inewtfix = 0 ; /* is damping factor to be fixed? */
3586
IW3D_invert_newt(IndexWarp3D * AA,IndexWarp3D * BB,int icode)3587 IndexWarp3D * IW3D_invert_newt( IndexWarp3D *AA, IndexWarp3D *BB, int icode )
3588 {
3589 int nx,ny,nz,nxy,nxyz , nall , pp,qtop ;
3590 float *xda,*yda,*zda , *xq,*yq,*zq,*xr,*yr,*zr , *xdc,*ydc,*zdc , *xdb,*ydb,*zdb ;
3591 IndexWarp3D *CC ;
3592 float esar[18] ;
3593
3594 ENTRY("IW3D_invert_newt") ;
3595
3596 if( AA == NULL || BB == NULL ) RETURN(NULL) ; /* stoopidd luser */
3597
3598 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
3599
3600 if( nx != BB->nx || ny != BB->ny || nz != BB->nz ) RETURN(NULL) ;
3601
3602 nall = NALL(nxyz) ; /* workspace size */
3603
3604 xq = (float *)malloc(sizeof(float)*nall) ; /* workspace */
3605 yq = (float *)malloc(sizeof(float)*nall) ;
3606 zq = (float *)malloc(sizeof(float)*nall) ;
3607
3608 xr = (float *)malloc(sizeof(float)*nall) ;
3609 yr = (float *)malloc(sizeof(float)*nall) ;
3610 zr = (float *)malloc(sizeof(float)*nall) ;
3611
3612 CC = IW3D_empty_copy(AA) ; /* will be the output */
3613
3614 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
3615 xdb = BB->xd ; ydb = BB->yd ; zdb = BB->zd ;
3616 xdc = CC->xd ; ydc = CC->yd ; zdc = CC->zd ;
3617
3618 /* Warps are stored as voxel index displacements, so we have
3619 A(x) = x + a(x) [not needed below]
3620 B(x) = x + b(x)
3621 A(B(x)) = x + b(x) + a(x+b(x))
3622 2x - A(B(x)) = x - b(x) - a(x+b(x))
3623 B( 2x - A(B(x)) ) = x - b(x) - a(x+b(x)) + b(x-b(x)-a(x+b(x)))
3624 These steps are carried out in the inner loops below: */
3625
3626 for( pp=0 ; pp < nxyz ; pp+=nall ){ /* loop over segments */
3627
3628 qtop = MIN( nxyz , pp+nall ) ; /* process points from pp to qtop-1 */
3629
3630 /* Compute [xq,yq,zq] = x+b(x) */
3631
3632 AFNI_OMP_START ;
3633 #pragma omp parallel if( qtop-pp > 1111 )
3634 { int qq , ii,jj,kk ;
3635 #pragma ivdep
3636 #pragma omp for
3637 for( qq=pp ; qq < qtop ; qq++ ){
3638 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3639 xq[qq-pp] = ii + xdb[qq] ;
3640 yq[qq-pp] = jj + ydb[qq] ;
3641 zq[qq-pp] = kk + zdb[qq] ;
3642 }
3643 }
3644 AFNI_OMP_END ;
3645
3646 /* Compute [xr,yr,zr] = a(x+b(x)) by interpolation */
3647
3648 if( 0 /*AA->use_es*/ ) ES_PACK(AA,esar) ;
3649 IW3D_interp( icode, nx,ny,nz , xda, yda, zda,
3650 0 /*AA->use_es*/ , esar ,
3651 qtop-pp , xq , yq , zq ,
3652 xr , yr , zr ) ;
3653
3654 /* Compute [xr,yr,zr] = x - b(x) - a(x+b(x)) */
3655
3656 AFNI_OMP_START ;
3657 #pragma omp parallel if( qtop-pp > 1111 )
3658 { int qq , ii,jj,kk ;
3659 #pragma ivdep
3660 #pragma omp for
3661 for( qq=pp ; qq < qtop ; qq++ ){
3662 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3663 xr[qq-pp] = ii - xdb[qq] - xr[qq-pp] ;
3664 yr[qq-pp] = jj - ydb[qq] - yr[qq-pp] ;
3665 zr[qq-pp] = kk - zdb[qq] - zr[qq-pp] ;
3666 }
3667 }
3668 AFNI_OMP_END ;
3669
3670 /* Compute [xq,yq,zq] = b(x-b(x)-a(x+b(x))) by interpolation */
3671
3672 if( 0 /*BB->use_es*/ ) ES_PACK(BB,esar) ;
3673 IW3D_interp( icode, nx,ny,nz , xdb, ydb, zdb,
3674 0 /*BB->use_es*/ , esar ,
3675 qtop-pp , xr , yr , zr ,
3676 xq , yq , zq ) ;
3677
3678 /* Compute result = -b(x) - a(x+b(x)) + b(x-b(x)-a(x+b(x))) */
3679
3680 if( inewtfac <= 0.0f ){ /* undamped Newton */
3681
3682 AFNI_OMP_START ;
3683 #pragma omp parallel if( qtop-pp > 1111 )
3684 { int qq , ii,jj,kk ;
3685 #pragma ivdep
3686 #pragma omp for
3687 for( qq=pp ; qq < qtop ; qq++ ){
3688 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3689 xdc[qq] = xr[qq-pp] - ii + xq[qq-pp] ;
3690 ydc[qq] = yr[qq-pp] - jj + yq[qq-pp] ;
3691 zdc[qq] = zr[qq-pp] - kk + zq[qq-pp] ;
3692 }
3693 }
3694 AFNI_OMP_END ;
3695
3696 } else { /* damped Newton (for stability) */
3697
3698 AFNI_OMP_START ;
3699 #pragma omp parallel if( qtop-pp > 1111 )
3700 { int qq , ii,jj,kk ;
3701 register float nf , nf1 ;
3702 nf = inewtfac ; nf1 = 1.0f - nf ;
3703 #pragma ivdep
3704 #pragma omp for
3705 for( qq=pp ; qq < qtop ; qq++ ){
3706 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
3707 xdc[qq] = nf * (xr[qq-pp]-ii+xq[qq-pp]) + nf1*xdb[qq] ;
3708 ydc[qq] = nf * (yr[qq-pp]-jj+yq[qq-pp]) + nf1*ydb[qq] ;
3709 zdc[qq] = nf * (zr[qq-pp]-kk+zq[qq-pp]) + nf1*zdb[qq] ;
3710 }
3711 }
3712 AFNI_OMP_END ;
3713
3714 }
3715
3716 } /* end of loop over segments of length NPER (or less) */
3717
3718 free(zr); free(yr); free(xr); free(zq); free(yq); free(xq);
3719 RETURN(CC);
3720 }
3721
3722 /*---------------------------------------------------------------------------*/
3723 /* Find the inverse warp BB(x) to AA(x). If not NULL, BBinit is the
3724 initial estimate for BB(x). icode tells how to interpolate warps. ?????
3725 *//*-------------------------------------------------------------------------*/
3726
IW3D_invert(IndexWarp3D * AA,IndexWarp3D * BBinit,int icode)3727 IndexWarp3D * IW3D_invert( IndexWarp3D *AA , IndexWarp3D *BBinit , int icode )
3728 {
3729 IndexWarp3D *BB , *CC ;
3730 float normAA , normBC , nrat , orat ;
3731 int ii , nnewt=0 , nss,nii , jcode=MRI_LINEAR ;
3732 float switchval=0.001f ;
3733
3734 ENTRY("IW3D_invert") ;
3735
3736 if( AA == NULL ) RETURN(NULL) ;
3737
3738 normAA = IW3D_normLinf( AA , NULL ) ;
3739 if( normAA == 0.0f ){ /* input AA is all zero?! */
3740 BB = IW3D_empty_copy(AA) ; RETURN(BB) ;
3741 }
3742 if( icode == MRI_WSINC5 ) icode = MRI_QUINTIC ; /* wsinc5 is too slow */
3743 /* for many iterations */
3744 /* get BB = initial guess at inverse */
3745
3746 if( verb_nww ) ININFO_message(" -- invert max|AA|=%f",normAA) ;
3747
3748 if( BBinit == NULL ){ /* approximate inverse by power iteration */
3749 int pp = 1+(int)ceil(log2(normAA)) ; /* number of iterations */
3750 float qq ;
3751 if( pp < 2 ) pp = 2 ;
3752 qq = pow(0.5,pp) ;
3753 if( verb_nww ) ININFO_message(" - init nstep=%d qq=1/2^%d=%f",pp,pp,qq) ;
3754 BB = IW3D_copy( AA,-qq ) ; /* BB = I-qq*a(x) where A(x) = x + a(x) */
3755 for( ii=0 ; ii < pp ; ii++ ){ /* compute BB = [I-qq*a(x)]^pp */
3756 if( verb_nww > 1 ) ININFO_message(" - init step %d",ii+1) ;
3757 else if( Hverb && !verb_nww ) fprintf(stderr,"*") ;
3758 CC = IW3D_compose(BB,BB,jcode) ; IW3D_destroy(BB) ; BB = CC ;
3759 }
3760 } else {
3761 BB = IW3D_copy( BBinit , 1.0f ) ; /* that was easy */
3762 }
3763
3764 normAA = IW3D_normL1( AA , NULL ) ;
3765 if( !inewtfix ){ /* initialize Newton */
3766 inewtfac = 2.0f / (2.0f+sqrtf(normAA)) ; /* damping factor based */
3767 if( inewtfac > 0.333f ) inewtfac = 0.333f ; /* on size of input AA */
3768 }
3769
3770 if( verb_nww )
3771 ININFO_message(" - start iterations: normAA=%f inewtfac=%f",normAA,inewtfac) ;
3772 else if( Hverb )
3773 fprintf(stderr,".") ; /* for 3dNwarpAdjust (etc.) user pacification */
3774
3775 /* iterate some, until convergence or exhaustion */
3776
3777 #if 0
3778 if( getenv("AFNI_NWARP_SWITCHVAL") != NULL ){
3779 switchval = (float)AFNI_numenv("AFNI_NWARP_SWITCHVAL") ;
3780 if( switchval <= 0.0002f ) switchval = 0.0002f ;
3781 else if( switchval >= 0.0100f ) switchval = 0.0100f ;
3782 }
3783 #endif
3784
3785 nrat = 666.666f ; /* will be convergence criterion */
3786
3787 for( nii=nss=ii=0 ; ii < 69 ; ii++ ){ /* shouldn't take so many iterations */
3788
3789 /* take a Newton step [linear interp to start, higher order later] */
3790
3791 CC = BB ; BB = IW3D_invert_newt(AA,CC,jcode) ;
3792
3793 if( !verb_nww && Hverb ) fprintf(stderr,".") ; /* for 3dNwarpAdjust */
3794
3795 /* how close are they now (L1 norm of difference)? */
3796
3797 normBC = IW3D_normL1( BB , CC ) ; IW3D_destroy(CC) ;
3798
3799 orat = nrat ; nrat = normBC / normAA ; /* this is the closeness measure */
3800
3801 if( verb_nww ) ININFO_message(" - iterate %d nrat=%f",++nnewt,nrat) ;
3802
3803 /* check for convergence of B and C or at least closeness */
3804
3805 if( jcode != icode && nrat < switchval ){ /* switch to better interp? */
3806 jcode = icode ; nss = 0 ;
3807 if( verb_nww ) ININFO_message(" - switching from linear interp") ;
3808 else if( Hverb ) fprintf(stderr,":") ;
3809 continue ; /* iterate at least one more time */
3810 }
3811
3812 /*--- this is the Good Place of the iterations (i.e., converged) ---*/
3813
3814 if( nrat < 0.0001f ){ /* maybe we're done already? */
3815 if( verb_nww ) ININFO_message(" -- iteration converged") ;
3816 RETURN(BB) ; /* converged! */
3817 }
3818
3819 /* if instead things are getting worse, what to do?! */
3820
3821 if( ii > 3 && nrat > orat ){
3822 nii++ ; if( nii == 2 ) break ; /* getting worse! :-( */
3823 } else {
3824 nii = 0 ;
3825 }
3826
3827 /* update the damping factor for the next Newton step;
3828 make it larger if things got better, make it smaller if worser */
3829
3830 if( !inewtfix ){
3831 if( nss > 0 && nrat < 0.199f && nrat < orat && inewtfac < 0.678901f ){
3832 nss = 0 ; inewtfac *= 1.234f ; if( inewtfac > 0.678901f ) inewtfac = 0.678901f ;
3833 if( verb_nww > 1 ) ININFO_message(" - switch to inewtfac=%f",inewtfac) ;
3834 } else if( nss > 0 && nrat > orat ){
3835 nss = -66 ; inewtfac *= 0.444f ;
3836 if( verb_nww > 1 ) ININFO_message(" - switch to inewtfac=%f",inewtfac) ;
3837 } else {
3838 nss++ ;
3839 }
3840 }
3841
3842 } /* end of iteration loop */
3843
3844 /* failed to converge, return latest result anyhoo [not common] */
3845
3846 WARNING_message("IW3D_invert: iterations failed to converge :-(") ;
3847 RETURN(BB) ;
3848 }
3849
3850 /*---------------------------------------------------------------------------*/
3851 /* Invert a dataset that represents a warp.
3852 This is done the brute force way, by conversion to an index warp,
3853 inversion of that, and then conversion back to a dataset struct.
3854 *//*-------------------------------------------------------------------------*/
3855
THD_nwarp_invert(THD_3dim_dataset * dset_nwarp)3856 THD_3dim_dataset * THD_nwarp_invert( THD_3dim_dataset *dset_nwarp )
3857 {
3858 IndexWarp3D *AA , *BB ;
3859 THD_3dim_dataset *qset ;
3860
3861 ENTRY("THD_nwarp_invert") ;
3862
3863 if( dset_nwarp == NULL || DSET_NVALS(dset_nwarp) < 3 ) RETURN(NULL) ;
3864 DSET_load(dset_nwarp) ; if( !DSET_LOADED(dset_nwarp) ) RETURN(NULL) ;
3865
3866 AA = IW3D_from_dataset( dset_nwarp , 0 , 0 ) ; DSET_unload(dset_nwarp) ;
3867 BB = IW3D_extend( AA , 32, 32, 32, 32, 32, 32 , 0 ) ; IW3D_destroy(AA) ;
3868 AA = IW3D_invert( BB , NULL , MRI_QUINTIC ) ; IW3D_destroy(BB) ;
3869 BB = IW3D_extend( AA , -32,-32,-32,-32,-32,-32 , 0 ) ; IW3D_destroy(AA) ;
3870
3871 qset = IW3D_to_dataset( BB , "InvertedWarp" ) ; IW3D_destroy(BB) ;
3872 qset->view_type = dset_nwarp->view_type ;
3873 THD_init_diskptr_names( qset->dblk->diskptr, NULL,NULL,NULL, qset->view_type, False ) ;
3874 RETURN(qset) ;
3875 }
3876
3877 #endif /*(C10)*/ /*###########################################################*/
3878
3879 #if 1
3880 /*============================================================================*/
3881 /* (C11) Functions to compute the 'square root' of a warp.
3882 The square root is probably not actually very useful
3883 (especially given the 3dQwarp -plusminus option).
3884 [[The old code to do this was excised completely on 05 Feb 2021]] */
3885 /*============================================================================*/
3886
3887 /*---------------------------------------------------------------------------*/
3888 /* The following iterates on pairs of warps to produce the sqrt and
3889 sqrtinv at the same time. ?????
3890 *//*-------------------------------------------------------------------------*/
3891
3892 static float spgam = 1.0f ;
3893 static int spini = 0 ;
3894
IW3D_sqrtpair_step(IndexWarp3D_pair * YYZZ,int icode)3895 float IW3D_sqrtpair_step( IndexWarp3D_pair *YYZZ , int icode )
3896 {
3897 IndexWarp3D *YY , *ZZ , *Yinv , *Zinv ;
3898 float *Yixd, *Yiyd, *Yizd , *Zixd , *Ziyd , *Zizd ;
3899 float *Yfxd, *Yfyd, *Yfzd , *Zfxd , *Zfyd , *Zfzd ;
3900 int nxyz ; float tsum=0.0f ;
3901
3902 YY = YYZZ->fwarp ; ZZ = YYZZ->iwarp ; nxyz = YY->nx * YY->ny * YY->nz ;
3903
3904 if( spini ){
3905 Yinv = IW3D_invert( YY , ZZ , icode ) ;
3906 Zinv = IW3D_invert( ZZ , YY , icode ) ;
3907 } else {
3908 Yinv = IW3D_invert( YY , NULL , icode ) ;
3909 Zinv = IW3D_invert( ZZ , NULL , icode ) ;
3910 }
3911
3912 Yixd = Yinv->xd ; Yiyd = Yinv->yd ; Yizd = Yinv->zd ;
3913 Zixd = Zinv->xd ; Ziyd = Zinv->yd ; Zizd = Zinv->zd ;
3914 Yfxd = YY->xd ; Yfyd = YY->yd ; Yfzd = YY->zd ;
3915 Zfxd = ZZ->xd ; Zfyd = ZZ->yd ; Zfzd = ZZ->zd ;
3916
3917 AFNI_OMP_START ;
3918 #pragma omp parallel
3919 { int qq ; float sf , sf1 , yf,zf,yi,zi , esum=0.0f ;
3920 sf = 0.5f*spgam ; sf1 = 0.5f/spgam ;
3921
3922 #pragma omp for
3923 for( qq=0 ; qq < nxyz ; qq++ ){
3924 yf = Yfxd[qq] ; zf = Zfxd[qq] ; yi = Yixd[qq] ; zi = Zixd[qq] ;
3925 Yfxd[qq] = sf*yf + sf1*zi ; Zfxd[qq] = sf*zf + sf1*yi ;
3926 esum += fabsf(Yfxd[qq]-yf) + fabsf(Zfxd[qq]-zf) ;
3927
3928 yf = Yfyd[qq] ; zf = Zfyd[qq] ; yi = Yiyd[qq] ; zi = Ziyd[qq] ;
3929 Yfyd[qq] = sf*yf + sf1*zi ; Zfyd[qq] = sf*zf + sf1*yi ;
3930 esum += fabsf(Yfyd[qq]-yf) + fabsf(Zfyd[qq]-zf) ;
3931
3932 yf = Yfzd[qq] ; zf = Zfzd[qq] ; yi = Yizd[qq] ; zi = Zizd[qq] ;
3933 Yfzd[qq] = sf*yf + sf1*zi ; Zfzd[qq] = sf*zf + sf1*yi ;
3934 esum += fabsf(Yfzd[qq]-yf) + fabsf(Zfzd[qq]-zf) ;
3935 }
3936 #pragma omp critical
3937 { tsum += esum ; }
3938 }
3939 AFNI_OMP_END ;
3940
3941 IW3D_destroy(Yinv) ; IW3D_destroy(Zinv) ;
3942 return (tsum/nxyz) ;
3943 }
3944
3945 /*---------------------------------------------------------------------------*/
3946 /* Compute the warp pair ( sqrt(AA) , sqrtinv(AA) ) ????? */
3947
IW3D_sqrtpair(IndexWarp3D * AA,int icode)3948 IndexWarp3D_pair * IW3D_sqrtpair( IndexWarp3D *AA , int icode )
3949 {
3950 IndexWarp3D_pair *YYZZ ; IndexWarp3D *YY , *ZZ ;
3951 float tsum , normAA , nrat,orat ; int nite ;
3952
3953 /*-- initialize Y = 0.5*A , Z = 0.5*inv(A) --*/
3954
3955 if( verb_nww ) INFO_message("*** start sqrtpair") ;
3956
3957 normAA = IW3D_normL2(AA,NULL) ;
3958 YY = IW3D_copy(AA,0.5f) ;
3959 ZZ = IW3D_invert(AA,NULL,MRI_LINEAR) ; IW3D_scale(ZZ,0.5f) ;
3960 YYZZ = malloc(sizeof(IndexWarp3D_pair)) ;
3961 YYZZ->fwarp = YY ; YYZZ->iwarp = ZZ ;
3962
3963 spgam = 1.01f ; spini = 0 ; nrat = 666.0f ;
3964
3965 spini = 1 ; inewtfix = 1 ; inewtfac = 0.666666f ;
3966
3967 for( nite=0 ; nite < 39 ; nite++ ){
3968
3969 orat = nrat ;
3970
3971 tsum = IW3D_sqrtpair_step( YYZZ , MRI_LINEAR ) ;
3972
3973 nrat = tsum / normAA ;
3974 if( verb_nww ) ININFO_message("*** sqrtpair: nite=%d nrat=%g",nite,nrat) ;
3975
3976 if( nrat < 0.001666f ) break ; /* converged? */
3977 if( nite > 2 && nrat > orat*0.99f ) break ; /* not converging at all? */
3978 }
3979
3980 if( verb_nww ) INFO_message("*** sqrtpair: exit after %d iterations",nite+1) ;
3981
3982 inewtfix = 0 ;
3983 return YYZZ ;
3984 }
3985
3986 /*---------------------------------------------------------------------------*/
3987 /* Adaptations [05 Feb 2021] */
3988
IW3D_sqrtinv(IndexWarp3D * AA,int icode)3989 IndexWarp3D * IW3D_sqrtinv( IndexWarp3D *AA , int icode )
3990 {
3991 IndexWarp3D_pair *YZ ; IndexWarp3D *QQ ;
3992 YZ = IW3D_sqrtpair(AA,icode) ;
3993 QQ = YZ->iwarp ; IW3D_destroy(YZ->fwarp) ; free(YZ) ; return QQ ;
3994 }
3995
IW3D_sqrt(IndexWarp3D * AA,int icode)3996 IndexWarp3D * IW3D_sqrt( IndexWarp3D *AA , int icode )
3997 {
3998 IndexWarp3D_pair *YZ ; IndexWarp3D *QQ ;
3999 YZ = IW3D_sqrtpair(AA,icode) ;
4000 QQ = YZ->fwarp ; IW3D_destroy(YZ->iwarp) ; free(YZ) ; return QQ ;
4001 }
4002
4003 /*---------------------------------------------------------------------------*/
4004 /* Compute the warp pair ( sqrt(dset) , sqrtinv(dset) ) from a dataset. */
4005
THD_nwarp_sqrt(THD_3dim_dataset * dset_nwarp,int invert)4006 THD_3dim_dataset * THD_nwarp_sqrt( THD_3dim_dataset *dset_nwarp , int invert )
4007 {
4008 THD_3dim_dataset *qset ;
4009 IndexWarp3D *AA , *BB ; IndexWarp3D_pair *ABpair ; char *prefix=NULL ;
4010
4011 ENTRY("THD_nwarp_sqrt") ;
4012
4013 if( dset_nwarp == NULL || DSET_NVALS(dset_nwarp) < 3 ) RETURN(NULL) ;
4014 DSET_load(dset_nwarp) ; if( !DSET_LOADED(dset_nwarp) ) RETURN(NULL) ;
4015
4016 AA = IW3D_from_dataset( dset_nwarp , 0 , 0 ) ; DSET_unload(dset_nwarp) ;
4017 BB = IW3D_extend( AA , 32, 32, 32, 32, 32, 32 , 0 ) ; IW3D_destroy(AA) ;
4018
4019 ABpair = IW3D_sqrtpair( BB , MRI_QUINTIC ) ; IW3D_destroy(BB) ;
4020 if( ABpair == NULL ) RETURN(NULL) ;
4021
4022 if( invert ){
4023 AA = IW3D_extend( ABpair->iwarp , -32,-32,-32,-32,-32,-32 , 0 ) ;
4024 prefix = "SqrtInvWarp" ;
4025 } else {
4026 AA = IW3D_extend( ABpair->fwarp , -32,-32,-32,-32,-32,-32 , 0 ) ;
4027 prefix = "SqrtWarp" ;
4028 }
4029
4030 IW3D_pair_destroy(ABpair) ; IW3D_adopt_dataset(AA,dset_nwarp) ;
4031
4032 qset = IW3D_to_dataset( AA , prefix ) ; IW3D_destroy(AA) ;
4033 qset->view_type = dset_nwarp->view_type ;
4034 THD_init_diskptr_names( qset->dblk->diskptr, NULL,NULL,NULL, qset->view_type, False ) ;
4035 RETURN(qset) ;
4036 }
4037
4038 #endif /*(C11)*/ /*###########################################################*/
4039
4040 #if 1
4041 /*============================================================================*/
4042 /* (C12) These functions are for taking stuff from 3dAllineate -nwarp output.
4043 Since that option is obsolete, these functions are probably useless. ????? */
4044 /*============================================================================*/
4045
4046 #undef AFF_PARAM
4047 #undef AFF_MATRIX
4048
4049 #define AFF_PARAM 1
4050 #define AFF_MATRIX 2
4051
4052 static int affmode = AFF_PARAM ;
4053
4054 /*---------------------------------------------------------------------------*/
4055 /* Create a warp from a set of parameters, matching a template warp.
4056 (This is for compatibility with the obsolete 3dAllineate -nwarp option.)
4057 *//*-------------------------------------------------------------------------*/
4058
IW3D_from_poly(int npar,float * par,IndexWarp3D * WW)4059 IndexWarp3D * IW3D_from_poly( int npar, float *par, IndexWarp3D *WW )
4060 {
4061 GA_warpfunc *wfunc ; char *wname ;
4062 int nall , ii,jj,kk , nx,ny,nz,nxy,nxyz , pp,qq,qtop ;
4063 IndexWarp3D *AA ;
4064 float *xda,*yda,*zda , *xq,*yq,*zq , afpar[12] ;
4065
4066 ENTRY("IW3D_from_poly") ;
4067
4068 if( par == NULL || WW == NULL ) RETURN(NULL) ; /* should not happen */
4069
4070 /* cmat takes ijk -> xyz ; imat is cmat's inverse */
4071
4072 mri_genalign_affine_set_befafter( &(WW->cmat) , &(WW->imat) ) ;
4073
4074 /* choose the warping function, based on number of parameters */
4075
4076 switch( npar ){
4077 default: RETURN(NULL) ;
4078 case 64: wfunc = mri_genalign_cubic ; wname = "poly3" ; break ;
4079 case 172: wfunc = mri_genalign_quintic ; wname = "poly5" ; break ;
4080 case 364: wfunc = mri_genalign_heptic ; wname = "poly7" ; break ;
4081 case 664: wfunc = mri_genalign_nonic ; wname = "poly9" ; break ;
4082
4083 case 12:
4084 if( affmode == AFF_PARAM ){
4085 wfunc = mri_genalign_affine ; wname = "affine_param" ;
4086 } else {
4087 mat44 wmat, qmat, amat ; float qdet ; /* create index warp */
4088 LOAD_MAT44_AR(amat,par) ; /* matrix from coord */
4089 wmat = MAT44_MUL(amat,WW->cmat) ; /* warp matrix, and */
4090 qmat = MAT44_MUL(WW->imat,wmat) ; /* substitute for the */
4091 qdet = MAT44_DET(qmat) ; /* input parameters */
4092 if( qdet < 0.025f ){
4093 WARNING_message("Can't create warp from matrix with determinant=%g",qdet) ;
4094 RETURN(NULL) ;
4095 }
4096 UNLOAD_MAT44_AR(qmat,afpar) ; par = afpar ;
4097 wfunc = mri_genalign_mat44 ; wname = "affine_matrix" ;
4098 }
4099 break ;
4100 }
4101
4102 /* setup the output warp */
4103
4104 nx = WW->nx ; ny = WW->ny ; nz = WW->nz ; nxy = nx*ny ; nxyz = nxy*nz ;
4105 nxyz = nx*ny*nz ; nall = NALL(nxyz) ;
4106
4107 AA = IW3D_empty_copy( WW ) ;
4108 xda = AA->xd ; yda = AA->yd ; zda = AA->zd ;
4109
4110 xq = (float *)malloc(sizeof(float)*nall) ; /* workspace */
4111 yq = (float *)malloc(sizeof(float)*nall) ;
4112 zq = (float *)malloc(sizeof(float)*nall) ;
4113
4114 /* send parameters to warping function, for setup */
4115
4116 if( verb_nww > 1 ) ININFO_message(" - warp name = '%s' has %d parameters",wname,npar) ;
4117
4118 wfunc( npar , par , 0,NULL,NULL,NULL , NULL,NULL,NULL ) ;
4119
4120 /* do the work, Jake */
4121
4122 for( pp=0 ; pp < nxyz ; pp+=nall ){ /* loop over segments */
4123
4124 qtop = MIN( nxyz , pp+nall ) ; /* process points from pp to qtop-1 */
4125
4126 /* input coords are indexes */
4127
4128 AFNI_OMP_START ;
4129 #pragma omp parallel if( qtop-pp > 1111 )
4130 { int qq , ii,jj,kk ;
4131 #pragma omp for
4132 for( qq=pp ; qq < qtop ; qq++ ){
4133 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
4134 xq[qq-pp] = ii ; yq[qq-pp] = jj ; zq[qq-pp] = kk ;
4135 }
4136 }
4137 AFNI_OMP_END ;
4138
4139 /* compute index-to-index warp */
4140
4141 wfunc( npar , NULL , qtop-pp , xq,yq,zq , xda+pp,yda+pp,zda+pp ) ;
4142
4143 /* subtract off base indexes to make the result just be displacments */
4144
4145 AFNI_OMP_START ;
4146 #pragma omp parallel if( qtop-pp > 1111 )
4147 { int qq ;
4148 #pragma omp for
4149 for( qq=pp ; qq < qtop ; qq++ ){
4150 xda[qq] -= xq[qq-pp] ; yda[qq] -= yq[qq-pp] ; zda[qq] -= zq[qq-pp] ;
4151 }
4152 }
4153 AFNI_OMP_END ;
4154
4155 } /* end of loop over segments */
4156
4157 /* time to trot, Bwana */
4158
4159 free(zq) ; free(yq) ; free(xq) ;
4160 RETURN(AA) ;
4161 }
4162
4163 /*---------------------------------------------------------------------------*/
4164 /* Create a 'nonlinear' warp from a matrix;
4165 needs a dataset to serve as spatial template
4166 *//*-------------------------------------------------------------------------*/
4167
IW3D_from_mat44(mat44 mm,THD_3dim_dataset * mset)4168 IndexWarp3D * IW3D_from_mat44( mat44 mm , THD_3dim_dataset *mset )
4169 {
4170 IndexWarp3D *AA , *WW ; float mar[12] ;
4171
4172 if( !ISVALID_DSET(mset) ) return NULL ;
4173 if( MAT44_DET(mm) == 0.0f ) return NULL ;
4174
4175 WW = IW3D_create_vacant( DSET_NX(mset) , DSET_NY(mset) , DSET_NZ(mset) ) ;
4176 IW3D_adopt_dataset( WW , mset ) ;
4177 UNLOAD_MAT44( mm ,
4178 mar[0] , mar[1] , mar[2] , mar[3] , mar[ 4] , mar[ 5] ,
4179 mar[6] , mar[7] , mar[8] , mar[9] , mar[10] , mar[11] ) ;
4180 affmode = AFF_MATRIX ;
4181 AA = IW3D_from_poly( 12 , mar , WW ) ; /* kind of crude */
4182 IW3D_destroy( WW ) ;
4183 return AA ;
4184 }
4185
4186 #endif /*(C12)*/ /*###########################################################*/
4187
4188 #if 1
4189 /*============================================================================*/
4190 /* (C13) Various functions for interpolating images at arbitrary indexes. */
4191 /*============================================================================*/
4192
4193 /*--------------------------------------------------------------------------*/
4194 /* interpolate from a float image to a set of indexes (ip,jp,kp);
4195 this is just a wrapper for functions from mri_genalign_util.c */
4196
THD_interp_floatim(MRI_IMAGE * fim,int np,float * ip,float * jp,float * kp,int code,float * outar)4197 void THD_interp_floatim( MRI_IMAGE *fim ,
4198 int np , float *ip , float *jp , float *kp ,
4199 int code, float *outar )
4200 {
4201 ENTRY("THD_interp_floatim") ;
4202
4203 switch( code ){
4204 case MRI_NN: GA_interp_NN ( fim, np,ip,jp,kp, outar ) ; break ;
4205 case MRI_LINEAR: GA_interp_linear ( fim, np,ip,jp,kp, outar ) ; break ;
4206 case MRI_CUBIC: GA_interp_cubic ( fim, np,ip,jp,kp, outar ) ; break ;
4207 default:
4208 case MRI_QUINTIC: GA_interp_quintic( fim, np,ip,jp,kp, outar ) ; break ;
4209 case MRI_WSINC5: GA_interp_wsinc5 ( fim, np,ip,jp,kp, outar ) ; break ;
4210 }
4211
4212 /* clipping */
4213
4214 if( MRI_HIGHORDER(code) ){
4215 int ii,nn=fim->nvox ; float bot,top , *far=MRI_FLOAT_PTR(fim) ;
4216 bot = top = far[0] ;
4217 for( ii=1 ; ii < nn ; ii++ ) if( bot > far[ii] ) bot = far[ii] ;
4218 else if( top < far[ii] ) top = far[ii] ;
4219 for( ii=0 ; ii < np ; ii++ ) if( outar[ii] < bot ) outar[ii] = bot ;
4220 else if( outar[ii] > top ) outar[ii] = top ;
4221 }
4222
4223 EXRETURN ;
4224 }
4225
4226 /*--------------------------------------------------------------------------*/
4227 /* interpolate from a complex-valued image to a set of indexes (ip,jp,kp) */
4228
THD_interp_complexim(MRI_IMAGE * fim,int np,float * ip,float * jp,float * kp,int code,complex * outar)4229 void THD_interp_complexim( MRI_IMAGE *fim ,
4230 int np , float *ip , float *jp , float *kp ,
4231 int code, complex *outar )
4232 {
4233 MRI_IMARR *rpair ;
4234 MRI_IMAGE *rim , *iim , *aim , *bim ;
4235 float *rar , *iar ;
4236 complex *aar ;
4237
4238 ENTRY("THD_interp_complexim") ;
4239
4240 /* split input into float pair */
4241
4242 rpair = mri_complex_to_pair( fim ) ;
4243 if( rpair == NULL ) EXRETURN ;
4244 aim = IMARR_SUBIM(rpair,0) ;
4245 bim = IMARR_SUBIM(rpair,1) ;
4246
4247 /* make float images for outputs */
4248
4249 rim = mri_new_conforming( fim , MRI_float ) ; rar = MRI_FLOAT_PTR(rim) ;
4250 iim = mri_new_conforming( fim , MRI_float ) ; iar = MRI_FLOAT_PTR(iim) ;
4251
4252 /* interpolate into these new images */
4253
4254 switch( code ){
4255 case MRI_NN: GA_interp_NN ( aim, np,ip,jp,kp, rar ) ;
4256 GA_interp_NN ( bim, np,ip,jp,kp, iar ) ; break ;
4257 case MRI_LINEAR: GA_interp_linear ( aim, np,ip,jp,kp, rar ) ;
4258 GA_interp_linear ( bim, np,ip,jp,kp, iar ) ; break ;
4259 case MRI_CUBIC: GA_interp_cubic ( aim, np,ip,jp,kp, rar ) ;
4260 GA_interp_cubic ( bim, np,ip,jp,kp, iar ) ; break ;
4261 default:
4262 case MRI_QUINTIC: GA_interp_quintic( aim, np,ip,jp,kp, rar ) ;
4263 GA_interp_quintic( bim, np,ip,jp,kp, iar ) ; break ;
4264 case MRI_WSINC5: GA_interp_wsinc5 ( aim, np,ip,jp,kp, rar ) ;
4265 GA_interp_wsinc5 ( bim, np,ip,jp,kp, iar ) ; break ;
4266 }
4267
4268 /* toss the input pair */
4269
4270 DESTROY_IMARR(rpair) ;
4271
4272 /* convert output pair to single complex image */
4273
4274 aim = mri_pair_to_complex( rim , iim ) ; mri_free(rim) ; mri_free(iim) ;
4275
4276 /* copy that data to the user-provided output array */
4277
4278 aar = MRI_COMPLEX_PTR(aim) ;
4279 memcpy( outar , aar , sizeof(complex)*np ) ;
4280 mri_free(aim) ;
4281
4282 EXRETURN ;
4283 }
4284
4285 /*----------------------------------------------------------------------------*/
4286 /* Apply a warp to a source image 'sim', and stick values into output 'fim'.
4287 Apply to a sub-volume ibot..itop, jbot..jtop, kbot..ktop (inclusive).
4288 *//*--------------------------------------------------------------------------*/
4289
IW3D_warp_into_floatim(IndexWarp3D * AA,MRI_IMAGE * sim,MRI_IMAGE * fim,int ibot,int itop,int jbot,int jtop,int kbot,int ktop,int code,float fac)4290 void IW3D_warp_into_floatim( IndexWarp3D *AA, MRI_IMAGE *sim, MRI_IMAGE *fim,
4291 int ibot, int itop ,
4292 int jbot, int jtop ,
4293 int kbot, int ktop , int code , float fac )
4294 {
4295 int nx,ny,nz,nxy , nii,njj,nkk , np , ii,jj,kk,ijk , pp ;
4296 float *ip,*jp,*kp ;
4297 float *far , *xd,*yd,*zd ;
4298
4299 ENTRY("IW3D_warp_into_floatim") ;
4300
4301 if( AA == NULL ) EXRETURN ;
4302 if( sim == NULL || sim->kind != MRI_float ) EXRETURN ;
4303 if( fim == NULL || fim->kind != MRI_float ) EXRETURN ;
4304
4305 nx = AA->nx ; ny = AA->ny ; nz = AA->nz ; nxy = nx*ny ;
4306 if( sim->nx != nx || sim->ny != ny || sim->nz != nz ) EXRETURN ;
4307 if( fim->nx != nx || fim->ny != ny || fim->nz != nz ) EXRETURN ;
4308
4309 if( ibot < 0 ) ibot = 0 ; if( itop > nx-1 ) itop = nx-1 ;
4310 if( jbot < 0 ) jbot = 0 ; if( jtop > ny-1 ) itop = ny-1 ;
4311 if( kbot < 0 ) kbot = 0 ; if( ktop > nz-1 ) itop = nz-1 ;
4312
4313 nii = itop - ibot + 1 ; if( nii < 1 ) EXRETURN ;
4314 njj = jtop - jbot + 1 ; if( njj < 1 ) EXRETURN ;
4315 nkk = ktop - kbot + 1 ; if( nkk < 1 ) EXRETURN ;
4316
4317 np = nii*njj*nkk ;
4318 ip = (float *)malloc(sizeof(float)*np) ;
4319 jp = (float *)malloc(sizeof(float)*np) ;
4320 kp = (float *)malloc(sizeof(float)*np) ;
4321
4322 xd = AA->xd ; yd = AA->yd ; zd = AA->zd ;
4323
4324 for( pp=0,kk=kbot ; kk <= ktop ; kk++ ){
4325 for( jj=jbot ; jj <= jtop ; jj++ ){
4326 #pragma ivdep
4327 for( ii=ibot ; ii <= itop ; ii++,pp++ ){
4328 ijk = ii + jj*nx + kk*nxy ;
4329 ip[pp] = ii + xd[ijk] * fac ;
4330 jp[pp] = jj + yd[ijk] * fac ;
4331 kp[pp] = kk + zd[ijk] * fac ;
4332 }
4333 }
4334 }
4335
4336 far = MRI_FLOAT_PTR(fim) ;
4337
4338 /*-- All of them, Frank? --*/
4339
4340 if( nii == nx && njj == ny && nkk == nz ){
4341
4342 THD_interp_floatim( sim , np,ip,jp,kp , code , far ) ;
4343
4344 } else { /*-- just some of them, Mother Goose? --*/
4345
4346 float *val = (float *)malloc(sizeof(float)*np) ;
4347
4348 THD_interp_floatim( sim , np,ip,jp,kp , code , val ) ;
4349
4350 for( pp=0,kk=kbot ; kk <= ktop ; kk++ )
4351 for( jj=jbot ; jj <= jtop ; jj++ )
4352 for( ii=ibot ; ii <= itop ; ii++,pp++ ) far[ii+jj*nx+kk*nxy] = val[pp];
4353
4354 free(val) ;
4355 }
4356
4357 free(kp) ; free(jp) ; free(ip) ;
4358 EXRETURN ;
4359 }
4360
4361 /*----------------------------------------------------------------------------*/
4362 /* Warp a source image 'sim' to create an output image */
4363
IW3D_warp_floatim(IndexWarp3D * AA,MRI_IMAGE * sim,int code,float fac)4364 MRI_IMAGE * IW3D_warp_floatim( IndexWarp3D *AA, MRI_IMAGE *sim, int code, float fac )
4365 {
4366 MRI_IMAGE *fim ;
4367
4368 ENTRY("IW3D_warp_floatim") ;
4369
4370 if( AA == NULL || sim == NULL ) RETURN(NULL) ;
4371
4372 fim = mri_new_conforming( sim , MRI_float ) ;
4373
4374 IW3D_warp_into_floatim( AA , sim , fim ,
4375 0,sim->nx-1 , 0,sim->ny-1 , 0,sim->nz-1 , code , fac ) ;
4376
4377 if( MRI_HIGHORDER(code) ){ /* clipping */
4378 double_pair smm = mri_minmax(sim) ;
4379 float sb=(float)smm.a , st=(float)smm.b ; int qq ;
4380 float *far=MRI_FLOAT_PTR(fim) ;
4381 for( qq=0 ; qq < fim->nvox ; qq++ ){
4382 if( far[qq] < sb ) far[qq] = sb ; else if( far[qq] > st ) far[qq] = st ;
4383 }
4384 }
4385
4386 RETURN(fim) ;
4387 }
4388
4389 /*----------------------------------------------------------------------------*/
4390 /* Source into output (sim to fim) with a matrix instead of an index warp */
4391
IW3D_mat44_into_floatim(mat44 imat,MRI_IMAGE * sim,MRI_IMAGE * fim,int ibot,int itop,int jbot,int jtop,int kbot,int ktop,int code)4392 void IW3D_mat44_into_floatim( mat44 imat , MRI_IMAGE *sim, MRI_IMAGE *fim,
4393 int ibot, int itop ,
4394 int jbot, int jtop ,
4395 int kbot, int ktop , int code )
4396 {
4397 int nx,ny,nz,nxy , nii,njj,nkk , np , ii,jj,kk , pp ;
4398 float *ip,*jp,*kp , *far ;
4399
4400 ENTRY("IW3D_mat44_into_floatim") ;
4401
4402 if( sim == NULL || sim->kind != MRI_float ) EXRETURN ;
4403 if( fim == NULL || fim->kind != MRI_float ) EXRETURN ;
4404
4405 nx = fim->nx ; ny = fim->ny ; nz = fim->nz ; nxy = nx*ny ;
4406
4407 if( ibot < 0 ) ibot = 0 ; if( itop > nx-1 ) itop = nx-1 ;
4408 if( jbot < 0 ) jbot = 0 ; if( jtop > ny-1 ) itop = ny-1 ;
4409 if( kbot < 0 ) kbot = 0 ; if( ktop > nz-1 ) itop = nz-1 ;
4410
4411 nii = itop - ibot + 1 ; if( nii < 1 ) EXRETURN ;
4412 njj = jtop - jbot + 1 ; if( njj < 1 ) EXRETURN ;
4413 nkk = ktop - kbot + 1 ; if( nkk < 1 ) EXRETURN ;
4414
4415 np = nii*njj*nkk ;
4416 ip = (float *)malloc(sizeof(float)*np) ;
4417 jp = (float *)malloc(sizeof(float)*np) ;
4418 kp = (float *)malloc(sizeof(float)*np) ;
4419
4420 for( pp=0,kk=kbot ; kk <= ktop ; kk++ ){
4421 for( jj=jbot ; jj <= jtop ; jj++ ){
4422 #pragma ivdep
4423 for( ii=ibot ; ii <= itop ; ii++,pp++ ){
4424 MAT44_VEC( imat , ii,jj,kk , ip[pp],jp[pp],kp[pp] ) ;
4425 }
4426 }
4427 }
4428
4429 far = MRI_FLOAT_PTR(fim) ;
4430
4431 /*-- All of them, Frank? --*/
4432
4433 if( nii == nx && njj == ny && nkk == nz ){
4434
4435 THD_interp_floatim( sim , np,ip,jp,kp , code , far ) ;
4436
4437 } else { /*-- just some of them, Mother Goose? --*/
4438
4439 float *val = (float *)malloc(sizeof(float)*np) ;
4440
4441 THD_interp_floatim( sim , np,ip,jp,kp , code , val ) ;
4442
4443 for( pp=0,kk=kbot ; kk <= ktop ; kk++ )
4444 for( jj=jbot ; jj <= jtop ; jj++ )
4445 for( ii=ibot ; ii <= itop ; ii++,pp++ ) far[ii+jj*nx+kk*nxy] = val[pp];
4446
4447 free(val) ;
4448 }
4449
4450 free(kp) ; free(jp) ; free(ip) ;
4451 EXRETURN ;
4452 }
4453
4454 #if 0 /* maybe useful someday? */
4455 /*----------------------------------------------------------------------------*/
4456 /* interpolate from 1 image to another, preserving type */
4457
4458 void THD_interp( MRI_IMAGE *inim ,
4459 int np , float *ip , float *jp , float *kp ,
4460 int code, void *outar )
4461 {
4462 MRI_IMAGE *fim=inim ; float *far ; register int ii ;
4463
4464 ENTRY("THD_interp") ;
4465
4466 switch( fim->kind ){
4467
4468 default:
4469 ERROR_message("Illegal input type %d in THD_interp()",(int)fim->kind) ;
4470 break ;
4471
4472 /*--------------------*/
4473
4474 case MRI_float:
4475 THD_interp_floatim( inim , np,ip,jp,kp , code,(float *)outar ) ;
4476 break ;
4477
4478 /*--------------------*/
4479
4480 case MRI_fvect:{
4481 int kk , vd=inim->vdim ; float *oar=(float *)outar ;
4482 far = (float *)malloc(sizeof(float)*np) ;
4483 for( kk=0 ; kk < vd ; kk++ ){
4484 fim = mri_fvect_subimage(inim,kk) ;
4485 THD_interp_floatim( inim , np,ip,jp,kp , code,far ) ;
4486 for( ii=0 ; ii < np ; ii++ ) oar[ii*vd+kk] = far[ii] ;
4487 mri_free(fim) ;
4488 }
4489 free(far) ;
4490 }
4491 break ;
4492
4493 /*--------------------*/
4494
4495 case MRI_short:{
4496 short *sar=(short *)outar ;
4497 fim = mri_to_float(inim) ; far = (float *)malloc(sizeof(float)*np) ;
4498 THD_interp_floatim( inim , np,ip,jp,kp , code,far ) ;
4499 for( ii=0 ; ii < np ; ii++ ) sar[ii] = SHORTIZE(far[ii]) ;
4500 free(far) ; mri_free(fim) ;
4501 }
4502 break ;
4503
4504 /*--------------------*/
4505
4506 case MRI_byte:{
4507 byte *bar=(byte *)outar ;
4508 fim = mri_to_float(inim) ; far = (float *)malloc(sizeof(float)*np) ;
4509 THD_interp_floatim( inim , np,ip,jp,kp , code,far ) ;
4510 for( ii=0 ; ii < np ; ii++ ) bar[ii] = BYTEIZE(far[ii]) ;
4511 free(far) ; mri_free(fim) ;
4512 }
4513 break ;
4514
4515 /*--------------------*/
4516
4517 case MRI_complex:{
4518 complex *car=(complex *)outar ; MRI_IMARR *imar ; float *gar ;
4519 far = (float *)malloc(sizeof(float)*np) ;
4520 gar = (float *)malloc(sizeof(float)*np) ;
4521 imar = mri_complex_to_pair(inim) ;
4522 THD_interp_floatim( IMARR_SUBIM(imar,0) , np,ip,jp,kp , code,far ) ;
4523 THD_interp_floatim( IMARR_SUBIM(imar,1) , np,ip,jp,kp , code,gar ) ;
4524 for( ii=0 ; ii < np ; ii++ ){ car[ii].r = far[ii]; car[ii].i = gar[ii]; }
4525 DESTROY_IMARR(imar) ; free(gar) ; free(far) ;
4526 }
4527 break ;
4528
4529 /*--------------------*/
4530
4531 case MRI_rgb:{
4532 MRI_IMARR *imar ; float *gar , *har ; byte *bar=(byte *)outar ;
4533 far = (float *)malloc(sizeof(float)*np) ;
4534 gar = (float *)malloc(sizeof(float)*np) ;
4535 har = (float *)malloc(sizeof(float)*np) ;
4536 imar = mri_rgb_to_3float(inim) ;
4537 THD_interp_floatim( IMARR_SUBIM(imar,0) , np,ip,jp,kp , code,far ) ;
4538 THD_interp_floatim( IMARR_SUBIM(imar,1) , np,ip,jp,kp , code,gar ) ;
4539 THD_interp_floatim( IMARR_SUBIM(imar,2) , np,ip,jp,kp , code,har ) ;
4540 for( ii=0 ; ii < np ; ii++ ){
4541 bar[3*ii ] = BYTEIZE(far[ii]) ;
4542 bar[3*ii+1] = BYTEIZE(gar[ii]) ;
4543 bar[3*ii+2] = BYTEIZE(har[ii]) ;
4544 }
4545 DESTROY_IMARR(imar) ; free(har) ; free(gar) ; free(far) ;
4546 }
4547 break ;
4548
4549 }
4550
4551 EXRETURN ;
4552 }
4553 #endif
4554
4555 #endif /*(C13)*/ /*###########################################################*/
4556
4557 #if 1
4558 /*============================================================================*/
4559 /* (C14) Functions used in 3dNwarpXYZ to warp a limited number of points given
4560 by x,y,z triples, rather than warping a whole grid at once. */
4561 /*============================================================================*/
4562
4563 /** Interpolation in 3 displacement fields at once **/
4564
4565 #if 0 /*----------------------------- via linear interp ---------------------*/
4566 int THD_nwarp_im_xyz( MRI_IMAGE *xdim , MRI_IMAGE *ydim , MRI_IMAGE *zdim ,
4567 float dfac , int npt ,
4568 float *xin , float *yin , float *zin ,
4569 float *xut , float *yut , float *zut ,
4570 mat44 imat , floatvec *esv )
4571 {
4572 ES_DECLARE_FLOATS ;
4573 int nx,ny,nz , nx1,ny1,nz1 , nxy , nx2,ny2,nz2 ;
4574 float *xdar , *ydar , *zdar ;
4575
4576 ENTRY("THD_nwarp_im_xyz") ;
4577
4578 if( esv != NULL ) ES_UNPACKVEC(esv->ar) ;
4579
4580 nx = xdim->nx ; ny = xdim->ny ; nz = zdim->nz ;
4581 nx1 = nx-1 ; ny1 = ny-1 ; nz1 = nz-1 ; nxy = nx*ny ;
4582 nx2 = nx-2 ; ny2 = ny-2 ; nz2 = nz-2 ;
4583
4584 xdar = MRI_FLOAT_PTR(xdim) ; /* displacement arrays */
4585 ydar = MRI_FLOAT_PTR(ydim) ;
4586 zdar = MRI_FLOAT_PTR(zdim) ;
4587
4588 /* parallel-ized linear interpolation (and extrapolation) */
4589
4590 AFNI_OMP_START ;
4591 #pragma omp parallel if( npt > 333 )
4592 { int pp ;
4593 float xx,yy,zz , fx,fy,fz ; int ix,jy,kz , aem ;
4594 float eex=0.0f,eey=0.0f,eez=0.0f , Exx=0.0f,Exy=0.0f,Exz=0.0f ,
4595 Eyx=0.0f,Eyy=0.0f,Eyz=0.0f , Ezx=0.0f,Ezy=0.0f,Ezz=0.0f , uex,vex,wex ;
4596 int ix_00,ix_p1 , jy_00,jy_p1 , kz_00,kz_p1 ;
4597 float wt_00,wt_p1 ;
4598 float f_j00_k00, f_jp1_k00, f_j00_kp1, f_jp1_kp1, f_k00, f_kp1 ;
4599 float g_j00_k00, g_jp1_k00, g_j00_kp1, g_jp1_kp1, g_k00, g_kp1 ;
4600 float h_j00_k00, h_jp1_k00, h_j00_kp1, h_jp1_kp1, h_k00, h_kp1 ;
4601
4602 /* loop over input points, transforming them to indexes, interpolating
4603 to that location, then adding the displacments to get the outputs */
4604
4605 #pragma omp for
4606 for( pp=0 ; pp < npt ; pp++ ){
4607 MAT44_VEC( imat , xin[pp],yin[pp],zin[pp] , xx,yy,zz ) ; /* convert to index coords */
4608 /* find location in or out of dataset grid, and deal with external slopes if need be */
4609 aem=0 ; /* flag for usage of external slopes */
4610 if( xx < 0.0f ){ eex = xx ; ix = 0 ; fx = 0.0f; aem++; Exx=es_xd_xm; Eyx=es_yd_xm; Ezx=es_zd_xm; }
4611 else if( xx < nx1 ){ eex = 0.0f ; ix = (int)xx; fx = xx-ix; }
4612 else { eex = xx-nx1; ix = nx2 ; fx = 1.0f; aem++; Exx=es_xd_xp; Eyx=es_yd_xp; Ezx=es_zd_xp; }
4613 if( yy < 0.0f ){ eey = yy ; jy = 0 ; fy = 0.0f; aem++; Exy=es_xd_ym; Eyy=es_yd_ym; Ezy=es_zd_ym; }
4614 else if( yy < ny1 ){ eey = 0.0f ; jy = (int)yy; fy = yy-jy; }
4615 else { eey = yy-ny1; jy = ny2 ; fy = 1.0f; aem++; Exy=es_xd_yp; Eyy=es_yd_yp; Ezy=es_zd_yp; }
4616 if( zz < 0.0f ){ eez = zz ; kz = 0 ; fz = 0.0f; aem++; Exz=es_xd_zm; Eyz=es_yd_zm; Ezz=es_zd_zm; }
4617 else if( zz < nz1 ){ eez = 0.0f ; kz = (int)zz; fz = zz-kz; }
4618 else { eez = zz-nz1; kz = nz2 ; fz = 1.0f; aem++; Exz=es_xd_zp; Eyz=es_yd_zp; Ezz=es_zd_zp; }
4619 if( aem ){
4620 uex = Exx*eex + Exy*eey + Exz*eez ; /* eex = how far past edge of warp, in x-direction (etc.) */
4621 vex = Eyx*eex + Eyy*eey + Eyz*eez ; /* Eab = external slope for 'a' displacement, */
4622 wex = Ezx*eex + Ezy*eey + Ezz*eez ; /* along the 'b' direction, for a and b = x or y or z */
4623 uex *= dfac ; vex *= dfac ; wex *= dfac ;
4624 } else {
4625 uex = vex = wex = 0.0f ;
4626 }
4627 /* now linearly interpolate displacements inside the dataset grid */
4628 ix_00 = ix ; ix_p1 = ix_00+1 ; /* at this point, we are 'fx' between indexes ix_00 and ix_p1 */
4629 jy_00 = jy ; jy_p1 = jy_00+1 ; /* et cetera */
4630 kz_00 = kz ; kz_p1 = kz_00+1 ;
4631 wt_00 = (1.0f-fx)*dfac ; wt_p1 = fx*dfac ; /* weights for ix_00 and ix_p1 points for linear interp */
4632
4633 #undef IJK /* 1D index from 3D index */
4634 #define IJK(i,j,k) ((i)+(j)*nx+(k)*nxy)
4635
4636 #undef XINT /* linear interpolation macro in array 'aaa' at plane jk */
4637 #define XINT(aaa,j,k) wt_00*aaa[IJK(ix_00,j,k)]+wt_p1*aaa[IJK(ix_p1,j,k)]
4638
4639 /* interpolate to location ix+fx at each jy,kz level */
4640
4641 f_j00_k00 = XINT(xdar,jy_00,kz_00) ; f_jp1_k00 = XINT(xdar,jy_p1,kz_00) ;
4642 f_j00_kp1 = XINT(xdar,jy_00,kz_p1) ; f_jp1_kp1 = XINT(xdar,jy_p1,kz_p1) ;
4643 g_j00_k00 = XINT(ydar,jy_00,kz_00) ; g_jp1_k00 = XINT(ydar,jy_p1,kz_00) ;
4644 g_j00_kp1 = XINT(ydar,jy_00,kz_p1) ; g_jp1_kp1 = XINT(ydar,jy_p1,kz_p1) ;
4645 h_j00_k00 = XINT(zdar,jy_00,kz_00) ; h_jp1_k00 = XINT(zdar,jy_p1,kz_00) ;
4646 h_j00_kp1 = XINT(zdar,jy_00,kz_p1) ; h_jp1_kp1 = XINT(zdar,jy_p1,kz_p1) ;
4647
4648 /* interpolate to jy+fy at each kz level */
4649
4650 wt_00 = 1.0f-fy ; wt_p1 = fy ;
4651 f_k00 = wt_00 * f_j00_k00 + wt_p1 * f_jp1_k00 ;
4652 f_kp1 = wt_00 * f_j00_kp1 + wt_p1 * f_jp1_kp1 ;
4653 g_k00 = wt_00 * g_j00_k00 + wt_p1 * g_jp1_k00 ;
4654 g_kp1 = wt_00 * g_j00_kp1 + wt_p1 * g_jp1_kp1 ;
4655 h_k00 = wt_00 * h_j00_k00 + wt_p1 * h_jp1_k00 ;
4656 h_kp1 = wt_00 * h_j00_kp1 + wt_p1 * h_jp1_kp1 ;
4657
4658 /* interpolate to kz+fz to get output, plus add in original coords */
4659
4660 xut[pp] = (1.0f-fz) * f_k00 + fz * f_kp1 + uex + xin[pp] ; /* note add-in of values */
4661 yut[pp] = (1.0f-fz) * g_k00 + fz * g_kp1 + vex + yin[pp] ; /* from external slope */
4662 zut[pp] = (1.0f-fz) * h_k00 + fz * h_kp1 + wex + zin[pp] ; /* calculation above */
4663
4664 } /* end of loop over input/output points */
4665 } /* end of parallel code */
4666 AFNI_OMP_END ;
4667
4668 RETURN(npt) ;
4669 }
4670 #else /*------------------------ via quintic interp -------------------------*/
THD_nwarp_im_xyz(MRI_IMAGE * xdim,MRI_IMAGE * ydim,MRI_IMAGE * zdim,float dfac,int npt,float * xin,float * yin,float * zin,float * xut,float * yut,float * zut,mat44 imat,floatvec * esv)4671 int THD_nwarp_im_xyz( MRI_IMAGE *xdim , MRI_IMAGE *ydim , MRI_IMAGE *zdim ,
4672 float dfac , int npt ,
4673 float *xin , float *yin , float *zin ,
4674 float *xut , float *yut , float *zut ,
4675 mat44 imat , floatvec *esv )
4676 {
4677 ES_DECLARE_FLOATS ;
4678 int nx,ny,nz , nx1,ny1,nz1 , nxy , nx2,ny2,nz2 ;
4679 float *xdar , *ydar , *zdar ;
4680
4681 ENTRY("THD_nwarp_im_xyz") ;
4682
4683 if( esv != NULL ) ES_UNPACKVEC(esv->ar) ;
4684
4685 nx = xdim->nx ; ny = xdim->ny ; nz = zdim->nz ;
4686 nx1 = nx-1 ; ny1 = ny-1 ; nz1 = nz-1 ; nxy = nx*ny ;
4687 nx2 = nx-2 ; ny2 = ny-2 ; nz2 = nz-2 ;
4688
4689 xdar = MRI_FLOAT_PTR(xdim) ; /* displacement arrays */
4690 ydar = MRI_FLOAT_PTR(ydim) ;
4691 zdar = MRI_FLOAT_PTR(zdim) ;
4692
4693 /* quintic interpolation */
4694
4695 AFNI_OMP_START ;
4696 #pragma omp parallel if( npt > 333 )
4697 { int pp ;
4698 float xx,yy,zz , fx,fy,fz ; int ix,jy,kz , aem ;
4699 float eex=0.0f,eey=0.0f,eez=0.0f , Exx=0.0f,Exy=0.0f,Exz=0.0f ,
4700 Eyx=0.0f,Eyy=0.0f,Eyz=0.0f , Ezx=0.0f,Ezy=0.0f,Ezz=0.0f , uex,vex,wex ;
4701 int nx1=nx-1,ny1=ny-1,nz1=nz-1 ;
4702 int nx2=nx-2,ny2=ny-2,nz2=nz-2 ;
4703 int ix_m2,ix_m1,ix_00,ix_p1,ix_p2,ix_p3 ; /* interpolation indices */
4704 int jy_m2,jy_m1,jy_00,jy_p1,jy_p2,jy_p3 ; /* (input image) */
4705 int kz_m2,kz_m1,kz_00,kz_p1,kz_p2,kz_p3 ;
4706
4707 float wt_m2,wt_m1,wt_00,wt_p1,wt_p2,wt_p3 ; /* interpolation weights */
4708
4709 float f_jm2_km2, f_jm1_km2, f_j00_km2, f_jp1_km2, f_jp2_km2, f_jp3_km2,
4710 f_jm2_km1, f_jm1_km1, f_j00_km1, f_jp1_km1, f_jp2_km1, f_jp3_km1,
4711 f_jm2_k00, f_jm1_k00, f_j00_k00, f_jp1_k00, f_jp2_k00, f_jp3_k00,
4712 f_jm2_kp1, f_jm1_kp1, f_j00_kp1, f_jp1_kp1, f_jp2_kp1, f_jp3_kp1,
4713 f_jm2_kp2, f_jm1_kp2, f_j00_kp2, f_jp1_kp2, f_jp2_kp2, f_jp3_kp2,
4714 f_jm2_kp3, f_jm1_kp3, f_j00_kp3, f_jp1_kp3, f_jp2_kp3, f_jp3_kp3,
4715 f_km2 , f_km1 , f_k00 , f_kp1 , f_kp2 , f_kp3 ;
4716 float g_jm2_km2, g_jm1_km2, g_j00_km2, g_jp1_km2, g_jp2_km2, g_jp3_km2,
4717 g_jm2_km1, g_jm1_km1, g_j00_km1, g_jp1_km1, g_jp2_km1, g_jp3_km1,
4718 g_jm2_k00, g_jm1_k00, g_j00_k00, g_jp1_k00, g_jp2_k00, g_jp3_k00,
4719 g_jm2_kp1, g_jm1_kp1, g_j00_kp1, g_jp1_kp1, g_jp2_kp1, g_jp3_kp1,
4720 g_jm2_kp2, g_jm1_kp2, g_j00_kp2, g_jp1_kp2, g_jp2_kp2, g_jp3_kp2,
4721 g_jm2_kp3, g_jm1_kp3, g_j00_kp3, g_jp1_kp3, g_jp2_kp3, g_jp3_kp3,
4722 g_km2 , g_km1 , g_k00 , g_kp1 , g_kp2 , g_kp3 ;
4723 float h_jm2_km2, h_jm1_km2, h_j00_km2, h_jp1_km2, h_jp2_km2, h_jp3_km2,
4724 h_jm2_km1, h_jm1_km1, h_j00_km1, h_jp1_km1, h_jp2_km1, h_jp3_km1,
4725 h_jm2_k00, h_jm1_k00, h_j00_k00, h_jp1_k00, h_jp2_k00, h_jp3_k00,
4726 h_jm2_kp1, h_jm1_kp1, h_j00_kp1, h_jp1_kp1, h_jp2_kp1, h_jp3_kp1,
4727 h_jm2_kp2, h_jm1_kp2, h_j00_kp2, h_jp1_kp2, h_jp2_kp2, h_jp3_kp2,
4728 h_jm2_kp3, h_jm1_kp3, h_j00_kp3, h_jp1_kp3, h_jp2_kp3, h_jp3_kp3,
4729 h_km2 , h_km1 , h_k00 , h_kp1 , h_kp2 , h_kp3 ;
4730
4731 #pragma omp for
4732 for( pp=0 ; pp < npt ; pp++ ){
4733 MAT44_VEC( imat , xin[pp],yin[pp],zin[pp] , xx,yy,zz ) ; /* convert to index coords */
4734 /* find location in or out of dataset grid, and deal with external slopes if need be */
4735 aem=0 ; /* flag for usage of external slopes */
4736 if( xx < 0.0f ){ eex = xx ; ix = 0 ; fx = 0.0f; aem++; Exx=es_xd_xm; Eyx=es_yd_xm; Ezx=es_zd_xm; }
4737 else if( xx < nx1 ){ eex = 0.0f ; ix = (int)xx; fx = xx-ix; }
4738 else { eex = xx-nx1; ix = nx2 ; fx = 1.0f; aem++; Exx=es_xd_xp; Eyx=es_yd_xp; Ezx=es_zd_xp; }
4739 if( yy < 0.0f ){ eey = yy ; jy = 0 ; fy = 0.0f; aem++; Exy=es_xd_ym; Eyy=es_yd_ym; Ezy=es_zd_ym; }
4740 else if( yy < ny1 ){ eey = 0.0f ; jy = (int)yy; fy = yy-jy; }
4741 else { eey = yy-ny1; jy = ny2 ; fy = 1.0f; aem++; Exy=es_xd_yp; Eyy=es_yd_yp; Ezy=es_zd_yp; }
4742 if( zz < 0.0f ){ eez = zz ; kz = 0 ; fz = 0.0f; aem++; Exz=es_xd_zm; Eyz=es_yd_zm; Ezz=es_zd_zm; }
4743 else if( zz < nz1 ){ eez = 0.0f ; kz = (int)zz; fz = zz-kz; }
4744 else { eez = zz-nz1; kz = nz2 ; fz = 1.0f; aem++; Exz=es_xd_zp; Eyz=es_yd_zp; Ezz=es_zd_zp; }
4745 if( aem ){
4746 uex = Exx*eex + Exy*eey + Exz*eez ; /* eex = how far past edge of warp, in x-direction (etc.) */
4747 vex = Eyx*eex + Eyy*eey + Eyz*eez ; /* Eab = external slope for 'a' displacement, */
4748 wex = Ezx*eex + Ezy*eey + Ezz*eez ; /* along the 'b' direction, for a and b = x or y or z */
4749 uex *= dfac ; vex *= dfac ; wex *= dfac ;
4750 } else {
4751 uex = vex = wex = 0.0f ;
4752 }
4753
4754 /* special case of no interpolation needed */
4755
4756 if( fabsf(fx)+fabsf(fy)+fabsf(fz) < 0.00222f ){ /* 05 Nov 2014 */
4757 xut[pp] = xdar[IJK(ix,jy,kz)] + uex + xin[pp] ;
4758 yut[pp] = ydar[IJK(ix,jy,kz)] + vex + yin[pp] ;
4759 zut[pp] = zdar[IJK(ix,jy,kz)] + wex + zin[pp] ;
4760 continue ;
4761 }
4762
4763 /* compute indexes from which to interpolate (-2,-1,0,+1,+2,+3),
4764 but clipped to lie inside input image volume */
4765
4766 ix_m1 = ix-1 ; ix_00 = ix ; ix_p1 = ix+1 ; ix_p2 = ix+2 ;
4767 CLIP(ix_m1,nx1) ; CLIP(ix_00,nx1) ; CLIP(ix_p1,nx1) ; CLIP(ix_p2,nx1) ;
4768 ix_m2 = ix-2 ; ix_p3 = ix+3 ;
4769 CLIP(ix_m2,nx1) ; CLIP(ix_p3,nx1) ;
4770
4771 jy_m1 = jy-1 ; jy_00 = jy ; jy_p1 = jy+1 ; jy_p2 = jy+2 ;
4772 CLIP(jy_m1,ny1) ; CLIP(jy_00,ny1) ; CLIP(jy_p1,ny1) ; CLIP(jy_p2,ny1) ;
4773 jy_m2 = jy-2 ; jy_p3 = jy+3 ;
4774 CLIP(jy_m2,ny1) ; CLIP(jy_p3,ny1) ;
4775
4776 kz_m1 = kz-1 ; kz_00 = kz ; kz_p1 = kz+1 ; kz_p2 = kz+2 ;
4777 CLIP(kz_m1,nz1) ; CLIP(kz_00,nz1) ; CLIP(kz_p1,nz1) ; CLIP(kz_p2,nz1) ;
4778 kz_m2 = kz-2 ; kz_p3 = kz+3 ;
4779 CLIP(kz_m2,nz1) ; CLIP(kz_p3,nz1) ;
4780
4781 wt_m1 = Q_M1(fx)*dfac ; wt_00 = Q_00(fx)*dfac ; /* interpolation weights */
4782 wt_p1 = Q_P1(fx)*dfac ; wt_p2 = Q_P2(fx)*dfac ; /* in x-direction */
4783 wt_m2 = Q_M2(fx)*dfac ; wt_p3 = Q_P3(fx)*dfac ;
4784
4785 #undef XINT
4786 #define XINT(aaa,j,k) wt_m2*aaa[IJK(ix_m2,j,k)]+wt_m1*aaa[IJK(ix_m1,j,k)] \
4787 +wt_00*aaa[IJK(ix_00,j,k)]+wt_p1*aaa[IJK(ix_p1,j,k)] \
4788 +wt_p2*aaa[IJK(ix_p2,j,k)]+wt_p3*aaa[IJK(ix_p3,j,k)]
4789
4790 /* interpolate to location ix+fx at each jy,kz level */
4791
4792 f_jm2_km2 = XINT(xdar,jy_m2,kz_m2) ; f_jm1_km2 = XINT(xdar,jy_m1,kz_m2) ;
4793 f_j00_km2 = XINT(xdar,jy_00,kz_m2) ; f_jp1_km2 = XINT(xdar,jy_p1,kz_m2) ;
4794 f_jp2_km2 = XINT(xdar,jy_p2,kz_m2) ; f_jp3_km2 = XINT(xdar,jy_p3,kz_m2) ;
4795 f_jm2_km1 = XINT(xdar,jy_m2,kz_m1) ; f_jm1_km1 = XINT(xdar,jy_m1,kz_m1) ;
4796 f_j00_km1 = XINT(xdar,jy_00,kz_m1) ; f_jp1_km1 = XINT(xdar,jy_p1,kz_m1) ;
4797 f_jp2_km1 = XINT(xdar,jy_p2,kz_m1) ; f_jp3_km1 = XINT(xdar,jy_p3,kz_m1) ;
4798 f_jm2_k00 = XINT(xdar,jy_m2,kz_00) ; f_jm1_k00 = XINT(xdar,jy_m1,kz_00) ;
4799 f_j00_k00 = XINT(xdar,jy_00,kz_00) ; f_jp1_k00 = XINT(xdar,jy_p1,kz_00) ;
4800 f_jp2_k00 = XINT(xdar,jy_p2,kz_00) ; f_jp3_k00 = XINT(xdar,jy_p3,kz_00) ;
4801 f_jm2_kp1 = XINT(xdar,jy_m2,kz_p1) ; f_jm1_kp1 = XINT(xdar,jy_m1,kz_p1) ;
4802 f_j00_kp1 = XINT(xdar,jy_00,kz_p1) ; f_jp1_kp1 = XINT(xdar,jy_p1,kz_p1) ;
4803 f_jp2_kp1 = XINT(xdar,jy_p2,kz_p1) ; f_jp3_kp1 = XINT(xdar,jy_p3,kz_p1) ;
4804 f_jm2_kp2 = XINT(xdar,jy_m2,kz_p2) ; f_jm1_kp2 = XINT(xdar,jy_m1,kz_p2) ;
4805 f_j00_kp2 = XINT(xdar,jy_00,kz_p2) ; f_jp1_kp2 = XINT(xdar,jy_p1,kz_p2) ;
4806 f_jp2_kp2 = XINT(xdar,jy_p2,kz_p2) ; f_jp3_kp2 = XINT(xdar,jy_p3,kz_p2) ;
4807 f_jm2_kp3 = XINT(xdar,jy_m2,kz_p3) ; f_jm1_kp3 = XINT(xdar,jy_m1,kz_p3) ;
4808 f_j00_kp3 = XINT(xdar,jy_00,kz_p3) ; f_jp1_kp3 = XINT(xdar,jy_p1,kz_p3) ;
4809 f_jp2_kp3 = XINT(xdar,jy_p2,kz_p3) ; f_jp3_kp3 = XINT(xdar,jy_p3,kz_p3) ;
4810
4811 g_jm2_km2 = XINT(ydar,jy_m2,kz_m2) ; g_jm1_km2 = XINT(ydar,jy_m1,kz_m2) ;
4812 g_j00_km2 = XINT(ydar,jy_00,kz_m2) ; g_jp1_km2 = XINT(ydar,jy_p1,kz_m2) ;
4813 g_jp2_km2 = XINT(ydar,jy_p2,kz_m2) ; g_jp3_km2 = XINT(ydar,jy_p3,kz_m2) ;
4814 g_jm2_km1 = XINT(ydar,jy_m2,kz_m1) ; g_jm1_km1 = XINT(ydar,jy_m1,kz_m1) ;
4815 g_j00_km1 = XINT(ydar,jy_00,kz_m1) ; g_jp1_km1 = XINT(ydar,jy_p1,kz_m1) ;
4816 g_jp2_km1 = XINT(ydar,jy_p2,kz_m1) ; g_jp3_km1 = XINT(ydar,jy_p3,kz_m1) ;
4817 g_jm2_k00 = XINT(ydar,jy_m2,kz_00) ; g_jm1_k00 = XINT(ydar,jy_m1,kz_00) ;
4818 g_j00_k00 = XINT(ydar,jy_00,kz_00) ; g_jp1_k00 = XINT(ydar,jy_p1,kz_00) ;
4819 g_jp2_k00 = XINT(ydar,jy_p2,kz_00) ; g_jp3_k00 = XINT(ydar,jy_p3,kz_00) ;
4820 g_jm2_kp1 = XINT(ydar,jy_m2,kz_p1) ; g_jm1_kp1 = XINT(ydar,jy_m1,kz_p1) ;
4821 g_j00_kp1 = XINT(ydar,jy_00,kz_p1) ; g_jp1_kp1 = XINT(ydar,jy_p1,kz_p1) ;
4822 g_jp2_kp1 = XINT(ydar,jy_p2,kz_p1) ; g_jp3_kp1 = XINT(ydar,jy_p3,kz_p1) ;
4823 g_jm2_kp2 = XINT(ydar,jy_m2,kz_p2) ; g_jm1_kp2 = XINT(ydar,jy_m1,kz_p2) ;
4824 g_j00_kp2 = XINT(ydar,jy_00,kz_p2) ; g_jp1_kp2 = XINT(ydar,jy_p1,kz_p2) ;
4825 g_jp2_kp2 = XINT(ydar,jy_p2,kz_p2) ; g_jp3_kp2 = XINT(ydar,jy_p3,kz_p2) ;
4826 g_jm2_kp3 = XINT(ydar,jy_m2,kz_p3) ; g_jm1_kp3 = XINT(ydar,jy_m1,kz_p3) ;
4827 g_j00_kp3 = XINT(ydar,jy_00,kz_p3) ; g_jp1_kp3 = XINT(ydar,jy_p1,kz_p3) ;
4828 g_jp2_kp3 = XINT(ydar,jy_p2,kz_p3) ; g_jp3_kp3 = XINT(ydar,jy_p3,kz_p3) ;
4829
4830 h_jm2_km2 = XINT(zdar,jy_m2,kz_m2) ; h_jm1_km2 = XINT(zdar,jy_m1,kz_m2) ;
4831 h_j00_km2 = XINT(zdar,jy_00,kz_m2) ; h_jp1_km2 = XINT(zdar,jy_p1,kz_m2) ;
4832 h_jp2_km2 = XINT(zdar,jy_p2,kz_m2) ; h_jp3_km2 = XINT(zdar,jy_p3,kz_m2) ;
4833 h_jm2_km1 = XINT(zdar,jy_m2,kz_m1) ; h_jm1_km1 = XINT(zdar,jy_m1,kz_m1) ;
4834 h_j00_km1 = XINT(zdar,jy_00,kz_m1) ; h_jp1_km1 = XINT(zdar,jy_p1,kz_m1) ;
4835 h_jp2_km1 = XINT(zdar,jy_p2,kz_m1) ; h_jp3_km1 = XINT(zdar,jy_p3,kz_m1) ;
4836 h_jm2_k00 = XINT(zdar,jy_m2,kz_00) ; h_jm1_k00 = XINT(zdar,jy_m1,kz_00) ;
4837 h_j00_k00 = XINT(zdar,jy_00,kz_00) ; h_jp1_k00 = XINT(zdar,jy_p1,kz_00) ;
4838 h_jp2_k00 = XINT(zdar,jy_p2,kz_00) ; h_jp3_k00 = XINT(zdar,jy_p3,kz_00) ;
4839 h_jm2_kp1 = XINT(zdar,jy_m2,kz_p1) ; h_jm1_kp1 = XINT(zdar,jy_m1,kz_p1) ;
4840 h_j00_kp1 = XINT(zdar,jy_00,kz_p1) ; h_jp1_kp1 = XINT(zdar,jy_p1,kz_p1) ;
4841 h_jp2_kp1 = XINT(zdar,jy_p2,kz_p1) ; h_jp3_kp1 = XINT(zdar,jy_p3,kz_p1) ;
4842 h_jm2_kp2 = XINT(zdar,jy_m2,kz_p2) ; h_jm1_kp2 = XINT(zdar,jy_m1,kz_p2) ;
4843 h_j00_kp2 = XINT(zdar,jy_00,kz_p2) ; h_jp1_kp2 = XINT(zdar,jy_p1,kz_p2) ;
4844 h_jp2_kp2 = XINT(zdar,jy_p2,kz_p2) ; h_jp3_kp2 = XINT(zdar,jy_p3,kz_p2) ;
4845 h_jm2_kp3 = XINT(zdar,jy_m2,kz_p3) ; h_jm1_kp3 = XINT(zdar,jy_m1,kz_p3) ;
4846 h_j00_kp3 = XINT(zdar,jy_00,kz_p3) ; h_jp1_kp3 = XINT(zdar,jy_p1,kz_p3) ;
4847 h_jp2_kp3 = XINT(zdar,jy_p2,kz_p3) ; h_jp3_kp3 = XINT(zdar,jy_p3,kz_p3) ;
4848
4849 /* interpolate to jy+fy at each kz level */
4850
4851 wt_m1 = Q_M1(fy) ; wt_00 = Q_00(fy) ; wt_p1 = Q_P1(fy) ;
4852 wt_p2 = Q_P2(fy) ; wt_m2 = Q_M2(fy) ; wt_p3 = Q_P3(fy) ;
4853
4854 f_km2 = wt_m2 * f_jm2_km2 + wt_m1 * f_jm1_km2 + wt_00 * f_j00_km2
4855 + wt_p1 * f_jp1_km2 + wt_p2 * f_jp2_km2 + wt_p3 * f_jp3_km2 ;
4856 f_km1 = wt_m2 * f_jm2_km1 + wt_m1 * f_jm1_km1 + wt_00 * f_j00_km1
4857 + wt_p1 * f_jp1_km1 + wt_p2 * f_jp2_km1 + wt_p3 * f_jp3_km1 ;
4858 f_k00 = wt_m2 * f_jm2_k00 + wt_m1 * f_jm1_k00 + wt_00 * f_j00_k00
4859 + wt_p1 * f_jp1_k00 + wt_p2 * f_jp2_k00 + wt_p3 * f_jp3_k00 ;
4860 f_kp1 = wt_m2 * f_jm2_kp1 + wt_m1 * f_jm1_kp1 + wt_00 * f_j00_kp1
4861 + wt_p1 * f_jp1_kp1 + wt_p2 * f_jp2_kp1 + wt_p3 * f_jp3_kp1 ;
4862 f_kp2 = wt_m2 * f_jm2_kp2 + wt_m1 * f_jm1_kp2 + wt_00 * f_j00_kp2
4863 + wt_p1 * f_jp1_kp2 + wt_p2 * f_jp2_kp2 + wt_p3 * f_jp3_kp2 ;
4864 f_kp3 = wt_m2 * f_jm2_kp3 + wt_m1 * f_jm1_kp3 + wt_00 * f_j00_kp3
4865 + wt_p1 * f_jp1_kp3 + wt_p2 * f_jp2_kp3 + wt_p3 * f_jp3_kp3 ;
4866
4867 g_km2 = wt_m2 * g_jm2_km2 + wt_m1 * g_jm1_km2 + wt_00 * g_j00_km2
4868 + wt_p1 * g_jp1_km2 + wt_p2 * g_jp2_km2 + wt_p3 * g_jp3_km2 ;
4869 g_km1 = wt_m2 * g_jm2_km1 + wt_m1 * g_jm1_km1 + wt_00 * g_j00_km1
4870 + wt_p1 * g_jp1_km1 + wt_p2 * g_jp2_km1 + wt_p3 * g_jp3_km1 ;
4871 g_k00 = wt_m2 * g_jm2_k00 + wt_m1 * g_jm1_k00 + wt_00 * g_j00_k00
4872 + wt_p1 * g_jp1_k00 + wt_p2 * g_jp2_k00 + wt_p3 * g_jp3_k00 ;
4873 g_kp1 = wt_m2 * g_jm2_kp1 + wt_m1 * g_jm1_kp1 + wt_00 * g_j00_kp1
4874 + wt_p1 * g_jp1_kp1 + wt_p2 * g_jp2_kp1 + wt_p3 * g_jp3_kp1 ;
4875 g_kp2 = wt_m2 * g_jm2_kp2 + wt_m1 * g_jm1_kp2 + wt_00 * g_j00_kp2
4876 + wt_p1 * g_jp1_kp2 + wt_p2 * g_jp2_kp2 + wt_p3 * g_jp3_kp2 ;
4877 g_kp3 = wt_m2 * g_jm2_kp3 + wt_m1 * g_jm1_kp3 + wt_00 * g_j00_kp3
4878 + wt_p1 * g_jp1_kp3 + wt_p2 * g_jp2_kp3 + wt_p3 * g_jp3_kp3 ;
4879
4880 h_km2 = wt_m2 * h_jm2_km2 + wt_m1 * h_jm1_km2 + wt_00 * h_j00_km2
4881 + wt_p1 * h_jp1_km2 + wt_p2 * h_jp2_km2 + wt_p3 * h_jp3_km2 ;
4882 h_km1 = wt_m2 * h_jm2_km1 + wt_m1 * h_jm1_km1 + wt_00 * h_j00_km1
4883 + wt_p1 * h_jp1_km1 + wt_p2 * h_jp2_km1 + wt_p3 * h_jp3_km1 ;
4884 h_k00 = wt_m2 * h_jm2_k00 + wt_m1 * h_jm1_k00 + wt_00 * h_j00_k00
4885 + wt_p1 * h_jp1_k00 + wt_p2 * h_jp2_k00 + wt_p3 * h_jp3_k00 ;
4886 h_kp1 = wt_m2 * h_jm2_kp1 + wt_m1 * h_jm1_kp1 + wt_00 * h_j00_kp1
4887 + wt_p1 * h_jp1_kp1 + wt_p2 * h_jp2_kp1 + wt_p3 * h_jp3_kp1 ;
4888 h_kp2 = wt_m2 * h_jm2_kp2 + wt_m1 * h_jm1_kp2 + wt_00 * h_j00_kp2
4889 + wt_p1 * h_jp1_kp2 + wt_p2 * h_jp2_kp2 + wt_p3 * h_jp3_kp2 ;
4890 h_kp3 = wt_m2 * h_jm2_kp3 + wt_m1 * h_jm1_kp3 + wt_00 * h_j00_kp3
4891 + wt_p1 * h_jp1_kp3 + wt_p2 * h_jp2_kp3 + wt_p3 * h_jp3_kp3 ;
4892
4893 /* interpolate to kz+fz to get output */
4894
4895 wt_m1 = Q_M1(fz) ; wt_00 = Q_00(fz) ; wt_p1 = Q_P1(fz) ;
4896 wt_p2 = Q_P2(fz) ; wt_m2 = Q_M2(fz) ; wt_p3 = Q_P3(fz) ;
4897
4898 xut[pp] = wt_m2 * f_km2 + wt_m1 * f_km1 + wt_00 * f_k00
4899 + wt_p1 * f_kp1 + wt_p2 * f_kp2 + wt_p3 * f_kp3 + uex + xin[pp] ;
4900
4901 yut[pp] = wt_m2 * g_km2 + wt_m1 * g_km1 + wt_00 * g_k00
4902 + wt_p1 * g_kp1 + wt_p2 * g_kp2 + wt_p3 * g_kp3 + vex + yin[pp] ;
4903
4904 zut[pp] = wt_m2 * h_km2 + wt_m1 * h_km1 + wt_00 * h_k00
4905 + wt_p1 * h_kp1 + wt_p2 * h_kp2 + wt_p3 * h_kp3 + wex + zin[pp] ;
4906 }
4907
4908 } /* end OpenMP */
4909 AFNI_OMP_END ;
4910
4911 RETURN(npt) ;
4912 }
4913 #endif
4914
4915 /*----------------------------------------------------------------------------*/
4916 /* Forward warp a collection of DICOM xyz coordinates.
4917 Return value is number of points computed (should be npt).
4918 Negative is an error code.
4919 *//*--------------------------------------------------------------------------*/
4920
THD_nwarp_forward_xyz(THD_3dim_dataset * dset_nwarp,float dfac,int npt,float * xin,float * yin,float * zin,float * xut,float * yut,float * zut)4921 int THD_nwarp_forward_xyz( THD_3dim_dataset *dset_nwarp ,
4922 float dfac , int npt ,
4923 float *xin , float *yin , float *zin ,
4924 float *xut , float *yut , float *zut )
4925 {
4926 int pp ;
4927 floatvec *esv ;
4928 mat44 nwarp_cmat , nwarp_imat ;
4929 MRI_IMAGE *xdim , *ydim , *zdim ;
4930
4931 ENTRY("THD_nwarp_forward_xyz") ;
4932
4933 /* check inputs */
4934
4935 if( npt <= 0 ) RETURN(-1) ;
4936
4937 if( xin == NULL || yin == NULL || zin == NULL ||
4938 xut == NULL || yut == NULL || zut == NULL ) RETURN(-2) ;
4939
4940 /* get external slopes and check if dataset is any good */
4941
4942 esv = THD_nwarp_external_slopes( dset_nwarp ) ;
4943 /*** if( esv == NULL ) RETURN(-3) ; ***/
4944
4945 /* matrices */
4946
4947 nwarp_cmat = dset_nwarp->daxes->ijk_to_dicom ; /* convert ijk to xyz */
4948 nwarp_imat = MAT44_INV(nwarp_cmat) ; /* convert xyz to ijk */
4949
4950 xdim = DSET_BRICK(dset_nwarp,0) ; /* displacement images */
4951 ydim = DSET_BRICK(dset_nwarp,1) ;
4952 zdim = DSET_BRICK(dset_nwarp,2) ;
4953
4954 /* outsource all the actual work */
4955
4956 pp = THD_nwarp_im_xyz( xdim , ydim , zdim ,
4957 dfac , npt ,
4958 xin , yin , zin , xut , yut , zut , nwarp_imat , esv ) ;
4959
4960 RETURN(pp) ;
4961 }
4962
4963 /*----------------------------------------------------------------------------*/
4964 /* Inverse warp a collection of DICOM xyz coordinates.
4965 Return value is number of points computed (should be npt).
4966 Negative is an error code.
4967 This code is now just used as the initialization for the final iteration
4968 to find the best inverse point. The method used herein is backward
4969 tracing, nstep short steps.
4970 *//*--------------------------------------------------------------------------*/
4971
THD_nwarp_inverse_xyz_step(MRI_IMAGE * xdim,MRI_IMAGE * ydim,MRI_IMAGE * zdim,float dfac,int npt,float * xin,float * yin,float * zin,float * xut,float * yut,float * zut,mat44 imat,floatvec * esv,int nstep)4972 int THD_nwarp_inverse_xyz_step( MRI_IMAGE *xdim , MRI_IMAGE *ydim , MRI_IMAGE *zdim ,
4973 float dfac , int npt ,
4974 float *xin , float *yin , float *zin ,
4975 float *xut , float *yut , float *zut ,
4976 mat44 imat , floatvec *esv , int nstep )
4977 {
4978 float qfac ;
4979 float *qx , *qy , *qz ; int qq ;
4980
4981 ENTRY("THD_nwarp_inverse_xyz_step") ;
4982
4983 /* check inputs */
4984
4985 if( npt <= 0 ) RETURN(-1) ;
4986
4987 if( xin == NULL || yin == NULL || zin == NULL ||
4988 xut == NULL || yut == NULL || zut == NULL ) RETURN(-2) ;
4989
4990 if( nstep < 1 ) nstep = 1 ;
4991 qfac = -dfac / nstep ; /* short step size */
4992
4993 qx = (float *)malloc(sizeof(float)*npt) ; memcpy(qx,xin,sizeof(float)*npt) ;
4994 qy = (float *)malloc(sizeof(float)*npt) ; memcpy(qy,yin,sizeof(float)*npt) ;
4995 qz = (float *)malloc(sizeof(float)*npt) ; memcpy(qz,zin,sizeof(float)*npt) ;
4996
4997 for( qq=0 ; qq < nstep ; qq++ ){ /* take nstep short steps backwards */
4998 THD_nwarp_im_xyz( xdim , ydim , zdim ,
4999 qfac , npt ,
5000 qx , qy , qz , xut , yut , zut , imat , esv ) ;
5001 if( qq < nstep-1 ){
5002 memcpy(qx,xut,sizeof(float)*npt) ; /* copy output into input */
5003 memcpy(qy,yut,sizeof(float)*npt) ; /* for next step backwards */
5004 memcpy(qz,zut,sizeof(float)*npt) ;
5005 }
5006 }
5007
5008 free(qz); free(qy); free(qx) ;
5009 RETURN(npt) ;
5010 }
5011
5012 /*----------------------------------------------------------------------------*/
5013
5014 static double ww_xtarg , ww_ytarg , ww_ztarg ;
5015 static MRI_IMAGE *ww_xdim , *ww_ydim , *ww_zdim ;
5016 static mat44 ww_imat ;
5017 static floatvec *ww_esv ;
5018 static float ww_tol ;
5019
5020 /* Cost function for better pointwise inversion:
5021 forward warp input, see how far it is (squared) from target point */
5022
NW_invert_costfunc(int npar,double * par)5023 double NW_invert_costfunc( int npar , double *par )
5024 {
5025 float xin=par[0] , yin=par[1] , zin=par[2] , xut,yut,zut , a,b,c ;
5026
5027 THD_nwarp_im_xyz( ww_xdim , ww_ydim , ww_zdim ,
5028 1.0f , 1 ,
5029 &xin,&yin,&zin, &xut,&yut,&zut , ww_imat , ww_esv ) ;
5030
5031 a = ww_xtarg - xut ; b = ww_ytarg - yut ; c = ww_ztarg - zut ;
5032 return (double)(a*a+b*b+c*c) ;
5033 }
5034
5035 /*----------------------------------------------------------------------------*/
5036 /* Use Powell's NEWUOA to invert a warp (for one point) */
5037
5038 static int invert_xyz_nstep = 10 ;
5039
NW_invert_xyz(float xg,float yg,float zg,MRI_IMAGE * xdim,MRI_IMAGE * ydim,MRI_IMAGE * zdim,mat44 imat,floatvec * esv)5040 float_triple NW_invert_xyz( float xg , float yg , float zg ,
5041 MRI_IMAGE *xdim , MRI_IMAGE *ydim , MRI_IMAGE *zdim ,
5042 mat44 imat , floatvec *esv )
5043 {
5044 float xin,yin,zin , xut,yut,zut ;
5045 double par[3] ; float_triple xyz ;
5046 int pp ;
5047
5048 ENTRY("NW_invert_xyz") ;
5049
5050 /* initialize by backwards stream tracing with 10 steps */
5051
5052 xin = xg ; yin = yg ; zin = zg ;
5053
5054 THD_nwarp_inverse_xyz_step( xdim,ydim,zdim , -1.0f , 1 ,
5055 &xin,&yin,&zin , &xut,&yut,&zut , imat,esv , invert_xyz_nstep ) ;
5056
5057 /* setup and use Powell for the better result */
5058
5059 ww_xtarg = xg ; ww_ytarg = yg ; ww_ztarg = zg ;
5060 par[0] = xut ; par[1] = yut ; par[2] = zut ;
5061
5062 ww_xdim = xdim ; ww_ydim = ydim ; ww_zdim = zdim ; ww_imat = imat ; ww_esv = esv ;
5063
5064 pp = powell_newuoa( 3 , par , 0.555 , ww_tol , 66 , NW_invert_costfunc ) ;
5065 #if 0
5066 INFO_message("powell count = %d",pp) ;
5067 #endif
5068
5069 /* return the results */
5070
5071 xyz.a = par[0] ; xyz.b = par[1] ; xyz.c = par[2] ; RETURN(xyz) ;
5072 }
5073
5074 /*----------------------------------------------------------------------------*/
5075 /* Find the inverse warp of the set of input x,y,z coordinates.
5076 NOTE that dfac is not used in this implementation (assumed to be 1)!!!
5077 *//*--------------------------------------------------------------------------*/
5078
THD_nwarp_inverse_xyz(THD_3dim_dataset * dset_nwarp,float dfac,int npt,float * xin,float * yin,float * zin,float * xut,float * yut,float * zut)5079 int THD_nwarp_inverse_xyz( THD_3dim_dataset *dset_nwarp ,
5080 float dfac , int npt ,
5081 float *xin , float *yin , float *zin ,
5082 float *xut , float *yut , float *zut )
5083 {
5084 floatvec *esv ;
5085 mat44 nwarp_cmat , nwarp_imat ;
5086 MRI_IMAGE *xdim , *ydim , *zdim ;
5087 float vx,vy,vz ;
5088
5089 ENTRY("THD_nwarp_inverse_xyz") ;
5090
5091 /* check inputs */
5092
5093 if( npt <= 0 ) RETURN(-1) ;
5094
5095 if( xin == NULL || yin == NULL || zin == NULL ||
5096 xut == NULL || yut == NULL || zut == NULL ) RETURN(-2) ;
5097
5098 /* get external slopes and check if dataset is any good */
5099
5100 esv = THD_nwarp_external_slopes( dset_nwarp ) ;
5101 /*** if( esv == NULL ) RETURN(-3) ; ***/
5102
5103 /* matrices */
5104
5105 nwarp_cmat = dset_nwarp->daxes->ijk_to_dicom ; /* convert ijk to xyz */
5106 nwarp_imat = MAT44_INV(nwarp_cmat) ; /* convert xyz to ijk */
5107
5108 xdim = DSET_BRICK(dset_nwarp,0) ; /* displacement images */
5109 ydim = DSET_BRICK(dset_nwarp,1) ;
5110 zdim = DSET_BRICK(dset_nwarp,2) ;
5111
5112 vx = DSET_DX(dset_nwarp) ;
5113 vy = DSET_DY(dset_nwarp) ;
5114 vz = DSET_DZ(dset_nwarp) ;
5115
5116 #if 0 /*--- the old way, using only backward stream tracing ---*/
5117 { float *qx,*qy,*qz , *px,*py,*pz ; int qq , nstep ;
5118 float pqdif , vq , tol ;
5119 px = (float *)malloc(sizeof(float)*npt) ; qx = (float *)malloc(sizeof(float)*npt) ;
5120 py = (float *)malloc(sizeof(float)*npt) ; qy = (float *)malloc(sizeof(float)*npt) ;
5121 pz = (float *)malloc(sizeof(float)*npt) ; qz = (float *)malloc(sizeof(float)*npt) ;
5122
5123 tol = (vx*vx + vy*vy + vz*vz) * 1.e-5f ;
5124
5125 THD_nwarp_inverse_xyz_step( xdim,ydim,zdim , dfac,npt ,
5126 xin,yin,zin , qx,qy,qz , nwarp_imat,esv , 4 ) ;
5127
5128 for( nstep=6 ; nstep < 99 ; nstep = (int)rint(1.37*nstep) ){
5129 memcpy(px,qx,sizeof(float)*npt) ;
5130 memcpy(py,qy,sizeof(float)*npt) ;
5131 memcpy(pz,qz,sizeof(float)*npt) ;
5132 THD_nwarp_inverse_xyz_step( xdim,ydim,zdim , dfac,npt ,
5133 xin,yin,zin , qx,qy,qz , nwarp_imat,esv , nstep ) ;
5134 pqdif = 0.0f ;
5135 for( qq=0 ; qq < npt ; qq++ ){
5136 vx = qx[qq]-px[qq] ; vy = qy[qq]-py[qq] ; vz = qz[qq]-pz[qq] ;
5137 vq = vx*vx + vy*vy + vz*vz ;
5138 if( vq > pqdif ) pqdif = vq ;
5139 }
5140 if( pqdif <= tol ) break ;
5141 }
5142
5143 memcpy(xut,qx,sizeof(float)*npt) ;
5144 memcpy(yut,qy,sizeof(float)*npt) ;
5145 memcpy(zut,qz,sizeof(float)*npt) ;
5146 free(qz); free(qy); free(qx) ; free(pz); free(py); free(px) ;
5147 }
5148 #else /*--- the new way, using Powell's NEWUOA to solve for the inverse ---*/
5149 { float_triple xyz ; int qq ;
5150 ww_tol = (fabsf(vx)+fabsf(vy)+fabsf(vz)) * (0.0111f/3.0f) ;
5151 for( qq=0 ; qq < npt ; qq++ ){
5152 xyz = NW_invert_xyz( xin[qq] , yin[qq] , zin[qq] ,
5153 xdim , ydim , zdim , nwarp_imat , esv ) ;
5154 xut[qq] = xyz.a ; yut[qq] = xyz.b ; zut[qq] = xyz.c ;
5155 }
5156 }
5157 #endif
5158 RETURN(npt) ;
5159 }
5160
5161 #endif /*(C14)*/ /*###########################################################*/
5162
5163 #if 1
5164 /*============================================================================*/
5165 /* (C15) Functions to warp a dataset: a setup function and then the actual
5166 warper. Note that index warps are not really used in this computation! */
5167 /*============================================================================*/
5168
5169 /*----------------------------------------------------------------------------*/
5170 /* Take a warp dataset, and resample it to a new grid.
5171 The new grid is defined by a geometry string -- see function
5172 EDIT_get_geometry_string(). The format of the string is
5173 "MATRIX(b11,b12,b13,b14,b21,b22,b23,b24,b31,b32,b33,b34):nx,ny,nz"
5174 where the bij values comprise the 3x4 matrix that takes indexs (i,j,k)
5175 to coordinates (x,y,z).
5176 *//*--------------------------------------------------------------------------*/
5177
THD_nwarp_regrid(THD_3dim_dataset * inwarp,char * geomstring)5178 THD_3dim_dataset * THD_nwarp_regrid( THD_3dim_dataset *inwarp, char *geomstring )
5179 {
5180 THD_3dim_dataset *outwarp=NULL ;
5181 float *xdout,*ydout,*zdout , *xin,*yin,*zin ;
5182 int nx,ny,nz,nxy,nxyz ;
5183 mat44 cmat_out ;
5184
5185 ENTRY("THD_nwarp_regrid") ;
5186
5187 /*-- some simple checks for user sanity --*/
5188
5189 if( !ISVALID_DSET(inwarp) || DSET_NVALS(inwarp) < 3 || geomstring == NULL )
5190 RETURN(NULL) ;
5191
5192 DSET_load(inwarp) ; if( !DSET_LOADED(inwarp) ) RETURN(NULL) ;
5193
5194 outwarp = EDIT_geometry_constructor( geomstring , "RegridWarp") ;
5195 if( outwarp == NULL ) RETURN(NULL) ;
5196
5197 /*-- patch up the output dataset --*/
5198
5199 EDIT_dset_items( outwarp ,
5200 ADN_nvals , 3 ,
5201 ADN_datum_all , MRI_float ,
5202 ADN_none ) ;
5203 EDIT_substitute_brick( outwarp , 0 , MRI_float , NULL ) ;
5204 EDIT_substitute_brick( outwarp , 1 , MRI_float , NULL ) ;
5205 EDIT_substitute_brick( outwarp , 2 , MRI_float , NULL ) ;
5206 xdout = DSET_BRICK_ARRAY(outwarp,0) ; nx = DSET_NX(outwarp) ;
5207 ydout = DSET_BRICK_ARRAY(outwarp,1) ; ny = DSET_NY(outwarp) ; nxy = nx*ny ;
5208 zdout = DSET_BRICK_ARRAY(outwarp,2) ; nz = DSET_NZ(outwarp) ; nxyz = nxy*nz;
5209 cmat_out = outwarp->daxes->ijk_to_dicom_real ;
5210
5211 /*-- load xyz coordinates of output grid points --*/
5212
5213 xin = (float *)malloc(sizeof(float)*nxyz) ;
5214 yin = (float *)malloc(sizeof(float)*nxyz) ;
5215 zin = (float *)malloc(sizeof(float)*nxyz) ;
5216
5217 AFNI_OMP_START ;
5218 #pragma omp parallel if( nxyz > 6666 )
5219 { int ii,jj,kk,ijk ;
5220 #pragma omp for
5221 for( ijk=0 ; ijk < nxyz ; ijk++ ){
5222 ii = ijk % nx ; kk = ijk / nxy ; jj = (ijk-kk*nxy) / nx ;
5223 MAT44_VEC(cmat_out,ii,jj,kk,xin[ijk],yin[ijk],zin[ijk]) ;
5224 }
5225 }
5226
5227 /*-- compute forward warp of each point --*/
5228
5229 (void)THD_nwarp_forward_xyz( inwarp , 1.0f , nxyz ,
5230 xin , yin , zin ,
5231 xdout , ydout , zdout ) ;
5232
5233 /*-- subtract off the input xyz coords --*/
5234
5235 AFNI_OMP_START ;
5236 #pragma omp parallel if( nxyz > 6666 )
5237 { int ijk ;
5238 #pragma omp for
5239 for( ijk=0 ; ijk < nxyz ; ijk++ ){
5240 xdout[ijk] -= xin[ijk] ; ydout[ijk] -= yin[ijk] ; zdout[ijk] -= zin[ijk] ;
5241 }
5242 }
5243 AFNI_OMP_END ;
5244
5245 /*-- toss the trash, and then it's Miller Time (no endorsement implied) --*/
5246
5247 free(zin) ; free(yin) ; free(xin) ;
5248 DSET_superlock(outwarp) ;
5249 RETURN(outwarp) ;
5250 }
5251
5252 /*----------------------------------------------------------------------------*/
5253 /* Do the setup to warp images given
5254 bimar = array of DICOM (x,y,z) deltas
5255 = 3D warp displacment function in base image space;
5256 use_amat = if nonzero, use the amat matrix
5257 amat = matrix to transform (x,y,z) AFTER the bimar deltas
5258 cmat_bim = matrix to transform indexes (ib,jb,kb) to DICOM (xb,yb,zb),
5259 in base image space
5260 cmat_src = similar matrix for source dataset (to be warped from)
5261 cmat_out = similar matrix for output dataset (to be warped to);
5262 for most purposes, cmat_out will either be cmat_bim
5263 (to get the source image warped to the base grid)
5264 or will be cmat_src (warp the source image on its own grid)
5265
5266 foreach (io,jo,ko) in output dataset do {
5267 (xo,yo,zo) = [cmat_out](io,jo,ko) -- transform indexes to coords
5268 (ib,jb,kb) = inv[cmat_bim](xo,yo,zo) -- transform coords to indexes
5269 (xs,ys,zs) = (xo,yo,zo) -- compute warped coords
5270 + bimar interpolated at (ib,jb,kb)
5271 (is,js,ks) = inv[cmat_src](xs,ys,zs) -- compute warped indexes
5272 }
5273
5274 The output is the array of images of (is,js,ks) = indexes in the source
5275 dataset, for each point to interpolate to in the output dataset (io,jo,ko).
5276 (N.B.: this is NOT an IndexWarp3D struct!).
5277 This set of images can be used, in turn, to interpolate a src grid image
5278 to an output grid warped image via THD_interp_floatim().
5279 *//*--------------------------------------------------------------------------*/
5280
THD_setup_nwarp(MRI_IMARR * bimar,int use_amat,mat44 amat,mat44 cmat_bim,int incode,float wfac,mat44 cmat_src,mat44 cmat_out,int nx_out,int ny_out,int nz_out)5281 MRI_IMARR * THD_setup_nwarp( MRI_IMARR *bimar,
5282 int use_amat , mat44 amat ,
5283 mat44 cmat_bim ,
5284 int incode , float wfac ,
5285 mat44 cmat_src ,
5286 mat44 cmat_out ,
5287 int nx_out , int ny_out , int nz_out )
5288 {
5289 int nx,ny,nz,nxy,nxyz ;
5290 float *xp, *yp, *zp , *wx, *wy, *wz ;
5291 MRI_IMAGE *wxim, *wyim, *wzim ; MRI_IMARR *wimar ; mat44 tmat ;
5292
5293 ENTRY("THD_setup_nwarp") ;
5294
5295 nx = nx_out ; ny = ny_out ; nz = nz_out ; nxy = nx*ny ; nxyz = nxy*nz ;
5296
5297 /* make space for indexes/coordinates in output space */
5298
5299 xp = (float *)malloc(sizeof(float)*nxyz) ;
5300 yp = (float *)malloc(sizeof(float)*nxyz) ;
5301 zp = (float *)malloc(sizeof(float)*nxyz) ;
5302
5303 /* compute indexes of each point in output image
5304 (the _out grid) in the warp space (the _bim grid) */
5305
5306 if( !MAT44_FLEQ(cmat_bim,cmat_out) ){ /* output & base grids not the same */
5307 mat44 imat_out_to_bim ;
5308
5309 /* cmat_out takes (i,j,k):out to (x,y,z)
5310 tmat takes (x,y,z) to (i,j,k):base
5311 so imat_out_to_bim takes (i,j,k):out to (i,j,k):base */
5312
5313 tmat = MAT44_INV(cmat_bim) ; imat_out_to_bim = MAT44_MUL(tmat,cmat_out) ;
5314
5315 AFNI_OMP_START ;
5316 #pragma omp parallel if( nxyz > 1111 )
5317 { int qq , ii,jj,kk ;
5318 #pragma omp for
5319 for( qq=0 ; qq < nxyz ; qq++ ){
5320 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
5321 MAT44_VEC( imat_out_to_bim , ii,jj,kk , xp[qq],yp[qq],zp[qq] ) ;
5322 }
5323 }
5324 AFNI_OMP_END ;
5325
5326 } else { /* case where cmat_bim and cmat_out are equal */
5327 /* so (i,j,k):out == (i,j,k):base */
5328 AFNI_OMP_START ;
5329 #pragma omp parallel if( nxyz > 1111 )
5330 { int qq , ii,jj,kk ;
5331 #pragma omp for
5332 for( qq=0 ; qq < nxyz ; qq++ ){
5333 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
5334 xp[qq] = ii ; yp[qq] = jj ; zp[qq] = kk ;
5335 }
5336 }
5337 AFNI_OMP_END ;
5338
5339 }
5340
5341 /* now interpolate the warp delta volumes from the bim grid
5342 to the out grid, using the indexes computed just above;
5343 note that these deltas are still in mm, not in indexes! */
5344
5345 wxim = mri_new_vol(nx,ny,nz,MRI_float) ; wx = MRI_FLOAT_PTR(wxim) ;
5346 wyim = mri_new_vol(nx,ny,nz,MRI_float) ; wy = MRI_FLOAT_PTR(wyim) ;
5347 wzim = mri_new_vol(nx,ny,nz,MRI_float) ; wz = MRI_FLOAT_PTR(wzim) ;
5348
5349 THD_interp_floatim( IMARR_SUBIM(bimar,0), nxyz,xp,yp,zp, incode, wx ) ;
5350 THD_interp_floatim( IMARR_SUBIM(bimar,1), nxyz,xp,yp,zp, incode, wy ) ;
5351 THD_interp_floatim( IMARR_SUBIM(bimar,2), nxyz,xp,yp,zp, incode, wz ) ;
5352
5353 /* affinely transform these deltas, if ordered to use amat we are */
5354
5355 if( use_amat ){
5356 mat44 aamat=amat , aimat=amat , iimat ;
5357 /* aimat = amat - Identity */
5358 aimat.m[0][0] -= 1.0f ; aimat.m[1][1] -= 1.0f ; aimat.m[2][2] -= 1.0f ;
5359 /* iimat = matrix that takes (i,j,k) to (x,y,z) and then transforms via aimat */
5360 iimat = MAT44_MUL(aimat,cmat_out) ;
5361 AFNI_OMP_START ;
5362 #pragma omp parallel if( nxyz > 1111 )
5363 { int qq , ii,jj,kk ; float xb,yb,zb , xm,ym,zm ;
5364 #pragma omp for
5365 for( qq=0 ; qq < nxyz ; qq++ ){
5366 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
5367 MAT33_VEC(aamat,wx[qq],wy[qq],wz[qq],xb,yb,zb) ; /* just the 3x3 part */
5368 MAT44_VEC(iimat,ii ,jj ,kk ,xm,ym,zm) ; /* all of the matrix */
5369 wx[qq] = xb+xm ; wy[qq] = yb+ym ; wz[qq] = zb+zm ; /* add pieces parts */
5370 }
5371 }
5372 AFNI_OMP_END ;
5373 }
5374
5375 free(zp) ; free(yp) ; free(xp) ;
5376
5377 /* now convert to index warp from src to out space */
5378
5379 tmat = MAT44_INV(cmat_src) ; /* takes (x,y,z) to (i,j,k) in src space */
5380
5381 AFNI_OMP_START ;
5382 #pragma omp parallel if( nxyz > 1111 )
5383 { int qq,ii,jj,kk ; float xx,yy,zz , fac ;
5384 fac = (wfac == 0.0f) ? 1.0f : wfac ;
5385 #pragma omp for
5386 for( qq=0 ; qq < nxyz ; qq++ ){
5387 ii = qq % nx ; kk = qq / nxy ; jj = (qq-kk*nxy) / nx ;
5388 MAT44_VEC( cmat_out , ii,jj,kk , xx,yy,zz ) ; /* compute (xo,yo,zo) */
5389 xx += fac*wx[qq]; yy += fac*wy[qq]; zz += fac*wz[qq]; /* add in the deltas */
5390 MAT44_VEC( tmat, xx,yy,zz, wx[qq],wy[qq],wz[qq] ) ; /* ==> to (is,js,ks) */
5391 }
5392 }
5393 AFNI_OMP_END ;
5394
5395 /* package results for delivery to the (ab)user */
5396
5397 INIT_IMARR(wimar) ;
5398 ADDTO_IMARR(wimar,wxim) ; ADDTO_IMARR(wimar,wyim) ; ADDTO_IMARR(wimar,wzim) ;
5399
5400 RETURN(wimar) ;
5401 }
5402
5403 /*----------------------------------------------------------------------------*/
5404 /* Warp a dataset dset_src using the dset_nwarp dataset to control the
5405 displacements, patterning the output after dset_mast.
5406 *//*--------------------------------------------------------------------------*/
5407
THD_nwarp_dataset(THD_3dim_dataset * dset_nwarp,THD_3dim_dataset * dset_src,THD_3dim_dataset * dset_mast,char * prefix,int wincode,int dincode,float dxyz_mast,float wfac,int nvlim,MRI_IMAGE * amatim)5408 THD_3dim_dataset * THD_nwarp_dataset( THD_3dim_dataset *dset_nwarp ,
5409 THD_3dim_dataset *dset_src ,
5410 THD_3dim_dataset *dset_mast ,
5411 char *prefix , int wincode , int dincode ,
5412 float dxyz_mast, float wfac, int nvlim,
5413 MRI_IMAGE *amatim )
5414 {
5415 MRI_IMARR *imar_nwarp=NULL , *im_src=NULL ;
5416 mat44 src_cmat, nwarp_cmat, mast_cmat ;
5417 THD_3dim_dataset *dset_out , *dset_qwarp ;
5418 MRI_IMAGE *fim , *wim ; float *ip=NULL,*jp=NULL,*kp=NULL ;
5419 int nx,ny,nz,nxyz , nvals , kk,iv , next ;
5420 float *amatar=NULL ; int nxa=0,nya=0 ; mat44 amat ;
5421 int vp ;
5422
5423 ENTRY("THD_nwarp_dataset") ;
5424
5425 /*-------- check inputs to see if the user is completely demented ---------*/
5426
5427 if( dset_nwarp == NULL || dset_src == NULL ) RETURN(NULL) ;
5428
5429 DSET_load(dset_nwarp) ; if( !DSET_LOADED(dset_nwarp) ) RETURN(NULL) ;
5430 DSET_load(dset_src) ; if( !DSET_LOADED(dset_src) ) RETURN(NULL) ;
5431
5432 nvals = DSET_NVALS(dset_src) ; if( nvals > nvlim && nvlim > 0 ) nvals = nvlim ;
5433
5434 LOAD_IDENT_MAT44(amat) ;
5435 if( amatim != NULL ){
5436 amatar = MRI_FLOAT_PTR(amatim) ;
5437 nxa = amatim->nx ;
5438 nya = amatim->ny ;
5439 if( nxa < 12 ){ nya = 0 ; amatar = NULL ; } /* this is bad */
5440 else if( nya > nvals ){ nya = nvals ; } /* this is OK */
5441 }
5442
5443 if( dset_mast == NULL ) dset_mast = dset_src ; /* default master */
5444
5445 if( prefix == NULL || *prefix == '\0' ){ /* fake up a prefix */
5446 char *cpt ;
5447 prefix = (char *)malloc(sizeof(char)*THD_MAX_NAME) ;
5448 strcpy( prefix , DSET_PREFIX(dset_src) ) ;
5449 cpt = strcasestr(prefix,".nii") ; if( cpt != NULL ) *cpt = '\0' ;
5450 strcat( prefix , "_nwarp" ) ; if( cpt != NULL ) strcat(prefix,".nii") ;
5451 }
5452
5453 /*----- extend the warp dataset to allow for outliers [15 Apr 2014] -----*/
5454 /*........ ((( this is kind of arbitrary, but helps sometimes ))) .......*/
5455
5456 next = (int)AFNI_numenv("AFNI_NWARP_EXTEND") ; /* 18 Aug 2014 */
5457 if( next < 32 ) next = 32 ;
5458
5459 dset_qwarp = THD_nwarp_extend( dset_nwarp , next,next,next,next,next,next ) ;
5460 if( dset_qwarp == NULL ){ /* should never happen */
5461 ERROR_message("Can't extend nwarp dataset ?!?") ; RETURN(NULL) ;
5462 }
5463
5464 /*---------- manufacture the empty shell of the output dataset ----------*/
5465
5466 if( !ISVALID_MAT44(dset_src->daxes->ijk_to_dicom) )
5467 THD_daxes_to_mat44(dset_src->daxes) ;
5468 src_cmat = dset_src->daxes->ijk_to_dicom ;
5469
5470 if( !ISVALID_MAT44(dset_qwarp->daxes->ijk_to_dicom) )
5471 THD_daxes_to_mat44(dset_qwarp->daxes) ;
5472 nwarp_cmat = dset_qwarp->daxes->ijk_to_dicom ;
5473
5474 if( dxyz_mast > 0.0f ){
5475 THD_3dim_dataset *qset ; double dxyz = (double)dxyz_mast ;
5476 qset = r_new_resam_dset( dset_mast , NULL ,
5477 dxyz,dxyz,dxyz ,
5478 NULL , RESAM_NN_TYPE , NULL , 0 , 0) ;
5479 if( qset != NULL ){
5480 dset_mast = qset ;
5481 THD_daxes_to_mat44(dset_mast->daxes) ;
5482 }
5483 }
5484
5485 if( !ISVALID_MAT44(dset_mast->daxes->ijk_to_dicom) ) /* make sure have */
5486 THD_daxes_to_mat44(dset_mast->daxes) ; /* index-to-DICOM matrix */
5487
5488 mast_cmat = dset_mast->daxes->ijk_to_dicom ;
5489
5490 dset_out = EDIT_empty_copy( dset_mast ) ; /* create the output dataset! */
5491 EDIT_dset_items( dset_out , /* and patch it up */
5492 ADN_prefix , prefix ,
5493 ADN_nvals , nvals ,
5494 ADN_datum_all , MRI_float ,
5495 ADN_none ) ;
5496 if( DSET_NUM_TIMES(dset_src) > 1 && nvals > 1 )
5497 EDIT_dset_items( dset_out ,
5498 ADN_ntt , nvals ,
5499 ADN_ttdel , DSET_TR(dset_src) ,
5500 ADN_tunits, UNITS_SEC_TYPE ,
5501 ADN_nsl , 0 ,
5502 ADN_none ) ;
5503 else
5504 EDIT_dset_items( dset_out ,
5505 ADN_func_type , ISANAT(dset_out) ? ANAT_BUCK_TYPE
5506 : FUNC_BUCK_TYPE ,
5507 ADN_none ) ;
5508
5509 /* copy brick info into output */
5510
5511 THD_copy_datablock_auxdata( dset_src->dblk , dset_out->dblk ) ;
5512 for( kk=0 ; kk < nvals ; kk++ )
5513 EDIT_BRICK_FACTOR(dset_out,kk,0.0) ;
5514
5515 THD_daxes_to_mat44(dset_out->daxes) ; /* save coord transforms */
5516
5517 /*----- create warping indexes from warp dataset -----*/
5518
5519 INIT_IMARR(imar_nwarp) ;
5520 fim = THD_extract_float_brick(0,dset_qwarp) ; ADDTO_IMARR(imar_nwarp,fim) ;
5521 fim = THD_extract_float_brick(1,dset_qwarp) ; ADDTO_IMARR(imar_nwarp,fim) ;
5522 fim = THD_extract_float_brick(2,dset_qwarp) ; ADDTO_IMARR(imar_nwarp,fim) ;
5523 DSET_delete(dset_qwarp) ;
5524
5525 nx = DSET_NX(dset_out) ;
5526 ny = DSET_NY(dset_out) ;
5527 nz = DSET_NZ(dset_out) ; nxyz = nx*ny*nz ;
5528
5529 /* the actual work of setting up the warp (for all sub-bricks) */
5530
5531 if( amatar == NULL || nya == 1 ){
5532 if( amatar != NULL ){
5533 LOAD_MAT44(amat,amatar[0],amatar[1],amatar[ 2],amatar[ 3],
5534 amatar[4],amatar[5],amatar[ 6],amatar[ 7],
5535 amatar[8],amatar[9],amatar[10],amatar[11]) ;
5536 }
5537 im_src = THD_setup_nwarp( imar_nwarp, nya,amat ,
5538 nwarp_cmat, wincode , wfac ,
5539 src_cmat , mast_cmat , nx , ny , nz ) ;
5540 ip = MRI_FLOAT_PTR( IMARR_SUBIM(im_src,0) ) ;
5541 jp = MRI_FLOAT_PTR( IMARR_SUBIM(im_src,1) ) ;
5542 kp = MRI_FLOAT_PTR( IMARR_SUBIM(im_src,2) ) ;
5543 DESTROY_IMARR(imar_nwarp) ;
5544 }
5545
5546 /*----- warp each sub-brick of the input -----*/
5547
5548 vp = 1 + (nvals/40) ;
5549 for( iv=0 ; iv < nvals ; iv++ ){
5550 fim = THD_extract_float_brick(iv,dset_src) ; DSET_unload_one(dset_src,iv) ;
5551 wim = mri_new_vol(nx,ny,nz,MRI_float) ;
5552 if( nya > 1 ){ /* warp setup for just this sub-brick */
5553 int im = nxa * MIN(iv,nya-1) ;
5554 LOAD_MAT44(amat,amatar[0+im],amatar[1+im],amatar[ 2+im],amatar[ 3+im],
5555 amatar[4+im],amatar[5+im],amatar[ 6+im],amatar[ 7+im],
5556 amatar[8+im],amatar[9+im],amatar[10+im],amatar[11+im]) ;
5557 im_src = THD_setup_nwarp( imar_nwarp, nya,amat ,
5558 nwarp_cmat, wincode, wfac ,
5559 src_cmat , mast_cmat , nx , ny , nz ) ;
5560 ip = MRI_FLOAT_PTR( IMARR_SUBIM(im_src,0) ) ;
5561 jp = MRI_FLOAT_PTR( IMARR_SUBIM(im_src,1) ) ;
5562 kp = MRI_FLOAT_PTR( IMARR_SUBIM(im_src,2) ) ;
5563 }
5564 if( verb_nww && iv == 0 ) fprintf(stderr,"++ Warping dataset: ") ;
5565 THD_interp_floatim( fim, nxyz,ip,jp,kp, dincode, MRI_FLOAT_PTR(wim) ) ;
5566 #if 0
5567 if( MRI_HIGHORDER(dincode) ){ /* clipping */
5568 double_pair fmm = mri_minmax(fim) ;
5569 float fb=(float)fmm.a , ft=(float)fmm.b ; int qq ;
5570 float *war=MRI_FLOAT_PTR(wim) ;
5571 for( qq=0 ; qq < wim->nvox ; qq++ ){
5572 if( war[qq] < fb ) war[qq] = fb ; else if( war[qq] > ft ) war[qq] = ft ;
5573 }
5574 }
5575 #endif
5576 EDIT_substitute_brick( dset_out , iv , MRI_float , MRI_FLOAT_PTR(wim) ) ;
5577 mri_clear_and_free(wim) ; mri_free(fim) ;
5578 if( nya > 1 ){ DESTROY_IMARR(im_src) ; } /* will be re-computed */
5579 if( verb_nww && iv%vp == 0 ) fprintf(stderr,".") ;
5580 }
5581
5582 if( imar_nwarp != NULL ) DESTROY_IMARR(imar_nwarp) ;
5583 if( im_src != NULL ) DESTROY_IMARR(im_src) ;
5584 DSET_unload(dset_src) ;
5585 if( verb_nww ) fprintf(stderr,"\n") ;
5586 RETURN(dset_out) ;
5587 }
5588
5589 /*----------------------------------------------------------------------------*/
5590 /* This function is used to tell THD_nwarp_dataset_array() to save
5591 all generated warps into datasets (for debugging purposes, mostly)
5592 *//*--------------------------------------------------------------------------*/
5593
5594 static char *QWARP_prefix = NULL ;
5595
THD_set_nwarp_apply_prefix(char * ppp)5596 void THD_set_nwarp_apply_prefix( char *ppp )
5597 {
5598 ENTRY("set_nwarp_apply_prefix") ;
5599 if( QWARP_prefix != NULL ){
5600 free(QWARP_prefix) ;
5601 QWARP_prefix = NULL ;
5602 }
5603 if( THD_filename_ok(ppp) ){
5604 QWARP_prefix = strdup(ppp) ;
5605 }
5606 EXRETURN ;
5607 }
5608
5609 /*----------------------------------------------------------------------------*/
5610 /* Warp a dataset dset_src using the Nwarp_catlist nwc to control the
5611 displacements, patterning the output after dset_mast. [21 Oct 2014]
5612 *//*--------------------------------------------------------------------------*/
5613
THD_nwarp_dataset_array(Nwarp_catlist * nwc,THD_3dim_dataset_array * dset_src,THD_3dim_dataset * dset_mast,char ** prefix,int wincode,int dincode,float dxyz_mast,float wfac,int nvlim)5614 THD_3dim_dataset_array * THD_nwarp_dataset_array(
5615 Nwarp_catlist *nwc ,
5616 THD_3dim_dataset_array *dset_src ,
5617 THD_3dim_dataset *dset_mast ,
5618 char **prefix ,
5619 int wincode, int dincode,
5620 float dxyz_mast, float wfac, int nvlim )
5621 {
5622 THD_3dim_dataset_array *dset_out=NULL ;
5623 MRI_IMARR *imar_nwarp=NULL , *imar_src=NULL ;
5624 mat44 src_cmat, nwarp_cmat, mast_cmat , amat ;
5625 THD_3dim_dataset *dset_qwarp=NULL , *dset_nwarp=NULL , *dset_sss=NULL,*dset_ooo=NULL;
5626 MRI_IMAGE *fim=NULL , *wim=NULL ; float *ip=NULL,*jp=NULL,*kp=NULL ;
5627 int nx,ny,nz,nxyz , nvals , kk,iv , next , kds,numds,nvmax=0,newprefix=0 ;
5628 int vp , reuse=0 ;
5629 char *gs=NULL,*hs=NULL ;
5630
5631 ENTRY("THD_nwarp_dataset_array") ;
5632
5633 /*-------- check inputs to see if the user is completely demented ---------*/
5634
5635 if( nwc == NULL || dset_src == NULL ) RETURN(NULL) ;
5636
5637 numds = dset_src->num ; if( numds <= 0 ) RETURN(NULL) ;
5638
5639 /* some elementary setup and checking stuff */
5640
5641 for( kds=0 ; kds < numds ; kds++ ){ /* loop over input datasets */
5642 dset_sss = DSET_IN_3DARR(dset_src,kds) ;
5643 if( verb_nww > 1 )
5644 INFO_message("Loading '%s' for warping",DSET_HEADNAME(dset_sss)) ;
5645 DSET_load(dset_sss) ;
5646 if( verb_nww > 1 )
5647 ININFO_message(" DSET_LOAD returns") ;
5648 if( !DSET_LOADED(dset_sss) ){
5649 ERROR_message("Can't load dataset '%s' for warping",DSET_HEADNAME(dset_sss)) ;
5650 RETURN(NULL) ;
5651 }
5652 if( kds == 0 ){ /* get first dataset's geometry */
5653 gs = EDIT_get_geometry_string(dset_sss) ;
5654 if( verb_nww > 1 )
5655 ININFO_message(" dataset geometry = %s",gs) ;
5656 } else { /* check later datasets to see if they match */
5657 hs = EDIT_get_geometry_string(dset_sss) ;
5658 if( EDIT_geometry_string_diff(gs,hs) > 0.01f ){
5659 ERROR_message("Can't warp multiple datasets because they have different grids!") ;
5660 free(hs) ; free(gs) ; RETURN(NULL) ;
5661 }
5662 free(hs) ; /* don't need this any more */
5663 }
5664 /* find the longest dataset ("time" axis, that is) */
5665 nvals = DSET_NVALS(dset_sss) ; if( nvals > nvmax ) nvmax = nvals ;
5666 if( !ISVALID_MAT44(dset_sss->daxes->ijk_to_dicom) )
5667 THD_daxes_to_mat44(dset_sss->daxes) ;
5668 NI_sleep(1) ;
5669 }
5670 free(gs) ; gs = hs = NULL ;
5671
5672 if( nvmax > nvlim && nvlim > 0 ) nvmax = nvlim ;
5673
5674 if( dset_mast == NULL ) dset_mast = DSET_IN_3DARR(dset_src,0) ; /* default master */
5675
5676 src_cmat = DSET_IN_3DARR(dset_src,0)->daxes->ijk_to_dicom ; /* source coordinate matrix */
5677
5678 if( prefix == NULL ){ /* fake up some prefixes */
5679 char *cpt ;
5680 prefix = (char **)malloc(sizeof(char *)*numds) ; newprefix = 1 ;
5681 for( kds=0 ; kds < numds ; kds++ ){
5682 prefix[kds] = (char *)malloc(sizeof(char)*THD_MAX_NAME) ;
5683 dset_sss = DSET_IN_3DARR(dset_src,kds) ;
5684 strcpy( prefix[kds] , DSET_PREFIX(dset_sss) ) ;
5685 cpt = strcasestr(prefix[kds],".nii") ; if( cpt != NULL ) *cpt = '\0' ;
5686 strcat( prefix[kds] , "_Nwarp" ) ; if( cpt != NULL ) strcat(prefix[kds],".nii") ;
5687 }
5688 }
5689
5690 /*----- for creating warps and dataset sub-bricks -----*/
5691
5692 next = (int)AFNI_numenv("AFNI_NWARP_EXTEND") ; /* 18 Aug 2014 */
5693 #if 0
5694 if( next < 32 ) next = 32 ; /* warp extension, just for luck */
5695 #endif
5696
5697 #ifdef DEBUG_CATLIST
5698 MEMORY_CHECK("enter THD_nwarp_dataset_array") ;
5699 #endif
5700
5701 if( dxyz_mast > 0.0f ){ /* if altering output grid spacings */
5702 THD_3dim_dataset *qset ; double dxyz = (double)dxyz_mast ;
5703 qset = r_new_resam_dset( dset_mast , NULL ,
5704 dxyz,dxyz,dxyz ,
5705 NULL , RESAM_NN_TYPE , NULL , 0 , 0) ;
5706 if( qset != NULL ){
5707 dset_mast = qset ;
5708 THD_daxes_to_mat44(dset_mast->daxes) ;
5709 }
5710 }
5711
5712 if( !ISVALID_MAT44(dset_mast->daxes->ijk_to_dicom) ) /* make sure have */
5713 THD_daxes_to_mat44(dset_mast->daxes) ; /* index-to-DICOM matrix */
5714 mast_cmat = dset_mast->daxes->ijk_to_dicom ; /* master coordinate matrix */
5715
5716 LOAD_IDENT_MAT44(amat) ; /* not actually used in this function */
5717
5718 /*--- create output dataset headers and patch them up ---*/
5719
5720 INIT_3DARR(dset_out) ;
5721
5722 for( kds=0 ; kds < numds ; kds++ ){
5723 dset_sss = DSET_IN_3DARR(dset_src,kds) ;
5724 nvals = DSET_NVALS(dset_sss) ; if( nvals > nvmax ) nvals = nvmax ;
5725 dset_ooo = EDIT_empty_copy( dset_mast ) ;
5726 if( verb_nww > 1 )
5727 ININFO_message(" creating empty output dataset '%s'",prefix[kds]) ;
5728 EDIT_dset_items( dset_ooo ,
5729 ADN_prefix , prefix[kds] ,
5730 ADN_nvals , nvals ,
5731 ADN_datum_all , MRI_float ,
5732 ADN_none ) ;
5733 if( DSET_NUM_TIMES(dset_sss) > 1 && nvals > 1 )
5734 EDIT_dset_items( dset_ooo ,
5735 ADN_ntt , nvals ,
5736 ADN_ttdel , DSET_TR(dset_sss) ,
5737 ADN_tunits, UNITS_SEC_TYPE ,
5738 ADN_nsl , 0 ,
5739 ADN_none ) ;
5740 else
5741 EDIT_dset_items( dset_ooo ,
5742 ADN_func_type , ISANAT(dset_ooo) ? ANAT_BUCK_TYPE
5743 : FUNC_BUCK_TYPE ,
5744 ADN_none ) ;
5745
5746 /* copy brick info into output (e.g., preserve sub-brick labels) */
5747
5748 THD_copy_datablock_auxdata( dset_sss->dblk , dset_ooo->dblk ) ;
5749 for( kk=0 ; kk < nvals ; kk++ )
5750 EDIT_BRICK_FACTOR(dset_ooo,kk,0.0f) ;
5751
5752 THD_daxes_to_mat44(dset_ooo->daxes) ; /* save coord transforms */
5753
5754 ADDTO_3DARR(dset_out,dset_ooo) ;
5755 NI_sleep(1) ;
5756 }
5757
5758 nx = DSET_NX(dset_mast) ; /* 3D grid sizes */
5759 ny = DSET_NY(dset_mast) ;
5760 nz = DSET_NZ(dset_mast) ; nxyz = nx*ny*nz ;
5761
5762 vp = 1 + (nvmax/50) ; /* how often to print a '.' progress meter */
5763 if( verb_nww ) fprintf(stderr,"++ Warping:") ; /* start progress meter */
5764
5765 /****** Loop over output sub-bricks,
5766 create the warp dataset for that 'time' index, then apply it *****/
5767
5768 #ifdef DEBUG_CATLIST
5769 NI_sleep(1) ;
5770 if( verb_nww > 1 ) fprintf(stderr,"[nvar=%d]",nwc->nvar) ;
5771 #endif
5772
5773 for( iv=0 ; iv < nvmax ; iv++ ){
5774
5775 /*--- create the warp dataset into dset_qwarp (if needed) ----*/
5776
5777 if( iv < nwc->nvar ){ /* don't do duplicates if past */
5778 /* end of 'time' inside nwc */
5779 /*** toss any old warps */
5780 #ifdef DEBUG_CATLIST
5781 if( verb_nww > 1 ) fprintf(stderr," a%s",MEMORY_SHORT) ;
5782 #endif
5783 DSET_delete (dset_nwarp) ; DSET_delete (dset_qwarp) ;
5784 DESTROY_IMARR(imar_nwarp) ; DESTROY_IMARR(imar_src ) ;
5785 /*** get the iv-th warp from the list, and pad it */
5786 #ifdef DEBUG_CATLIST
5787 if( verb_nww > 1 ) fprintf(stderr,"b%s",MEMORY_SHORT) ;
5788 #endif
5789 dset_nwarp = IW3D_from_nwarp_catlist( nwc , iv ) ; /* get the iv-th warp */
5790 if( dset_nwarp == NULL ){ /* should never happen */
5791 ERROR_message("Can't acquire/compute nwarp dataset #%d ?!?",iv); RETURN(NULL) ;
5792 }
5793
5794 if( iv == 0 ){ /* save the warped space label [moved here 07 Jan 2019] */
5795 for( kds=0 ; kds < numds ; kds++ ){
5796 dset_ooo = DSET_IN_3DARR(dset_out,kds) ;
5797 MCW_strncpy( dset_ooo->atlas_space , dset_nwarp->atlas_space , THD_MAX_NAME ) ;
5798 }
5799 }
5800
5801 #ifdef DEBUG_CATLIST
5802 if( verb_nww > 1 ) fprintf(stderr,"'%s",MEMORY_SHORT) ;
5803 #endif
5804 if( next > 0 ){
5805 dset_qwarp = THD_nwarp_extend( dset_nwarp , next,next,next,next,next,next ) ;
5806 if( dset_qwarp == NULL ){
5807 ERROR_message("Can't extend nwarp dataset #%d ?!?",iv) ; RETURN(NULL) ;
5808 }
5809 } else {
5810 dset_qwarp = EDIT_full_copy( dset_nwarp , "ZharksRevenge" ) ;
5811 if( dset_qwarp == NULL ){
5812 ERROR_message("Can't copy nwarp dataset #%d ?!?",iv) ; RETURN(NULL) ;
5813 }
5814 }
5815
5816 DSET_delete(dset_nwarp) ; dset_nwarp = NULL ; /* 07 Jan 2019 */
5817
5818 if( !ISVALID_MAT44(dset_qwarp->daxes->ijk_to_dicom) )
5819 THD_daxes_to_mat44(dset_qwarp->daxes) ;
5820 nwarp_cmat = dset_qwarp->daxes->ijk_to_dicom ; /* coordinates of warp */
5821
5822 /* create warping indexes from warp dataset */
5823
5824 INIT_IMARR(imar_nwarp) ;
5825 #ifdef DEBUG_CATLIST
5826 if( verb_nww > 1 ) fprintf(stderr,"c%s",MEMORY_SHORT) ;
5827 #endif
5828 fim = THD_extract_float_brick(0,dset_qwarp) ; ADDTO_IMARR(imar_nwarp,fim) ;
5829 fim = THD_extract_float_brick(1,dset_qwarp) ; ADDTO_IMARR(imar_nwarp,fim) ;
5830 fim = THD_extract_float_brick(2,dset_qwarp) ; ADDTO_IMARR(imar_nwarp,fim) ;
5831
5832 /* Save this warp? [15 Mar 2021] */
5833
5834 if( QWARP_prefix != NULL ){
5835 char *qprefix , qstr[32] ;
5836 sprintf(qstr,"_%04d",iv) ;
5837 qprefix = modify_afni_prefix(QWARP_prefix,NULL,qstr) ;
5838 EDIT_dset_items( dset_qwarp, ADN_prefix , qprefix , ADN_none ) ;
5839 DSET_write(dset_qwarp) ;
5840 if( verb_nww ) ININFO_message(" wrote warp dataset %s",DSET_BRIKNAME(dset_qwarp)) ;
5841 }
5842
5843 DSET_delete(dset_qwarp) ; dset_qwarp = NULL ; /* 07 Jan 2019 */
5844
5845 /* the actual work of setting up the warp for this sub-brick */
5846
5847 #ifdef DEBUG_CATLIST
5848 if( verb_nww > 1 ) fprintf(stderr,"d%s",MEMORY_SHORT) ;
5849 #endif
5850 imar_src = THD_setup_nwarp( imar_nwarp, 0,amat ,
5851 nwarp_cmat, wincode , wfac ,
5852 src_cmat , mast_cmat , nx , ny , nz ) ;
5853
5854 DESTROY_IMARR(imar_nwarp) ; /* 07 Jan 2019 */
5855
5856 ip = MRI_FLOAT_PTR( IMARR_SUBIM(imar_src,0) ) ; /* warped indexes, */
5857 jp = MRI_FLOAT_PTR( IMARR_SUBIM(imar_src,1) ) ; /* for interpolation */
5858 kp = MRI_FLOAT_PTR( IMARR_SUBIM(imar_src,2) ) ; /* of dataset values */
5859
5860 } else if( !reuse && verb_nww ){
5861 reuse = 1 ; fprintf(stderr,"[R]") ; /* flag that re-use has started */
5862 }
5863
5864 /*----- warp this iv-th sub-brick of the input -----*/
5865
5866 for( kds=0 ; kds < numds ; kds++ ){ /* loop over input datasets */
5867 dset_sss = DSET_IN_3DARR(dset_src,kds) ;
5868 if( DSET_NVALS(dset_sss) < iv ) continue ; /* dataset is done already */
5869 dset_ooo = DSET_IN_3DARR(dset_out,kds) ;
5870 #ifdef DEBUG_CATLIST
5871 if( verb_nww > 1 ) fprintf(stderr,"-%s",MEMORY_SHORT) ;
5872 #endif
5873 if( DSET_BRICK_TYPE(dset_sss,iv) != MRI_complex ){
5874 fim = THD_extract_float_brick(iv,dset_sss) ; DSET_unload_one(dset_sss,iv) ;
5875 wim = mri_new_vol(nx,ny,nz,MRI_float) ;
5876 /*** the actual warping is done in the function below! ***/
5877 #ifdef DEBUG_CATLIST
5878 if( verb_nww > 1 ) fprintf(stderr,"+%s",MEMORY_SHORT) ;
5879 #endif
5880 THD_interp_floatim( fim, nxyz,ip,jp,kp, dincode, MRI_FLOAT_PTR(wim) ) ;
5881
5882 #if 0
5883 if( MRI_HIGHORDER(dincode) ){ /* clipping output values */
5884 double_pair fmm = mri_minmax(fim) ;
5885 float fb=(float)fmm.a , ft=(float)fmm.b ; int qq ;
5886 float *war=MRI_FLOAT_PTR(wim) ;
5887 #ifdef DEBUG_CATLIST
5888 if( verb_nww > 1 ) fprintf(stderr,"*%s",MEMORY_SHORT) ;
5889 #endif
5890 for( qq=0 ; qq < wim->nvox ; qq++ ){
5891 if( war[qq] < fb ) war[qq] = fb ; else if( war[qq] > ft ) war[qq] = ft ;
5892 }
5893 }
5894 #endif
5895 EDIT_substitute_brick( dset_ooo, iv, MRI_float, MRI_FLOAT_PTR(wim) ) ;
5896 mri_free(fim) ; /* is a copy, so delete it */
5897 } else { /* <<<-------------------------- complex image [27 Mar 2018] */
5898 fim = DSET_BRICK(dset_sss,iv) ;
5899 wim = mri_new_vol(nx,ny,nz,MRI_complex) ;
5900 #ifdef DEBUG_CATLIST
5901 if( verb_nww > 1 ) fprintf(stderr,"+%s",MEMORY_SHORT) ;
5902 #endif
5903 THD_interp_complexim( fim, nxyz,ip,jp,kp, dincode, MRI_COMPLEX_PTR(wim) ) ;
5904 EDIT_substitute_brick( dset_ooo, iv, MRI_complex, MRI_COMPLEX_PTR(wim) ) ;
5905 /* fim here is NOT a copy, so don't delete it */
5906 }
5907 #ifdef DEBUG_CATLIST
5908 if( verb_nww > 1 ) fprintf(stderr,"!%s",MEMORY_SHORT) ;
5909 #endif
5910 mri_clear_and_free(wim) ; /* clear_and_free won't delete the data, just the shell */
5911 } /* end of loop over input datasets */
5912
5913 if( verb_nww && iv%vp == 0 ) fprintf(stderr,".") ; /* progress meter */
5914
5915 } /*--- end of loop over output sub-bricks */
5916
5917 /* toss the final warps */
5918 #ifdef DEBUG_CATLIST
5919 if( verb_nww > 1 ) fprintf(stderr," z%s",MEMORY_SHORT) ;
5920 #endif
5921 DSET_delete (dset_nwarp) ; DSET_delete (dset_qwarp) ;
5922 DESTROY_IMARR(imar_nwarp) ; DESTROY_IMARR(imar_src ) ;
5923 for( kds=0 ; kds < numds ; kds++ ){
5924 dset_sss = DSET_IN_3DARR(dset_src,kds) ; DSET_unload(dset_sss) ;
5925 if( newprefix ) free(prefix[kds]) ;
5926 }
5927 if( newprefix ) free(prefix) ;
5928
5929 if( verb_nww ) fprintf(stderr,"Z%s\n",MEMORY_SHORT) ; /* end of progress */
5930
5931 RETURN(dset_out) ;
5932 }
5933
5934 /*----------------------------------------------------------------------------*/
5935 /* Shell to make calling the above function simpler for just 1 dataset.
5936 *//*--------------------------------------------------------------------------*/
5937
THD_nwarp_dataset_NEW(Nwarp_catlist * nwc,THD_3dim_dataset * dset_src,THD_3dim_dataset * dset_mast,char * prefix,int wincode,int dincode,float dxyz_mast,float wfac,int nvlim)5938 THD_3dim_dataset * THD_nwarp_dataset_NEW(
5939 Nwarp_catlist *nwc ,
5940 THD_3dim_dataset *dset_src ,
5941 THD_3dim_dataset *dset_mast ,
5942 char *prefix ,
5943 int wincode, int dincode,
5944 float dxyz_mast, float wfac, int nvlim )
5945 {
5946 THD_3dim_dataset_array *dset_sar , *dset_oar ;
5947 THD_3dim_dataset *dset_out=NULL ;
5948
5949 ENTRY("THD_nwarp_dataset_NEW") ;
5950
5951 INIT_3DARR(dset_sar) ; ADDTO_3DARR(dset_sar,dset_src) ;
5952
5953 dset_oar = THD_nwarp_dataset_array( nwc , dset_sar , dset_mast ,
5954 (prefix != NULL) ? &prefix : NULL ,
5955 wincode, dincode, dxyz_mast, wfac, nvlim ) ;
5956
5957 if( dset_oar != NULL ){
5958 dset_out = DSET_IN_3DARR(dset_oar,0) ;
5959 FREE_3DARR(dset_oar) ;
5960 }
5961 FREE_3DARR(dset_sar) ; RETURN(dset_out) ;
5962 }
5963
5964 #endif /*(C15)*/ /*###########################################################*/
5965
5966 #if 0 /* THESE FUNCTIONS ARE DELETED [27 Apr 2021] - RWCox */
5967 /*============================================================================*/
5968 /* (C16) Reverse Polish Notation warp calculator (3dNwarpCalc program). */
5969 /*============================================================================*/
5970
5971 #undef KILL_iwstk
5972 #define KILL_iwstk \
5973 do{ if( iwstk != NULL ){ \
5974 int qq; for( qq=0; qq < nstk; qq++ ) IW3D_destroy(iwstk[qq]); \
5975 free(iwstk) ; \
5976 } } while(0)
5977
5978 #undef ERREX
5979 #define ERREX(sss) \
5980 do{ ERROR_message("NwarpCalcRPN('%s')\n" \
5981 " at '%s': %s" , expr,cmd,sss ); \
5982 KILL_iwstk; NI_delete_str_array(sar); FREEIFNN(geomstring); \
5983 RETURN(NULL); \
5984 } while(0)
5985
5986 #undef ADDTO_iwstk
5987 #define ADDTO_iwstk(W) \
5988 do{ iwstk = (IndexWarp3D **)realloc(iwstk,sizeof(IndexWarp3D *)*(nstk+1)) ; \
5989 iwstk[nstk] = W ; nstk++ ; \
5990 } while(0)
5991
5992
5993 /*---------------------------------------------------------------------------*/
5994 /* nwarp RPN calculator function (cf. 3dNwarpCalc program) */
5995
5996 THD_3dim_dataset * NwarpCalcRPN( char *expr, char *prefix, int icode, int acode )
5997 {
5998 NI_str_array *sar ;
5999 char *cmd , acmd[4096] , mess[4096] ;
6000 IndexWarp3D **iwstk=NULL ;
6001 int nstk=0 , ii , ss ;
6002 IndexWarp3D *AA=NULL , *BB=NULL ;
6003 THD_3dim_dataset *oset=NULL ;
6004 int nx=0,ny=0,nz=0 ;
6005 mat44 cmat , imat ; /* cmat: i->x ; imat: x->i */
6006 char *geomstring=NULL , *sname=NULL ;
6007
6008 ENTRY("NwarpCalcRPN") ;
6009
6010 /**----- break string into sub-strings, delimited by whitespace -----**/
6011
6012 sar = NI_decode_string_list( expr , "`" ) ;
6013 if( sar == NULL ) RETURN(NULL) ;
6014 AAmemset(&imat,0,sizeof(mat44)); AAmemset(&cmat,0,sizeof(mat44));
6015 if( acode < 0 ) acode = icode ;
6016
6017 /**----- loop thru and process commands -----**/
6018
6019 if(verb_nww)INFO_message("NwarpCalcRPN('%s')",expr) ;
6020
6021 for( ss=0 ; ss < sar->num ; ss++ ){
6022
6023 cmd = sar->str[ss] ;
6024
6025 if(verb_nww)ININFO_message(" + stack size=%d next operation='%s'",nstk,cmd) ;
6026
6027 if( *cmd == '\0' ) continue ; /* WTF?! */
6028
6029 /*--- munge command? ---*/
6030
6031 if( *cmd == '%' || *cmd == '@' ){ /* a cheap trick */
6032 *cmd = '&' ;
6033 } else if( *cmd != '&' ){
6034 acmd[0] = '&' ; strcpy(acmd+1,cmd) ; cmd = acmd ; /* another cheap trick */
6035 }
6036
6037 /*--- read warp from a dataset ---*/
6038
6039 if( strncasecmp(cmd,"&readnwarp(",11) == 0 ||
6040 strncasecmp(cmd,"&readwarp(" ,10) == 0 ){
6041 char *buf , *bp=strchr(cmd,'(') ; THD_3dim_dataset *dset ;
6042 buf = strdup(bp+1) ;
6043 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6044 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6045 dset = THD_open_dataset(buf) ; DSET_COPYOVER_REAL(dset) ;
6046 if( dset == NULL ){
6047 sprintf(mess,"Can't read file '%s'",buf); free(buf); ERREX(mess);
6048 }
6049 AA = IW3D_from_dataset(dset,0,0) ; DSET_delete(dset) ;
6050 if( AA == NULL ){
6051 sprintf(mess,"Can't make warp from '%s'",buf); free(buf); ERREX(mess);
6052 }
6053 if( geomstring == NULL ){
6054 geomstring = strdup(AA->geomstring) ;
6055 sname = strdup(dset->atlas_space) ;
6056 nx = AA->nx; ny = AA->ny; nz = AA->nz; cmat = AA->cmat; imat = AA->imat;
6057 } else if( AA->nx != nx || AA->ny != ny || AA->nz != nz ){
6058 sprintf(mess,"non-conforming warp from '%s'",buf); free(buf); ERREX(mess);
6059 }
6060 ADDTO_iwstk(AA) ; free(buf) ;
6061 }
6062
6063 /*--- make identity warp from a dataset ---*/
6064
6065 else if( strncasecmp(cmd,"&identwarp(",11) == 0 ){
6066 char *buf=strdup(cmd+11) , *bp ; THD_3dim_dataset *dset ;
6067 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6068 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6069 dset = THD_open_dataset(buf) ; DSET_COPYOVER_REAL(dset) ;
6070 if( dset == NULL ){
6071 sprintf(mess,"Can't read file '%s'",buf); free(buf); ERREX(mess);
6072 }
6073 AA = IW3D_from_dataset(dset,1,0) ; DSET_delete(dset) ;
6074 if( AA == NULL ){
6075 sprintf(mess,"Can't make identwarp from '%s'",buf); free(buf); ERREX(mess);
6076 }
6077 if( geomstring == NULL ){
6078 geomstring = strdup(AA->geomstring) ;
6079 nx = AA->nx; ny = AA->ny; nz = AA->nz; cmat = AA->cmat; imat = AA->imat;
6080 } else if( AA->nx != nx || AA->ny != ny || AA->nz != nz ){
6081 sprintf(mess,"non-conforming warp from '%s'",buf); free(buf); ERREX(mess);
6082 }
6083 ADDTO_iwstk(AA) ; free(buf) ;
6084 }
6085
6086 /*--- create a warp from a set of polynomial parameters ---*/
6087
6088 else if( strncasecmp(cmd,"&readpoly(",10) == 0 ){
6089 char *buf=strdup(cmd+10) , *bp ; MRI_IMAGE *qim,*fim ; float *far ;
6090 if( nstk < 1 ){ free(buf); ERREX("nothing on stack -- needed for template"); }
6091 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6092 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6093 qim = mri_read_1D(buf) ;
6094 if( qim == NULL ){
6095 sprintf(mess,"Can't read file '%s'",buf); free(buf); ERREX(mess);
6096 }
6097 fim = mri_transpose(qim) ; mri_free(qim) ; far = MRI_FLOAT_PTR(fim) ;
6098 affmode = AFF_PARAM ;
6099 AA = IW3D_from_poly( fim->nx , far , iwstk[nstk-1] ) ;
6100 if( AA == NULL ){
6101 sprintf(mess,"Can't use file '%s' -- num param=%d",buf,fim->nx);
6102 mri_free(fim); free(buf); ERREX(mess);
6103 }
6104 mri_free(fim) ; ADDTO_iwstk(AA) ; free(buf) ;
6105 }
6106
6107 /*--- create a warp from a matrix ---*/
6108
6109 else if( strncasecmp(cmd,"&read4x4(",9) == 0 ){
6110 char *buf=strdup(cmd+9) , *bp ; MRI_IMAGE *qim,*fim ; float *far ;
6111 if( nstk < 1 ){ free(buf); ERREX("nothing on stack -- needed for template"); }
6112 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6113 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6114 qim = mri_read_1D(buf) ;
6115 if( qim == NULL ){
6116 sprintf(mess,"Can't read file '%s'",buf); free(buf); ERREX(mess);
6117 }
6118 fim = mri_transpose(qim) ; mri_free(qim) ; far = MRI_FLOAT_PTR(fim) ;
6119 if( fim->nvox < 12 ){
6120 sprintf(mess,"file '%s' has fewer than 12 numbers",buf);
6121 free(buf) ; mri_free(fim) ; ERREX(mess) ;
6122 }
6123 affmode = AFF_MATRIX ;
6124 AA = IW3D_from_poly( 12 , far , iwstk[nstk-1] ) ;
6125 if( AA == NULL ){
6126 sprintf(mess,"Can't make matrix from file '%s'",buf);
6127 mri_free(fim); free(buf); ERREX(mess);
6128 }
6129 mri_free(fim) ; ADDTO_iwstk(AA) ; free(buf) ;
6130 }
6131
6132 /*--- write it out, babee ---*/
6133
6134 else if( strncasecmp(cmd,"&write(",7) == 0 ){
6135 char *buf=strdup(cmd+7) , *bp ; THD_3dim_dataset *dset ;
6136 if( nstk < 1 ){ free(buf); ERREX("nothing on stack"); }
6137 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6138 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6139 AA = iwstk[nstk-1] ; FREEIFNN(AA->geomstring) ;
6140 AA->geomstring = strdup(geomstring) ; AA->cmat = cmat ; AA->imat = imat ;
6141 dset = IW3D_to_dataset( AA , buf ) ;
6142 if( sname != NULL ) MCW_strncpy( dset->atlas_space , sname , THD_MAX_NAME ) ;
6143 DSET_write(dset) ;
6144 if( verb_nww ) ININFO_message(" -- wrote dataset %s",DSET_BRIKNAME(dset)) ;
6145 DSET_delete(dset) ; free(buf) ;
6146 }
6147
6148 /*--- duplication ---*/
6149
6150 else if( strcasecmp(cmd,"&dup") == 0 ){
6151 if( nstk < 1 ) ERREX("nothing on stack") ;
6152 AA = IW3D_copy( iwstk[nstk-1] , 1.0f ) ;
6153 ADDTO_iwstk(AA) ;
6154 }
6155
6156 /*--- pop tart time! ---*/
6157
6158 else if( strcasecmp(cmd,"&pop") == 0 ){
6159 if( nstk < 1 ) ERREX("nothing on stack") ;
6160 IW3D_destroy( iwstk[nstk-1] ) ;
6161 nstk-- ;
6162 }
6163
6164 /*--- swap-eroni ---*/
6165
6166 else if( strncasecmp(cmd,"&swap",5) == 0 ){ /* modified 06 May 2013 */
6167 char *bp=strchr(cmd,'(') ; /* to allow (a,b) args */
6168 int nAA=1 , nBB=0 ;
6169 if( bp != NULL ){
6170 nAA = nBB = -666 ;
6171 sscanf(bp+1,"%d,%d",&nAA,&nBB) ;
6172 if( nAA < 0 || nBB < 0 || nAA == nBB ) ERREX("illegal values in &swap") ;
6173 }
6174 nAA++ ; nBB++ ;
6175 if( nstk < MAX(nAA,nBB) ) ERREX("stack too short for &swap") ;
6176 AA = iwstk[nstk-nAA] ; BB = iwstk[nstk-nBB] ;
6177 iwstk[nstk-nAA] = BB ; iwstk[nstk-nBB] = AA ;
6178 }
6179
6180 /*--- go to Australia (viz., invert) ---*/
6181
6182 else if( strcasecmp(cmd,"&invert") == 0 || strcasecmp(cmd,"&inverse") == 0 ){
6183 double ct = COX_cpu_time() ;
6184 if( nstk < 1 ) ERREX("nothing on stack") ;
6185 AA = IW3D_invert( iwstk[nstk-1] , NULL , icode ) ;
6186 if( AA == NULL ) ERREX("inversion failed :-(") ;
6187 IW3D_destroy( iwstk[nstk-1] ) ; iwstk[nstk-1] = AA ;
6188 if( verb_nww )
6189 ININFO_message(" -- invert CPU time = %.1f s",COX_cpu_time()-ct) ;
6190 }
6191
6192 /*--- inverse square root ---*/
6193
6194 else if( strcasecmp(cmd,"&sqrtinv") == 0 || strcasecmp(cmd,"&invsqrt") == 0 ){
6195 double ct = COX_cpu_time() ;
6196 if( nstk < 1 ) ERREX("nothing on stack") ;
6197 AA = IW3D_sqrtinv( iwstk[nstk-1] , icode ) ;
6198 if( AA == NULL ) ERREX("inverse square root failed :-(") ;
6199 IW3D_destroy( iwstk[nstk-1] ) ; iwstk[nstk-1] = AA ;
6200 if( verb_nww )
6201 ININFO_message(" -- inverse square root CPU time = %.1f s",COX_cpu_time()-ct) ;
6202 }
6203
6204 /*--- square root ---*/
6205
6206 else if( strcasecmp(cmd,"&sqrt") == 0 ){
6207 double ct = COX_cpu_time() ;
6208 if( nstk < 1 ) ERREX("nothing on stack") ;
6209 AA = IW3D_sqrt( iwstk[nstk-1] , icode ) ;
6210 if( AA == NULL ) ERREX("square root failed :-(") ;
6211 IW3D_destroy( iwstk[nstk-1] ) ; iwstk[nstk-1] = AA ;
6212 if( verb_nww )
6213 ININFO_message(" -- square root CPU time = %.1f s",COX_cpu_time()-ct) ;
6214 }
6215
6216 /*--- sqrtpair ---*/
6217
6218 else if( strcasecmp(cmd,"&sqrtpair") == 0 ){
6219 double ct = COX_cpu_time() ;
6220 if( nstk < 1 ) ERREX("nothing on stack") ;
6221 { IndexWarp3D_pair *YZ = IW3D_sqrtpair(iwstk[nstk-1],icode) ;
6222 BB = YZ->fwarp ; AA = YZ->iwarp ; free(YZ) ;
6223 }
6224 IW3D_destroy( iwstk[nstk-1] ) ; iwstk[nstk-1] = AA ; ADDTO_iwstk(BB) ;
6225 if( verb_nww )
6226 ININFO_message(" -- square root pair CPU time = %.1f s",COX_cpu_time()-ct) ;
6227 }
6228
6229 /*--- compose ---*/
6230
6231 else if( strcasecmp(cmd,"&compose") == 0 || strcasecmp(cmd,"&*") == 0 ||
6232 strcasecmp(cmd,"&mult") == 0 ){
6233 double ct = COX_cpu_time() ;
6234 if( nstk < 2 ) ERREX("stack too short") ;
6235 AA = IW3D_compose( iwstk[nstk-1] , iwstk[nstk-2] , icode ) ;
6236 if( AA == NULL ) ERREX("composition failed :-(") ;
6237 IW3D_destroy( iwstk[nstk-1] ) ; IW3D_destroy( iwstk[nstk-2] ) ;
6238 iwstk[nstk-2] = AA ; nstk-- ;
6239 if( verb_nww )
6240 ININFO_message(" -- compose CPU time = %.1f s",COX_cpu_time()-ct) ;
6241 }
6242
6243 /*--- totally square, man ---*/
6244
6245 else if( strcasecmp(cmd,"&sqr") == 0 || strcasecmp(cmd,"&square") == 0 ){
6246 double ct = COX_cpu_time() ;
6247 if( nstk < 1 ) ERREX("nothing on stack") ;
6248 AA = IW3D_compose( iwstk[nstk-1] , iwstk[nstk-1] , icode ) ;
6249 if( AA == NULL ) ERREX("composition failed :-(") ;
6250 IW3D_destroy( iwstk[nstk-1] ) ;
6251 iwstk[nstk-1] = AA ;
6252 if( verb_nww )
6253 ININFO_message(" -- sqr CPU time = %.1f s",COX_cpu_time()-ct) ;
6254 }
6255
6256 /*--- scale ---*/
6257
6258 else if( strncasecmp(cmd,"&scale(",7) == 0 ){
6259 char *buf=strdup(cmd+7) , *bp ; float val ;
6260 if( nstk < 1 ){ free(buf); ERREX("nothing on stack"); }
6261 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6262 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6263 val = (float)strtod(buf,NULL) ; free(buf) ;
6264 IW3D_scale( iwstk[nstk-1] , val ) ;
6265 }
6266
6267 /*--- sum ---*/
6268
6269 else if( strncasecmp(cmd,"&sum",4) == 0 ){
6270 char *bp=strchr(cmd,'(') ;
6271 float alpha=1.0f , beta=1.0f ;
6272 if( nstk < 2 ) ERREX("stack is too small") ;
6273 if( bp != NULL ) sscanf(bp+1,"%f,%f",&alpha,&beta) ;
6274 AA = IW3D_sum( iwstk[nstk-1],alpha , iwstk[nstk-2],beta ) ;
6275 IW3D_destroy( iwstk[nstk-2] ) ; IW3D_destroy( iwstk[nstk-1] ) ;
6276 nstk-- ; iwstk[nstk-1] = AA ;
6277 }
6278
6279 /*--- apply ---*/
6280
6281 else if( strncasecmp(cmd,"&apply(",7) == 0 ){
6282 char *buf=strdup(cmd+7) , *bp , *pref ;
6283 THD_3dim_dataset *wset , *iset , *oset ;
6284 if( nstk < 1 ){ free(buf); ERREX("nothing on stack"); }
6285 for( bp=buf ; *bp != '\0' && *bp != ')' ; bp++ ) ; /*nada*/
6286 if( *bp == ')' ) *bp = '\0' ; /* delete trailing ) */
6287 for( bp=buf ; *bp != '\0' && *bp != ',' ; bp++ ) ; /*nada*/
6288 if( *bp != ',' ){ free(buf); ERREX("no comma for prefix"); }
6289 *bp = '\0' ; pref = bp+1 ; /* delete comma */
6290 if( !THD_filename_ok(pref) ){ free(buf); ERREX("illegal prefix"); }
6291 AA = iwstk[nstk-1] ; FREEIFNN(AA->geomstring) ;
6292 AA->geomstring = strdup(geomstring) ; AA->cmat = cmat ; AA->imat = imat ;
6293 iset = THD_open_dataset(buf) ; DSET_COPYOVER_REAL(iset) ;
6294 if( iset == NULL ){
6295 sprintf(mess,"Can't read file '%s'",buf); free(buf); ERREX(mess);
6296 }
6297 wset = IW3D_to_dataset( AA , buf ) ;
6298 oset = THD_nwarp_dataset( wset, iset, NULL, pref, icode,acode, 0.0f, 1.0f, 999999999 , NULL ) ;
6299 tross_Copy_History (iset,oset) ;
6300 sprintf(mess,"NwarpCalcRPN '%s'",cmd) ; tross_Append_History(oset,mess) ;
6301 if( sname != NULL ) MCW_strncpy(oset->atlas_space,sname,THD_MAX_NAME) ;
6302 DSET_delete(iset) ; DSET_delete(wset) ; DSET_write(oset) ;
6303 if( verb_nww ) ININFO_message(" -- wrote dataset %s",DSET_BRIKNAME(oset)) ;
6304 DSET_delete(oset) ; free(buf) ;
6305 }
6306
6307 /*--- No worst, there is none ---*/
6308
6309 else {
6310 ERREX("unknown operation :-((") ;
6311 }
6312
6313 } /*----- end of loop over operations -----*/
6314
6315 if(verb_nww)INFO_message("end of evaluation loop") ;
6316
6317 if( nstk > 0 ){
6318 AA = iwstk[nstk-1] ;
6319 FREEIFNN(AA->geomstring) ;
6320 AA->geomstring = strdup(geomstring) ; AA->cmat = cmat ; AA->imat = imat ;
6321 oset = IW3D_to_dataset( AA , prefix ) ;
6322 }
6323
6324 KILL_iwstk ; NI_delete_str_array(sar) ; FREEIFNN(geomstring) ; FREEIFNN(sname) ;
6325
6326 RETURN(oset) ;
6327 }
6328
6329 #endif /*(C16)*/ /*###########################################################*/
6330
6331 #if 1
6332 /*============================================================================*/
6333 /* (C17) Functions for reading warps and inverting/catenating them right away */
6334 /* (for the '-nwarp' input of various 3dNwarp programs) */
6335 /*============================================================================*/
6336
6337 /**** These CW_ values are globals used only for this catenated warp stuff ****/
6338
6339 #define CW_NMAX 99 /* max number of warps allowed in input expression */
6340
6341 static int CW_nwtop=0 ;
6342 static IndexWarp3D *CW_iwarp[CW_NMAX] ;
6343 static float CW_iwfac[CW_NMAX] ; /* always 1.0 at present */
6344 static mat44 *CW_awarp[CW_NMAX] ;
6345 static mat44_vec *CW_vwarp[CW_NMAX] ;
6346 static int CW_nx=0,CW_ny=0,CW_nz=0 ; static char *CW_geomstring=NULL ;
6347 static mat44 CW_cmat , CW_imat ; /* matrices between indexes and coords */
6348
6349 static float CW_dxal=0.0f , CW_dyal=0.0f , CW_dzal=0.0f ;
6350 static float CW_dx =1.0f , CW_dy =1.0f , CW_dz =1.0f ;
6351
6352 static THD_3dim_dataset *CW_inset=NULL ; /* template for saving index */
6353 /* warp as an AFNI dataset */
6354 /*----------------------------------------------------------------------------*/
6355 static char *CW_saved_geomstring = NULL ;
6356 static int CW_saved_expad = 0 ;
6357 static int CW_no_expad = 1 ;
6358 static int CW_extra_pad = 0 ;
6359 static int CW_interp = MRI_WSINC5 ;
6360
CW_get_saved_geomstring(void)6361 char * CW_get_saved_geomstring(void){ return CW_saved_geomstring ; }
CW_get_saved_expad(void)6362 int CW_get_saved_expad (void){ return CW_saved_expad ; }
6363
6364 /*----------------------------------------------------------------------------*/
6365 /* Erase the above static data */
6366
CW_clear_data(void)6367 void CW_clear_data(void)
6368 {
6369 int ii ;
6370 for( ii=0 ; ii < CW_NMAX ; ii++ ){
6371 CW_iwfac[ii] = 1.0f ; /* usage is '#if 0'-ed out at present */
6372 if( CW_iwarp[ii] != NULL ){
6373 IW3D_destroy(CW_iwarp[ii]) ; CW_iwarp[ii] = NULL ;
6374 }
6375 if( CW_awarp[ii] != NULL ){
6376 free(CW_awarp[ii]) ; CW_awarp[ii] = NULL ;
6377 }
6378 if( CW_vwarp[ii] != NULL ){
6379 DESTROY_mat44_vec(CW_vwarp[ii]) ; CW_vwarp[ii] = NULL ;
6380 }
6381 }
6382 CW_nwtop = CW_nx = CW_ny = CW_nz = 0.0f ;
6383 if( CW_geomstring != NULL ){
6384 free(CW_geomstring) ; CW_geomstring = NULL ;
6385 }
6386 if( CW_inset != NULL ){
6387 DSET_delete(CW_inset) ; CW_inset = NULL ;
6388 }
6389 ZERO_MAT44(CW_imat) ; ZERO_MAT44(CW_cmat) ;
6390 CW_dxal = CW_dyal = CW_dzal = 0.0f ;
6391 CW_dx = CW_dy = CW_dz = 1.0f ;
6392
6393 return ;
6394 }
6395
6396 /*----------------------------------------------------------------------------*/
6397
M44_max_shifts(mat44_vec * mvv)6398 float_triple M44_max_shifts( mat44_vec *mvv )
6399 {
6400 float_triple xyz = {0.0f,0.0f,0.0f} ;
6401 mat44 mmm ; int ii ; float dxm,dym,dzm , dx,dy,dz ;
6402
6403 if( mvv == NULL || mvv->nmar == 0 || mvv->mar == NULL ) return xyz ;
6404
6405 dxm = dym = dzm = 0.0f ;
6406 for( ii=0 ; ii < mvv->nmar ; ii++ ){
6407 mmm = mvv->mar[ii] ;
6408 dx = fabsf(mmm.m[0][3]) ; if( dx > dxm ) dxm = dx ;
6409 dy = fabsf(mmm.m[1][3]) ; if( dy > dym ) dym = dy ;
6410 dz = fabsf(mmm.m[2][3]) ; if( dz > dzm ) dzm = dz ;
6411 }
6412
6413 xyz.a = dxm ; xyz.b = dym ; xyz.c = dzm ; return xyz ;
6414 }
6415
6416 /*----------------------------------------------------------------------------*/
6417 /* Reads a bunch of matrices, not just one */
6418
CW_read_affine_warp(char * cp)6419 mat44_vec * CW_read_affine_warp( char *cp )
6420 {
6421 mat44 mmm ; mat44_vec *mvv=NULL ;
6422 MRI_IMAGE *qim ; float *qar, *tar ; char *wp , *ocp ;
6423 int do_inv=0 , do_sqrt=0 , ii , nmat ;
6424
6425 ENTRY("CW_read_affine_warp") ;
6426
6427 if( cp == NULL || *cp == '\0' ) RETURN(mvv) ;
6428 ocp = cp ;
6429
6430 #if 0
6431 INFO_message("Enter CW_read_affine_warp( %s )",cp) ;
6432 #endif
6433
6434 if( strncasecmp(cp,"INV(",4) == 0 ){ /* set inversion flag */
6435 cp += 4 ; do_inv = 1 ;
6436 } else if( strncasecmp(cp,"INVERT(",7) == 0 ){
6437 cp += 7 ; do_inv = 1 ;
6438 } else if( strncasecmp(cp,"INVERSE(",8) == 0 ){
6439 cp += 8 ; do_inv = 1 ;
6440 } else if( strncasecmp(cp,"SQRT(",5) == 0 ){ /* set squareroot flag */
6441 cp += 5 ; do_sqrt = 1 ;
6442 } else if( strncasecmp(cp,"SQRTINV(",8) == 0 || strncasecmp(cp,"INVSQRT(",8) == 0 ){
6443 cp += 8 ; do_inv = do_sqrt = 1 ; /* set both flags */
6444 }
6445 wp = strdup(cp) ; ii = strlen(wp) ;
6446 if( ii < 4 ){
6447 ERROR_message("input filename '%s' to CW_read_affine_warp is too short :-((") ;
6448 free(wp) ; RETURN(mvv) ;
6449 }
6450 if( wp[ii-1] == ')' ) wp[ii-1] = '\0' ;
6451
6452 qim = mri_read_1D(wp) ;
6453
6454 /* bad data? */
6455
6456 if( qim == NULL || qim->nvox < 12 ){
6457 ERROR_message("Cannot read affine warp from file '%s'",wp); free(wp); RETURN(mvv);
6458 }
6459
6460 if( qim->nx == 3 && qim->ny == 4 ){ /* single matrix in 3x4 'Xat.1D' format? */
6461 MRI_IMAGE *tim = mri_rowmajorize_1D(qim); mri_free(qim); qim = tim; /* make it 12x1 */
6462 } else {
6463 MRI_IMAGE *tim ;
6464 if( qim->ny != 12 ){
6465 ERROR_message("Affine warp file '%s': have %d, not 12, values per row",wp,qim->ny) ;
6466 free(wp) ; RETURN(mvv) ;
6467 }
6468 tim = mri_transpose(qim) ; mri_free(qim) ; qim = tim ; /* flip to column major order */
6469 }
6470
6471 if( qim->nx != 12 ){
6472 ERROR_message("CW_read_affine_warp: nx == %d != 12 (this message should never happen!)",qim->nx) ;
6473 free(wp); RETURN(mvv);
6474 }
6475
6476 /* at this point, qim->nx = 12, and qim->ny = number of matrices */
6477
6478 nmat = qim->ny ;
6479 mvv = (mat44_vec *)malloc(sizeof(mat44_vec)) ;
6480 mvv->nmar = nmat ;
6481 mvv->mar = (mat44 *)malloc(sizeof(mat44)*nmat) ;
6482
6483 qar = MRI_FLOAT_PTR(qim) ;
6484 for( ii=0 ; ii < nmat ; ii++ ){
6485 tar = qar + ii*12 ;
6486 LOAD_MAT44(mmm,tar[0],tar[1],tar[2] ,tar[3],
6487 tar[4],tar[5],tar[6] ,tar[7],
6488 tar[8],tar[9],tar[10],tar[11]) ;
6489
6490 if( do_inv ){ mat44 imm=MAT44_INV(mmm) ; mmm=imm; } /* invert */
6491 if( do_sqrt ){ mat44 smm=THD_mat44_sqrt(mmm); mmm=smm; } /* sqrt */
6492
6493 mvv->mar[ii] = mmm ;
6494 }
6495
6496 #if 0
6497 ININFO_message("output Matrix[%d]",nmat) ;
6498 #endif
6499
6500 mri_free(qim) ; free(wp) ;
6501
6502 NI_strncpy(mvv->fname,ocp,128) ; RETURN(mvv) ;
6503 }
6504
6505 /*----------------------------------------------------------------------------*/
6506 /* Reads just one matrix - used in 3dNwarpCat.c */
6507
CW_read_affine_warp_OLD(char * cp)6508 mat44 CW_read_affine_warp_OLD( char *cp )
6509 {
6510 mat44_vec *mvv ; mat44 mmm ;
6511
6512 mvv = CW_read_affine_warp(cp) ;
6513
6514 if( mvv == NULL || mvv->nmar <= 0 || mvv->mar == NULL ){ /* bad bad bad */
6515 ERROR_message("Failed to read affine warp from file '%s' -- using identity matrix",cp) ;
6516 LOAD_IDENT_MAT44(mmm) ; return mmm ;
6517 } else if( mvv->nmar > 1 ){
6518 WARNING_message("Affine warp file '%s' has %d matrices: using only first one",cp,mvv->nmar) ;
6519 }
6520
6521 mmm = mvv->mar[0] ; DESTROY_mat44_vec(mvv) ;
6522 return mmm ;
6523 }
6524
6525 /*----------------------------------------------------------------------------*/
6526 /* Read a warp from a dataset -- don't convert to index warp (yet).
6527 Allows for all the weird pre-processing prefixes, like INV, SQRT, etc.
6528 *//*--------------------------------------------------------------------------*/
6529
CW_read_dataset_warp(char * cp)6530 THD_3dim_dataset * CW_read_dataset_warp( char *cp )
6531 {
6532 char *wp ; int do_inv=0 , do_sqrt=0 , do_empty=0 , ii ;
6533 THD_3dim_dataset *dset, *eset=NULL ;
6534
6535 ENTRY("CW_read_dataset_warp") ;
6536
6537 /* Does the user order some weird processing on the warp?? */
6538
6539 if( strncasecmp(cp,"INV(",4) == 0 ){ /* set inversion flag */
6540 cp += 4 ; do_inv = 1 ;
6541 } else if( strncasecmp(cp,"INVERT(",7) == 0 ){
6542 cp += 7 ; do_inv = 1 ;
6543 } else if( strncasecmp(cp,"INVERSE(",8) == 0 ){
6544 cp += 8 ; do_inv = 1 ;
6545 } else if( strncasecmp(cp,"SQRT(",5) == 0 ){ /* set squareroot flag */
6546 cp += 5 ; do_sqrt = 1 ;
6547 } else if( strncasecmp(cp,"SQRTINV(",8) == 0 || strncasecmp(cp,"INVSQRT(",8) == 0 ){
6548 cp += 8 ; do_inv = do_sqrt = 1 ; /* set both flags */
6549 } else if( strncasecmp(cp,"IDENT(",6) == 0 ){
6550 cp += 6 ; do_empty = 1 ; /* this one is easy */
6551 }
6552 wp = strdup(cp) ; ii = strlen(wp) ;
6553 if( ii < 4 ){
6554 ERROR_message("input string to CW_read_dataset_warp is too short :-((") ;
6555 free(wp) ; RETURN(NULL) ;
6556 }
6557 if( wp[ii-1] == ')' ) wp[ii-1] = '\0' ;
6558
6559 /* Check for special case of uni-directional warp from 1 sub-brick:
6560 RL: or LR: for x-direction only
6561 AP: or PA: for y-direction only
6562 IS: or SI: for z-direction only
6563 VEC:a,b,c: for arbitrary direction
6564 Can also have have a scale factor for the dataset, as in
6565 RL:0.7:datasetname or VEC:1,1,1:-0.5:datasetname
6566 Note that vector (a,b,c) will be L2-normalized to a unit direction */
6567
6568 if( strncasecmp(wp,"RL:",3) == 0 || strncasecmp(wp,"LR:",3) == 0 ||
6569 strncasecmp(wp,"AP:",3) == 0 || strncasecmp(wp,"PA:",3) == 0 ||
6570 strncasecmp(wp,"IS:",3) == 0 || strncasecmp(wp,"SI:",3) == 0 ||
6571 strncasecmp(wp,"VEC:",4)== 0 || strncasecmp(wp,"UNI:",4)== 0 ){
6572
6573 float vx=0.0f,vy=0.0f,vz=0.0f,vm=0.0f ;
6574 char *up=strchr(wp,':')+1 , *vp ;
6575 MRI_IMAGE *dim ; float *dar , *xar,*yar,*zar ; int nvox ;
6576
6577 /* set unit vector for direction of warp displacements in 3D */
6578
6579 switch( toupper(*wp) ){
6580 case 'R': case 'L': vx = 1.0f ; vy = vz = 0.0f ; break ;
6581 case 'A': case 'P': vy = 1.0f ; vx = vz = 0.0f ; break ;
6582 case 'I': case 'S': vz = 1.0f ; vx = vy = 0.0f ; break ;
6583
6584 case 'V': default:
6585 sscanf(up,"%f,%f,%f",&vx,&vy,&vz) ;
6586 vm = sqrtf(vx*vx+vy*vy+vz*vz) ;
6587 if( vm < 1.e-6f ){
6588 ERROR_message("warp '%s' :-) direction/factors unclear",wp) ;
6589 free(wp) ; RETURN(NULL) ;
6590 }
6591 if( toupper(*wp) == 'V' ){ vx /= vm ; vy /= vm ; vz /= vm ; }
6592 vp = strchr(up,':') ;
6593 if( vp == NULL || vp[1] == '\0' ){
6594 ERROR_message("warp '%s' :-) no dataset to read?",wp) ;
6595 free(wp) ; RETURN(NULL) ;
6596 }
6597 up = vp+1 ;
6598 break ;
6599 }
6600
6601 /* check if there is a scale factor (need one more ':') */
6602
6603 vp = strchr(up,':') ;
6604 if( vp != NULL && isnumeric(*up) ){
6605 float wfac = (float)strtod(up,NULL) ;
6606 if( wfac == 0.0f ){
6607 ERROR_message("uni-directional warp '%s' :-) scale factor = 0?",wp) ;
6608 free(wp) ; RETURN(NULL) ;
6609 }
6610 up = vp+1 ;
6611 vx *= wfac ; vy *= wfac ; vz *= wfac ;
6612 }
6613
6614 /* now read 1-brick dataset and do surgery on it to create a 3-brick dataset */
6615
6616 eset = THD_open_dataset(up) ; DSET_COPYOVER_REAL(eset) ;
6617 if( eset == NULL ){
6618 ERROR_message("Can't open dataset from file '%s'",up); free(wp); RETURN(NULL);
6619 }
6620 DSET_load(eset) ;
6621 if( !DSET_LOADED(eset) ){
6622 ERROR_message("Can't load dataset from file '%s'",up); free(wp); DSET_delete(eset); RETURN(NULL);
6623 }
6624 dim = THD_extract_float_brick(0,eset); dar = MRI_FLOAT_PTR(dim); DSET_unload(eset);
6625 nvox = dim->nvox ;
6626 xar = (float *)calloc(sizeof(float),nvox) ; /* bricks for output 3-brick dataset */
6627 yar = (float *)calloc(sizeof(float),nvox) ;
6628 zar = (float *)calloc(sizeof(float),nvox) ;
6629 dset = EDIT_empty_copy(eset) ; /* create 'true' 3D warp dataset */
6630 EDIT_dset_items( dset ,
6631 ADN_nvals , 3 ,
6632 ADN_ntt , 0 ,
6633 ADN_datum_all , MRI_float ,
6634 ADN_none ) ;
6635 EDIT_BRICK_FACTOR(dset,0,0.0) ; EDIT_substitute_brick(dset,0,MRI_float,xar) ;
6636 EDIT_BRICK_FACTOR(dset,1,0.0) ; EDIT_substitute_brick(dset,1,MRI_float,yar) ;
6637 EDIT_BRICK_FACTOR(dset,2,0.0) ; EDIT_substitute_brick(dset,2,MRI_float,zar) ;
6638 for( ii=0 ; ii < nvox ; ii++ ){ /* scale displacements appropriately */
6639 xar[ii] = vx * dar[ii]; yar[ii] = vy * dar[ii]; zar[ii] = vz * dar[ii];
6640 }
6641 mri_free(dim) ; DSET_delete(eset) ; eset = NULL ;
6642
6643 } else if( strncasecmp(wp,"FAC:",4) == 0 ){ /* special case: 3D scaling */
6644
6645 /* FAC:a,b,c:datasetname
6646 = scale 3-brick input dataset, each direction separately */
6647
6648 MRI_IMAGE *aim,*bim,*cim ; float *aar,*bar,*car , *xar,*yar,*zar ; int nvox ;
6649 float xfac=0.0f,yfac=0.0f,zfac=0.0f ;
6650 char *up=strchr(wp,':')+1 , *vp ;
6651
6652 sscanf(up,"%f,%f,%f",&xfac,&yfac,&zfac) ;
6653 if( fabsf(xfac)+fabsf(yfac)+fabsf(zfac) < 0.001f )
6654 WARNING_message("warp '%s': factors are small",wp) ;
6655 vp = strchr(up,':') ;
6656 if( vp == NULL ){
6657 ERROR_message("warp '%s': no dataset to read?",wp) ;
6658 free(wp) ; RETURN(NULL) ;
6659 }
6660 eset = THD_open_dataset(vp+1) ; DSET_COPYOVER_REAL(eset) ;
6661 if( eset == NULL ){
6662 ERROR_message("Can't open dataset from file '%s'",wp); free(wp); RETURN(NULL);
6663 } else if( DSET_NVALS(eset) < 3 ){
6664 ERROR_message("warp '%s': does not have 3 sub-bricks",wp) ;
6665 DSET_delete(eset) ; free(wp) ; RETURN(NULL) ;
6666 }
6667 dset = EDIT_empty_copy(eset) ;
6668 EDIT_dset_items( dset ,
6669 ADN_nvals , 3 ,
6670 ADN_ntt , 0 ,
6671 ADN_datum_all , MRI_float ,
6672 ADN_none ) ;
6673 EDIT_BRICK_FACTOR(dset,0,0.0) ; EDIT_substitute_brick(dset,0,MRI_float,NULL) ;
6674 EDIT_BRICK_FACTOR(dset,1,0.0) ; EDIT_substitute_brick(dset,1,MRI_float,NULL) ;
6675 EDIT_BRICK_FACTOR(dset,2,0.0) ; EDIT_substitute_brick(dset,2,MRI_float,NULL) ;
6676 aim = THD_extract_float_brick(0,eset); aar = MRI_FLOAT_PTR(aim);
6677 bim = THD_extract_float_brick(1,eset); bar = MRI_FLOAT_PTR(bim);
6678 cim = THD_extract_float_brick(2,eset); car = MRI_FLOAT_PTR(cim);
6679 DSET_delete(eset); eset = NULL;
6680 xar = DSET_ARRAY(dset,0); yar = DSET_ARRAY(dset,1); zar = DSET_ARRAY(dset,2);
6681 nvox = aim->nvox ;
6682 for( ii=0 ; ii < nvox ; ii++ ){
6683 xar[ii] = xfac*aar[ii] ; yar[ii] = yfac*bar[ii] ; zar[ii] = zfac*car[ii] ;
6684 }
6685 mri_free(cim); mri_free(bim); mri_free(aim);
6686
6687 } else { /*--- standard 3-brick warp (almost always the case) ---*/
6688
6689 dset = THD_open_dataset(wp) ; DSET_COPYOVER_REAL(dset) ;
6690 if( dset == NULL ){
6691 ERROR_message("Can't open dataset from file '%s'",wp); free(wp); RETURN(NULL);
6692 }
6693
6694 if( do_empty ){ /* replace dset with a 0-filled copy */
6695 eset = EDIT_empty_copy(dset) ;
6696 EDIT_dset_items( eset ,
6697 ADN_nvals , 3 ,
6698 ADN_ntt , 0 ,
6699 ADN_datum_all , MRI_float ,
6700 ADN_none ) ;
6701 EDIT_BRICK_FACTOR(eset,0,0.0) ; EDIT_substitute_brick(eset,0,MRI_float,NULL) ;
6702 EDIT_BRICK_FACTOR(eset,1,0.0) ; EDIT_substitute_brick(eset,1,MRI_float,NULL) ;
6703 EDIT_BRICK_FACTOR(eset,2,0.0) ; EDIT_substitute_brick(eset,2,MRI_float,NULL) ;
6704 DSET_delete(dset) ; dset = eset ; eset = NULL ;
6705
6706 } else if( DSET_NVALS(dset) < 3 ){ /* bad bad Leroy Brown */
6707 ERROR_message("warp '%s': does not have 3 sub-bricks",wp) ;
6708 DSET_delete(dset) ; free(wp) ; RETURN(NULL) ;
6709 }
6710
6711 DSET_load(dset) ;
6712
6713 if( !DSET_LOADED(dset) ){
6714 ERROR_message("warp '%s': cannot load data!",wp) ;
6715 DSET_delete(dset) ; free(wp) ; RETURN(NULL) ;
6716 }
6717 if( DSET_BRICK_TYPE(dset,0) != MRI_float ){
6718 ERROR_message("warp '%s': is not stored as floats",wp) ;
6719 DSET_delete(dset) ; free(wp) ; RETURN(NULL) ;
6720 }
6721
6722 }
6723
6724 /**--- do any functional processing of the warp ---**/
6725
6726 if( do_sqrt ){ /* user wants SQRT or SQRTINV */
6727 eset = THD_nwarp_sqrt(dset,do_inv) ;
6728 DSET_delete(dset) ; dset = eset ; eset = NULL ;
6729 } else if( do_inv ){ /* user wants INV */
6730 eset = THD_nwarp_invert(dset) ;
6731 DSET_delete(dset) ; dset = eset ; eset = NULL ;
6732 }
6733
6734 free(wp) ; RETURN(dset) ;
6735 }
6736
6737 /*----------------------------------------------------------------------------*/
6738 /* Load one warp into the nn-th static data array,
6739 converting to index warp (if it's not a matrix),
6740 and inverting it (or whatever) if necessary.
6741 *//*--------------------------------------------------------------------------*/
6742
CW_load_one_warp(int nn,char * cp)6743 void CW_load_one_warp( int nn , char *cp )
6744 {
6745 THD_3dim_dataset *dset ; IndexWarp3D *AA ;
6746
6747 ENTRY("CW_load_one_warp") ;
6748
6749 if( nn <= 0 || nn > CW_NMAX || cp == NULL || *cp == '\0' ){
6750 ERROR_message("bad inputs to CW_load_one_warp: nn=%d cp=%s",nn,cp) ; EXRETURN ;
6751 }
6752
6753 if( nn > CW_nwtop ) CW_nwtop = nn ; /* CW_nwtop = largest index thus far */
6754
6755 /* Deal with input of a matrix (from a .1D or .txt file) */
6756
6757 if( strcasestr(cp,".1D") != NULL || strcasestr(cp,".txt") != NULL ){
6758 mat44_vec *mvv ; mat44 mmm ;
6759 mvv = CW_read_affine_warp(cp) ; /* does INV() etc. */
6760 if( mvv == NULL || mvv->nmar <= 0 || mvv->mar == NULL ){ /* bad bad bad */
6761 ERROR_message("Failed to read affine warp from file '%s'",cp) ; EXRETURN ;
6762 } else if( mvv->nmar == 1 ){ /* only 1 matrix */
6763 #if 0
6764 INFO_message("CW_load_one_warp: single matrix") ;
6765 #endif
6766 CW_awarp[nn-1] = (mat44 *)malloc(sizeof(mat44)) ;
6767 mmm = mvv->mar[0] ;
6768 AAmemcpy(CW_awarp[nn-1],&mmm,sizeof(mat44)) ;
6769 CW_dxal += fabsf(mmm.m[0][3]) ; /* accumulate shifts */
6770 CW_dyal += fabsf(mmm.m[1][3]) ; /* for padding later */
6771 CW_dzal += fabsf(mmm.m[2][3]) ;
6772 } else { /* multiple matrices */
6773 float dx,dy,dz , dxm=0.0f,dym=0.0f,dzm=0.0f ; int ii ;
6774 #if 0
6775 INFO_message("CW_load_one_warp: matrix vector") ;
6776 #endif
6777 CW_vwarp[nn-1] = mvv ;
6778 NI_strncpy(CW_vwarp[nn-1]->fname,cp,128) ;
6779 for( ii=0 ; ii < mvv->nmar ; ii++ ){
6780 mmm = mvv->mar[ii] ;
6781 dx = fabsf(mmm.m[0][3]) ; if( dx > dxm ) dxm = dx ;
6782 dy = fabsf(mmm.m[1][3]) ; if( dy > dym ) dym = dy ;
6783 dz = fabsf(mmm.m[2][3]) ; if( dz > dzm ) dzm = dz ;
6784 }
6785 CW_dxal += dxm ; CW_dyal += dym ; CW_dzal += dzm ;
6786 }
6787 EXRETURN ;
6788 }
6789
6790 /*--- Below here is a dataset (nonlinear) warp ---*/
6791
6792 dset = CW_read_dataset_warp(cp) ; /* read it */
6793 if( dset == NULL ){
6794 ERROR_message("Failed to read 3D warp from '%s'",cp) ; EXRETURN ;
6795 }
6796
6797 AA = IW3D_from_dataset(dset,0,0) ; /* convert to index warp */
6798 if( AA == NULL ){
6799 ERROR_message("Can't make warp from dataset '%s'",cp); EXRETURN;
6800 }
6801
6802 /* first dataset ==> set geometry globals for this chain of warps */
6803
6804 if( CW_geomstring == NULL ){
6805 CW_geomstring = strdup(AA->geomstring) ; CW_saved_geomstring = strdup(CW_geomstring) ;
6806 CW_nx = AA->nx; CW_ny = AA->ny; CW_nz = AA->nz; CW_cmat = AA->cmat; CW_imat = AA->imat;
6807 CW_dx = fabsf(DSET_DX(dset)); CW_dy = fabsf(DSET_DY(dset)); CW_dz = fabsf(DSET_DZ(dset));
6808
6809 /* later dataset ==> check its geometry against the first */
6810
6811 } else if( EDIT_geometry_string_diff(CW_geomstring,AA->geomstring) > 0.01f ){
6812 ERROR_message("warp from dataset '%s' doesn't match earlier input geometry",cp) ;
6813 EXRETURN ;
6814 }
6815
6816 /* save first dataset as template for index warp to dataset conversion */
6817
6818 if( CW_inset == NULL ){
6819 DSET_unload(dset) ; CW_inset = dset ;
6820 } else {
6821 DSET_delete(dset) ;
6822 }
6823
6824 /* push this warp onto the stack we are creating */
6825
6826 CW_iwarp[nn-1] = AA ; EXRETURN ;
6827 }
6828
6829 /*----------------------------------------------------------------------------*/
6830 /* Read in a string like
6831 "warp1 warp2 warp3"
6832 and return the dataset that instantiates warp3(warp2(warp1(x))).
6833 This is how the '-nwarp' option in 3dNwarp*.c is implemented,
6834 except for 3dNwarpApply, which uses IW3D_read_nwarp_catlist().
6835
6836 N.B.: The nonlinear warps input this function must on the same grids!
6837 For a function that does not have this limitation, see the more
6838 complicated IW3D_read_nwarp_catlist() -- some distance below.
6839 *//*--------------------------------------------------------------------------*/
6840
IW3D_read_catenated_warp(char * cstr)6841 THD_3dim_dataset * IW3D_read_catenated_warp( char *cstr )
6842 {
6843 char *prefix = "NwarpCat" ;
6844 mat44 wmat , tmat , smat , qmat ;
6845 IndexWarp3D *warp=NULL , *tarp=NULL ;
6846 THD_3dim_dataset *oset ;
6847 NI_str_array *csar ; int ii ;
6848
6849 ENTRY("IW3D_read_catenated_warp") ;
6850
6851 if( cstr == NULL || *cstr == '\0' ) RETURN(NULL) ;
6852
6853 CW_clear_data() ; /* clear out any global CW_ data that's hanging around */
6854
6855 csar = NI_decode_string_list(cstr,";") ;
6856 if( csar == NULL || csar->num < 1 ) RETURN(NULL) ;
6857
6858 /*-- simple case of a single dataset input (very common) --*/
6859
6860 if( csar->num == 1 && strchr(csar->str[0],'(') == NULL && strchr(csar->str[0],':') == NULL ){
6861 oset = THD_open_dataset(csar->str[0]) ; DSET_COPYOVER_REAL(oset) ;
6862 if( oset == NULL ){
6863 ERROR_message("Can't open warp dataset '%s'",csar->str[0]) ;
6864 NI_delete_str_array(csar) ; RETURN(NULL) ;
6865 }
6866 if( DSET_NVALS(oset) < 3 ){
6867 ERROR_message("Warp dataset '%s' has < 3 sub-bricks",csar->str[0]) ;
6868 NI_delete_str_array(csar) ; DSET_delete(oset) ; RETURN(NULL) ;
6869 }
6870 DSET_load(oset) ;
6871 if( !DSET_LOADED(oset) ){
6872 ERROR_message("Warp dataset '%s' can't be loaded into memory",csar->str[0]) ;
6873 NI_delete_str_array(csar) ; DSET_delete(oset) ; RETURN(NULL) ;
6874 }
6875
6876 /* implement extra padding [14 Aug 2019] */
6877
6878 if( CW_extra_pad > 0 ){
6879 THD_3dim_dataset *QQ ;
6880 QQ = THD_nwarp_extend( oset ,
6881 CW_extra_pad, CW_extra_pad, CW_extra_pad,
6882 CW_extra_pad, CW_extra_pad, CW_extra_pad ) ;
6883 DSET_delete(oset) ; oset = QQ ;
6884 }
6885
6886 RETURN(oset) ;
6887 }
6888
6889 /*-- multiple input datasets (or INV operations, etc.) --*/
6890
6891 for( ii=0 ; ii < csar->num ; ii++ ) /* read them all in */
6892 CW_load_one_warp( ii+1 , csar->str[ii] ) ;
6893
6894 NI_delete_str_array(csar) ;
6895
6896 if( CW_geomstring == NULL ){ /* didn't get a real warp to use */
6897 ERROR_message("Can't compute nonlinear warp from string '%s'",cstr) ;
6898 CW_clear_data() ; RETURN(NULL) ;
6899 }
6900
6901 /*-- pad the nonlinear warps present [22 Aug 2014] --*/
6902
6903 if( CW_dxal > 0.0f || CW_dyal > 0.0f || CW_dzal > 0.0f ){
6904 float dm , xx,yy,zz ; int pp ;
6905 dm = MIN(CW_dx,CW_dy); dm = MIN(CW_dz,dm) ;
6906 xx = CW_dxal/dm ; yy = CW_dyal/dm ; zz = CW_dzal/dm ;
6907 dm = MAX(xx,yy) ; dm = MAX(dm,zz) ;
6908 pp = (int)rintf(1.1111f*dm) ; CW_saved_expad = MAX(pp,16) ;
6909 } else {
6910 CW_saved_expad = 16 ;
6911 }
6912 if( CW_no_expad ) CW_saved_expad = 0 ;
6913 CW_saved_expad += CW_extra_pad ;
6914 #if 0
6915 { int qq = AFNI_numenv("CW_EXPAD") ; if( qq >= 0 ) CW_saved_expad = qq ; }
6916 #endif
6917 if( CW_saved_expad > 0 ){
6918 IndexWarp3D *EE ; THD_3dim_dataset *QQ ; int first=1 ;
6919 QQ = THD_zeropad( CW_inset ,
6920 CW_saved_expad, CW_saved_expad, CW_saved_expad,
6921 CW_saved_expad, CW_saved_expad, CW_saved_expad,
6922 "ZharkExtends" , ZPAD_IJK | ZPAD_EMPTY ) ;
6923 DSET_delete(CW_inset) ; CW_inset = QQ ;
6924 for( ii=0 ; ii < CW_nwtop ; ii++ ){
6925 if( CW_iwarp[ii] != NULL ){
6926 EE = IW3D_extend( CW_iwarp[ii] ,
6927 CW_saved_expad, CW_saved_expad, CW_saved_expad,
6928 CW_saved_expad, CW_saved_expad, CW_saved_expad, 0 ) ;
6929 IW3D_destroy(CW_iwarp[ii]) ; CW_iwarp[ii] = EE ;
6930 if( first ){
6931 CW_cmat = EE->cmat; CW_imat = EE->imat; first = 0;
6932 }
6933 }
6934 }
6935 }
6936
6937 /*-- cat (compose) them --*/
6938
6939 LOAD_IDENT_MAT44(wmat) ;
6940
6941 for( ii=0 ; ii < CW_nwtop ; ii++ ){
6942
6943 if( CW_awarp[ii] != NULL ){ /* matrix to apply */
6944
6945 qmat = *(CW_awarp[ii]) ; /* convert from xyz warp to ijk warp */
6946 tmat = MAT44_MUL(qmat,CW_cmat) ;
6947 smat = MAT44_MUL(CW_imat,tmat) ;
6948
6949 if( warp == NULL ){ /* thus far, only matrices */
6950 qmat = MAT44_MUL(smat,wmat) ; wmat = qmat ;
6951 } else { /* apply matrix to nonlinear warp */
6952 tarp = IW3D_compose_w1m2(warp,smat,CW_interp) ;
6953 IW3D_destroy(warp) ; warp = tarp ;
6954 }
6955
6956 free(CW_awarp[ii]) ; CW_awarp[ii] = NULL ;
6957
6958 } else if( CW_iwarp[ii] != NULL ){ /* nonlinear warp to apply */
6959
6960 #if 0
6961 if( CW_iwfac[ii] != 1.0f ) IW3D_scale( CW_iwarp[ii] , CW_iwfac[ii] ) ;
6962 #endif
6963
6964 if( warp == NULL ){ /* create nonlinear warp at this point */
6965 if( ii == 0 ){ /* first one ==> don't compose with identity matrix */
6966 warp = IW3D_copy(CW_iwarp[ii],1.0f) ;
6967 } else { /* compose with previous matrix */
6968 warp = IW3D_compose_m1w2(wmat,CW_iwarp[ii],CW_interp) ;
6969 }
6970 } else { /* already have nonlinear warp, apply new one to it */
6971 tarp = IW3D_compose(warp,CW_iwarp[ii],CW_interp) ;
6972 IW3D_destroy(warp) ; warp = tarp ;
6973 }
6974
6975 IW3D_destroy(CW_iwarp[ii]) ; CW_iwarp[ii] = NULL ;
6976
6977 } else if( CW_vwarp[ii] != NULL ){ /* bad user */
6978
6979 WARNING_message(
6980 "Multi-line matrix file '%s' input when only one matrix is allowed!",
6981 CW_vwarp[ii]->fname ) ;
6982
6983 qmat = CW_vwarp[ii]->mar[0] ; /* convert from xyz warp to ijk warp */
6984 tmat = MAT44_MUL(qmat,CW_cmat) ;
6985 smat = MAT44_MUL(CW_imat,tmat) ;
6986
6987 if( warp == NULL ){ /* thus far, only matrices */
6988 qmat = MAT44_MUL(smat,wmat) ; wmat = qmat ;
6989 } else { /* apply matrix to nonlinear warp */
6990 tarp = IW3D_compose_w1m2(warp,smat,CW_interp) ;
6991 IW3D_destroy(warp) ; warp = tarp ;
6992 }
6993
6994 DESTROY_mat44_vec(CW_vwarp[ii]) ; CW_vwarp[ii] = NULL ;
6995
6996 }
6997
6998 } /* end of loop over input transformations */
6999
7000 /*--- create output dataset ---*/
7001
7002 if( warp == NULL ){
7003 ERROR_message("This message should never appear!!") ;
7004 CW_clear_data() ; RETURN(NULL) ;
7005 }
7006
7007 IW3D_adopt_dataset( warp , CW_inset ) ; /* this is why template was saved */
7008 oset = IW3D_to_dataset( warp , prefix ) ;
7009
7010 IW3D_destroy(warp) ; CW_clear_data() ;
7011
7012 RETURN(oset) ;
7013 }
7014
7015 /*----------------------------------------------------------------------------*/
7016 /* The following set of functions are for dealing with the Nwarp_catlist */
7017 /* struct, which is for use in 3dNwarpApply. Here, there is a chain of */
7018 /* transformations -- warps (datasets) and matrices -- with the wrinkle */
7019 /* that there may be multiple matrices in one transformation slot: */
7020 /* NetWarp[i] = Warp Matrix Matrix[i] Warp Matrix[i] ... */
7021 /* where [i] means the i-th transformation (i is 'time') */
7022 /*----------------------------------------------------------------------------*/
7023
IW3D_destroy_nwarp_catlist(Nwarp_catlist * nwc)7024 void IW3D_destroy_nwarp_catlist( Nwarp_catlist *nwc )
7025 {
7026 int ii ;
7027 if( nwc == NULL ) return ;
7028 if( nwc->nwarp != NULL ){
7029 for( ii=0 ; ii < nwc->ncat ; ii++ )
7030 if( nwc->nwarp[ii] != NULL ) DSET_delete(nwc->nwarp[ii]) ;
7031 free(nwc->nwarp) ;
7032 }
7033 if( nwc->awarp != NULL ){
7034 for( ii=0 ; ii < nwc->ncat ; ii++ )
7035 if( nwc->awarp[ii] != NULL ) DESTROY_mat44_vec(nwc->awarp[ii]) ;
7036 }
7037 if( nwc->actual_geomstring != NULL ) free(nwc->actual_geomstring) ;
7038 if( nwc->master_geomstring != NULL ) free(nwc->master_geomstring) ;
7039 free(nwc) ; return ;
7040 }
7041
7042 /*----------------------------------------------------------------------------*/
7043 /* Set the 'master' geometry of the catlist. Please note that the actual
7044 geometry of the warps inside will be padded from this. If gs is NULL,
7045 then the function will make something up instead, which you don't want.
7046 *//*--------------------------------------------------------------------------*/
7047
IW3D_set_geometry_nwarp_catlist(Nwarp_catlist * nwc,char * gsin)7048 void IW3D_set_geometry_nwarp_catlist( Nwarp_catlist *nwc , char *gsin )
7049 {
7050 int ii , nw=0 , xpad,ypad,zpad , first=1 ;
7051 char *gs , *gg , *hs ;
7052 float_triple delxyz ; float del ;
7053 THD_3dim_dataset *qset ;
7054
7055 ENTRY("IW3D_set_geometry_nwarp_catlist") ;
7056
7057 if( nwc == NULL || nwc->ncat < 1 ) EXRETURN ; /* bad */
7058
7059 /* count number of nonlinear warps */
7060
7061 for( ii=0 ; ii < nwc->ncat ; ii++ )
7062 if( ISVALID_DSET(nwc->nwarp[ii]) ) nw++ ;
7063 if( nw == 0 ) EXRETURN ; /* also bad */
7064
7065 /*--- must we make gs up from empty air? ---*/
7066
7067 if( gsin == NULL ) gs = strdup("\0") ;
7068 else gs = strdup(gsin) ;
7069
7070 if( !ISVALID_GEOMETRY_STRING(gs) ){
7071 THD_3dim_dataset **nset=NULL ; char **gset=NULL ; int jj ;
7072
7073 /* make pointers to nonlinear warp datasets and their geometry strings */
7074
7075 nset = (THD_3dim_dataset **)malloc(sizeof(THD_3dim_dataset *)*nw) ;
7076 gset = (char **)malloc(sizeof(char *)*nw) ;
7077 for( ii=jj=0 ; ii < nwc->ncat ; ii++ ){
7078 if( ISVALID_DSET(nwc->nwarp[ii]) ){
7079 nset[jj] = nwc->nwarp[ii] ;
7080 gset[jj] = EDIT_get_geometry_string(nset[jj]) ; jj++ ;
7081 }
7082 }
7083
7084 /* check for grid mismatch if have multiple warps */
7085
7086 for( jj=1 ; jj < nw ; jj++ ){
7087 if( EDIT_geometry_string_diff(gset[0],gset[jj]) > 0.01f ) break ;
7088 }
7089 if( jj == nw ){ /* no mismatch ==> keep this box */
7090 free(gs) ; gs = gset[0] ;
7091 for( jj=1 ; jj < nw ; jj++ ) free(gset[jj]) ;
7092 free(gset) ; gset = NULL ;
7093 } else { /* mismatch ==> build a big box */
7094 free(gs) ;
7095 gs = EDIT_geomstring_from_collection( nw , gset ) ;
7096 for( jj=0 ; jj < nw ; jj++ ) free(gset[jj]) ;
7097 free(gset) ; gset = NULL ;
7098 }
7099 free(nset) ;
7100
7101 } /*-- end of creating gs from nothing --*/
7102
7103 /*--- determine padding for gs from the internal shifts ---*/
7104
7105 if( nwc->xshift > 0.0f || nwc->yshift > 0.0f || nwc->zshift > 0.0f || CW_extra_pad > 0 ){
7106 delxyz = EDIT_geometry_string_to_delxyz(gs) ;
7107 del = MIN(delxyz.a,delxyz.b) ; del = MIN(del,delxyz.c) ;
7108 xpad = (int)rint( nwc->xshift / del ) ;
7109 ypad = (int)rint( nwc->yshift / del ) ;
7110 zpad = (int)rint( nwc->zshift / del ) ;
7111 if( xpad < ypad ) xpad = ypad ;
7112 if( xpad < zpad ) xpad = zpad ;
7113 xpad += CW_extra_pad ;
7114 if( xpad > 0 ) hs = EDIT_geometry_string_pad(gs,xpad) ;
7115 else hs = strdup(gs) ;
7116 nwc->xshift = nwc->yshift = nwc->zshift = 0.0f ;
7117 nwc->xpad = nwc->ypad = nwc->zpad = xpad ;
7118 #ifdef DEBUG_CATLIST
7119 if( verb_nww > 1 ) INFO_message("IW3D_set_geometry_nwarp_catlist: padding = %d",xpad) ;
7120 #endif
7121 } else {
7122 hs = strdup(gs) ;
7123 }
7124
7125 /*-- set the internal strings in the catlist --*/
7126
7127 nwc->master_geomstring = gs ; /* the one we were told to use */
7128 nwc->actual_geomstring = hs ; /* the one we actually use */
7129
7130 #ifdef DEBUG_CATLIST
7131 if( verb_nww > 1 ) ININFO_message("results: master_geomstring = %s actual_geomstring = %s",gs,hs) ;
7132 #endif
7133
7134 /* Now regrid each dataset in the catlist (if needed) */
7135
7136 for( ii=0 ; ii < nwc->ncat ; ii++ ){
7137 if( !ISVALID_DSET(nwc->nwarp[ii]) ) continue ;
7138 gg = EDIT_get_geometry_string(nwc->nwarp[ii]) ;
7139 #ifdef DEBUG_CATLIST
7140 if( verb_nww > 1 ) ININFO_message("-- warp #%d geometry = %s %s",ii,gg,MEMORY_SHORT) ;
7141 #endif
7142 if( EDIT_geometry_string_diff(hs,gg) > 0.01f ){
7143 #ifdef DEBUG_CATLIST
7144 if( verb_nww > 1 ) ININFO_message(" regridding warp #%d to geometry = %s",ii,hs) ;
7145 #endif
7146 qset = THD_nwarp_regrid( nwc->nwarp[ii] , hs ) ;
7147 DSET_delete(nwc->nwarp[ii]) ;
7148 nwc->nwarp[ii] = qset ;
7149 }
7150 #ifdef DEBUG_CATLIST
7151 else if( verb_nww > 1 ) ININFO_message(" do not need to regrid warp #%d",ii) ;
7152 #endif
7153
7154 if( first && ISVALID_DSET(nwc->nwarp[ii]) ){
7155 nwc->actual_cmat = nwc->nwarp[ii]->daxes->ijk_to_dicom ; /* 04 Dec 2014 */
7156 nwc->actual_imat = MAT44_INV(nwc->actual_cmat) ; /* [oopsie] */
7157 first = 0 ;
7158 #ifdef DEBUG_CATLIST
7159 if( verb_nww > 1 ){
7160 DUMP_MAT44("nwarp_catlist actual_cmat",nwc->actual_cmat) ;
7161 DUMP_MAT44("nwarp_catlist actual_imat",nwc->actual_imat) ;
7162 }
7163 #endif
7164 }
7165
7166 }
7167
7168 EXRETURN ;
7169 }
7170
7171 /*----------------------------------------------------------------------------*/
7172 /* Go over a Nwarp_catlist struct and collapse/reduce operations that
7173 aren't dependent on the 'time' index.
7174 Note that if there aren't any multi-matrix 'time dependent' transformations,
7175 this should reduce the result down to a single warp dataset.
7176 *//*--------------------------------------------------------------------------*/
7177
IW3D_reduce_nwarp_catlist(Nwarp_catlist * nwc)7178 int IW3D_reduce_nwarp_catlist( Nwarp_catlist *nwc )
7179 {
7180 int ii,jj , ndone=0 , totaldone=0 ;
7181 int doall=0 ;
7182 mat44 cmat , imat ;
7183
7184 ENTRY("IW3D_reduce_nwarp_catlist") ;
7185
7186 if( nwc == NULL || nwc->ncat < 1 ) RETURN(totaldone) ;
7187
7188 if( nwc->actual_geomstring == NULL ) /* has to be set to something */
7189 IW3D_set_geometry_nwarp_catlist( nwc , NULL ) ; /* so set it now */
7190
7191 cmat = nwc->actual_cmat ; /* matrix to convert indexes to coords */
7192 imat = nwc->actual_imat ; /* and vice-versa */
7193
7194 #ifdef DEBUG_CATLIST
7195 if( verb_nww > 1 ){
7196 fprintf(stderr,"+++++ initial structure of Nwarp_catlist:") ;
7197 for( ii=0 ; ii < nwc->ncat ; ii++ ){
7198 if( NWC_nwarp(nwc,ii) != NULL ){
7199 fprintf(stderr," Nwarp ;") ;
7200 } else if( NWC_awarp(nwc,ii) != NULL ){
7201 fprintf(stderr," Matrix[%d] ;",nwc->awarp[ii]->nmar) ;
7202 } else {
7203 fprintf(stderr," (NULL) ;") ;
7204 }
7205 }
7206 fprintf(stderr,"\n") ;
7207 }
7208 #endif
7209
7210 /* collapse ii and jj neighbors if possible;
7211 note that any single matrix will be collapsed;
7212 on the first pass (doall==0), only matrix-matrix reduction is done */
7213
7214 Try_It_Again_Dude: /* target for loop back when ndone > 0 */
7215
7216 #ifdef DEBUG_CATLIST
7217 if( verb_nww > 1 ) INFO_message("-- IW3D_reduce_nwarp_catlist -- start loop %s",MEMORY_SHORT) ;
7218 #endif
7219
7220 for( ndone=ii=0 ; ii < nwc->ncat-1 ; ii=jj ){
7221
7222 /* if this entry is empty (doubly NULL), skip it */
7223
7224 if( NWC_null(nwc,ii) ){
7225 #ifdef DEBUG_CATLIST
7226 if( verb_nww > 1 ) ININFO_message(" nwc[%d] is doubly NULL",ii) ;
7227 #endif
7228 jj = ii+1 ; continue ; }
7229
7230 /* search for next non-empty neighbor above (jj) */
7231 #ifdef DEBUG_CATLIST
7232 if( verb_nww > 1 ) ININFO_message(" looking at nwc[%d]",ii) ;
7233 #endif
7234 for( jj=ii+1 ; jj < nwc->ncat ; jj++ ) if( !NWC_null(nwc,jj) ) break ;
7235 if( jj == nwc->ncat ) break ; /* nothing above, so we are done */
7236
7237 #ifdef DEBUG_CATLIST
7238 if( verb_nww > 1 ) ININFO_message(".. #%d and #%d are neighbors",ii,jj) ;
7239 #endif
7240
7241 /* neighbors are both matrices (matrix vectors?) ==> multiply them */
7242
7243 if( NWC_awarp(nwc,ii) != NULL && NWC_awarp(nwc,jj) != NULL ){
7244 mat44_vec *mvii=nwc->awarp[ii] , *mvjj=nwc->awarp[jj] , *mvnn ;
7245 mat44 mii , mjj , mkk ;
7246 int nii=mvii->nmar , njj=mvjj->nmar , ntop=MAX(nii,njj) , kk ;
7247 #ifdef DEBUG_CATLIST
7248 if( verb_nww > 1 ) ININFO_message(".. IW3D_reduce_nwarp_catlist: Matrix[%d]-Matrix[%d] ii=%d jj=%d",nii,njj,ii,jj) ;
7249 #endif
7250 mvnn = (mat44_vec *)malloc(sizeof(mat44_vec)) ;
7251 mvnn->nmar = ntop ;
7252 mvnn->mar = (mat44 *)malloc(sizeof(mat44)*ntop) ;
7253 for( kk=0 ; kk < ntop ; kk++ ){
7254 mii = M44V_mat(mvii,kk) ;
7255 mjj = M44V_mat(mvjj,kk) ;
7256 mkk = MAT44_MUL(mjj,mii) ; /* note that mjj pre-multiplies mii */
7257 mvnn->mar[kk] = mkk ; /* substitute into output vwarp */
7258 }
7259 /* now, delete #ii and #jj, and substitute #jj with the new mvnn */
7260 DESTROY_mat44_vec(mvii) ; nwc->awarp[ii] = NULL ;
7261 DESTROY_mat44_vec(mvjj) ; nwc->awarp[jj] = mvnn ;
7262 nwc->nwarp[ii] = nwc->nwarp[jj] = NULL ; /* nugatory */
7263 ndone++ ; continue ;
7264 }
7265
7266 /* neighbors are both nonlinear 3D warps ==> compose them */
7267
7268 if( doall && NWC_nwarp(nwc,ii) != NULL && NWC_nwarp(nwc,jj) != NULL ){
7269 THD_3dim_dataset *ids=nwc->nwarp[ii] , *jds=nwc->nwarp[jj] , *kds ;
7270 IndexWarp3D *iww , *jww , *kww ; char prefix[64] ;
7271 #ifdef DEBUG_CATLIST
7272 if( verb_nww > 1 ) ININFO_message(".. IW3D_reduce_nwarp_catlist: warp-warp ii=%d jj=%d",ii,jj) ;
7273 if( verb_nww > 1 ) ININFO_message(" ii=%d: geometry = %s",ii,EDIT_get_geometry_string(ids)) ;
7274 if( verb_nww > 1 ) ININFO_message(" jj=%d: geometry = %s",jj,EDIT_get_geometry_string(jds)) ;
7275 #endif
7276 iww = IW3D_from_dataset(ids,0,0) ;
7277 jww = IW3D_from_dataset(jds,0,0) ;
7278 kww = IW3D_compose(iww,jww,CW_interp) ;
7279 IW3D_adopt_dataset(kww,ids) ; sprintf(prefix,"Nwarp#%02d",jj+1) ;
7280 kds = IW3D_to_dataset(kww,prefix) ; DSET_superlock(kds) ;
7281 IW3D_destroy(iww) ; IW3D_destroy(jww) ; IW3D_destroy(kww) ;
7282 DSET_delete(ids) ;
7283 DSET_delete(jds) ; nwc->nwarp[jj] = kds ;
7284 nwc->awarp[ii] = nwc->awarp[jj] = NULL ; nwc->nwarp[ii] = NULL ;
7285 ndone++ ; continue ;
7286 }
7287
7288 /* neighbors are single matrix and nonlinear warp (in that order) */
7289
7290 if( doall && NWC_awarp(nwc,ii) != NULL && NWC_nwarp(nwc,jj) != NULL ){
7291 mat44_vec *mvii=nwc->awarp[ii] ;
7292 THD_3dim_dataset *jds=nwc->nwarp[jj] , *kds ;
7293 IndexWarp3D *jww , *kww ; char prefix[64] ;
7294 mat44 mii , qmat,tmat ;
7295 if( mvii->nmar == 1 ){
7296 #ifdef DEBUG_CATLIST
7297 if( verb_nww > 1 ) ININFO_message(".. IW3D_reduce_nwarp_catlist: Matrix[1]-warp ii=%d jj=%d",ii,jj) ;
7298 #endif
7299 qmat = mvii->mar[0] ; tmat = MAT44_MUL(qmat,cmat) ; mii = MAT44_MUL(imat,tmat) ;
7300 jww = IW3D_from_dataset(jds,0,0) ;
7301 kww = IW3D_compose_m1w2(mii,jww,CW_interp) ;
7302 IW3D_adopt_dataset(kww,jds) ; sprintf(prefix,"Nwarp#%02d",jj+1) ;
7303 kds = IW3D_to_dataset(kww,prefix) ; DSET_superlock(kds) ;
7304 DESTROY_mat44_vec(mvii) ; IW3D_destroy(kww) ; DSET_delete(jds) ;
7305 nwc->nwarp[jj] = kds ;
7306 nwc->awarp[ii] = nwc->awarp[jj] = NULL ; nwc->nwarp[ii] = NULL ;
7307 ndone++ ;
7308 }
7309 #ifdef DEBUG_CATLIST
7310 else if( verb_nww > 1 ) ININFO_message(".. IW3D_reduce_nwarp_catlist: Matrix[%d]-warp ii=%d jj=%d -- skipped",mvii->nmar,ii,jj) ;
7311 #endif
7312 continue ;
7313 }
7314
7315 /* neighbors are nonlinear warp and single matrix (in that order) */
7316
7317 if( doall && NWC_nwarp(nwc,ii) != NULL && NWC_awarp(nwc,jj) != NULL ){
7318 mat44_vec *mvjj=nwc->awarp[jj] ;
7319 THD_3dim_dataset *ids=nwc->nwarp[ii] , *kds ;
7320 IndexWarp3D *iww , *kww ; char prefix[64] ;
7321 mat44 mjj , qmat,tmat ;
7322 if( mvjj->nmar == 1 ){
7323 #ifdef DEBUG_CATLIST
7324 if( verb_nww > 1 ) ININFO_message("IW3D_reduce_nwarp_catlist: warp-Matrix[1] ii=%d jj=%d",ii,jj) ;
7325 #endif
7326 qmat = mvjj->mar[0] ; tmat = MAT44_MUL(qmat,cmat) ; mjj = MAT44_MUL(imat,tmat) ;
7327 iww = IW3D_from_dataset(ids,0,0) ;
7328 kww = IW3D_compose_w1m2(iww,mjj,CW_interp) ;
7329 IW3D_adopt_dataset(kww,ids) ; sprintf(prefix,"Nwarp#%02d",jj+1) ;
7330 kds = IW3D_to_dataset(kww,prefix) ; DSET_superlock(kds) ;
7331 DESTROY_mat44_vec(mvjj) ; IW3D_destroy(kww) ; DSET_delete(ids) ;
7332 nwc->nwarp[jj] = kds ;
7333 nwc->awarp[ii] = nwc->awarp[jj] = NULL ; nwc->nwarp[ii] = NULL ;
7334 ndone++ ;
7335 }
7336 #ifdef DEBUG_CATLIST
7337 else if( verb_nww > 1 ) ININFO_message("IW3D_reduce_nwarp_catlist: warp-Matrix[%d] ii=%d jj=%d -- skipped",mvjj->nmar,ii,jj) ;
7338 #endif
7339 continue ;
7340 }
7341
7342 /* no other case on which to operate */
7343 }
7344
7345 #ifdef DEBUG_CATLIST
7346 if( verb_nww > 1 ) ININFO_message("-- loop reduction operation count = %d %s",ndone,MEMORY_SHORT) ;
7347 #endif
7348 totaldone += ndone ;
7349 if( ndone > 0 || !doall ){ doall = 1 ; goto Try_It_Again_Dude ; }
7350
7351 /*--- at this point, go thru and eliminate the doubly NULL entries ---*/
7352
7353 for( ii=0 ; ii < nwc->ncat ; ii++ ){
7354 if( ! NWC_null(nwc,ii) ) continue ; /* not doubly NULL */
7355
7356 /* move upper entries down by one */
7357
7358 for( jj=ii+1 ; jj < nwc->ncat ; jj++ ){
7359 if( nwc->awarp != NULL ) nwc->awarp[jj-1] = nwc->awarp[jj] ;
7360 if( nwc->nwarp != NULL ) nwc->nwarp[jj-1] = nwc->nwarp[jj] ;
7361 }
7362
7363 nwc->ncat-- ; /* one less entry now */
7364 }
7365
7366 /*--- if have only 1 warp left, and inversion is indicated, do it now ---*/
7367
7368 if( nwc->ncat == 1 && (nwc->flags & NWC_INVERT_MASK) ){
7369 THD_3dim_dataset *pset = NWC_nwarp(nwc,0) ;
7370 THD_3dim_dataset *qset = THD_nwarp_invert(pset) ;
7371 DSET_delete(pset) ; nwc->nwarp[0] = qset ; DSET_superlock(qset) ;
7372 nwc->flags &= ~(int)NWC_INVERT_MASK ; /* turn off invert flag */
7373 totaldone++ ;
7374 }
7375
7376 /** More general way to deal with inversion would be to
7377 (a) reverse the order of all the warps, and
7378 (b) invert them all (including multi-line matrices)
7379 This would save having to do inversions in IW3D_from_nwarp_catlist **/
7380
7381 #ifdef DEBUG_CATLIST
7382 if( verb_nww > 1 ){
7383 fprintf(stderr,"+++++ final structure of Nwarp_catlist: %s",MEMORY_SHORT) ;
7384 for( ii=0 ; ii < nwc->ncat ; ii++ ){
7385 if( NWC_nwarp(nwc,ii) != NULL ){
7386 fprintf(stderr," Nwarp ;") ;
7387 } else if( NWC_awarp(nwc,ii) != NULL ){
7388 fprintf(stderr," Matrix[%d] ;",nwc->awarp[ii]->nmar) ;
7389 } else {
7390 fprintf(stderr," (NULL) ;") ;
7391 }
7392 }
7393 fprintf(stderr,"\n") ;
7394 }
7395 #endif
7396
7397 RETURN(totaldone) ;
7398 }
7399
7400 /*----------------------------------------------------------------------------*/
7401 /* Return the vind-th 'time' warp from the list. */
7402
IW3D_from_nwarp_catlist(Nwarp_catlist * nwc,int vind)7403 THD_3dim_dataset * IW3D_from_nwarp_catlist( Nwarp_catlist *nwc , int vind )
7404 {
7405 int ii , do_regrid=0 ;
7406 char *prefix = "NwarpFromCatlist" ;
7407 mat44 wmat , tmat , smat , qmat , cmat,imat ;
7408 IndexWarp3D *warp=NULL , *tarp=NULL , *qarp=NULL ;
7409 THD_3dim_dataset *oset , *iset=NULL , *qset=NULL ;
7410
7411 ENTRY("IW3D_from_nwarp_catlist") ;
7412
7413 if( nwc == NULL ) RETURN(NULL) ;
7414 if( vind < 0 ) vind = 0 ; /* stupid user */
7415
7416 /* a single transformation ==> should be easy */
7417
7418 if( nwc->ncat == 1 ){ /* return a copy, so it can be deleted */
7419 oset = NWC_nwarp(nwc,0) ;
7420 if( nwc->flags & NWC_INVERT_MASK ) qset = THD_nwarp_invert(oset) ; /* not possible */
7421 else qset = EDIT_full_copy(oset,"Copy_Of_Nwarp") ;
7422 DSET_superlock(qset) ; RETURN(qset) ;
7423 }
7424
7425 /* otherwise, need to cat the list of transformations */
7426 /* start by initializing current state of cumulative warp */
7427
7428 LOAD_IDENT_MAT44(wmat) ; warp = NULL ;
7429 cmat = nwc->actual_cmat ; /* matrix to convert indexes to coords */
7430 imat = nwc->actual_imat ; /* and vice-versa */
7431
7432 #ifdef DEBUG_CATLIST
7433 if( verb_nww > 1 ) fprintf(stderr,"{nwarp_catlist[%d]",vind) ;
7434 #endif
7435
7436 for( ii=0 ; ii < nwc->ncat ; ii++ ){ /* warp catenation loop */
7437
7438 if( NWC_awarp(nwc,ii) != NULL ){ /* matrix to apply */
7439 mat44_vec *mvii=nwc->awarp[ii] ;
7440 qmat = M44V_mat(mvii,vind) ; /* pick out the vind-th matrix */
7441 tmat = MAT44_MUL(qmat,cmat) ;
7442 smat = MAT44_MUL(imat,tmat) ; /* convert from xyz warp to ijk warp */
7443 if( verb_nww > 2 ){
7444 float a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34 ;
7445 UNLOAD_MAT44(smat,a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34) ;
7446 fprintf(stderr," [new mat=%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f]",a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34);
7447 }
7448
7449 if( warp == NULL ){ /* thus far, only matrix warps */
7450 #ifdef DEBUG_CATLIST
7451 if( verb_nww > 1 ) fprintf(stderr," [matrix*matrix]") ;
7452 #endif
7453 qmat = MAT44_MUL(smat,wmat) ; wmat = qmat ; /* so multiply matrices */
7454
7455 } else { /* instead, apply matrix to nonlinear warp */
7456 #ifdef DEBUG_CATLIST
7457 if( verb_nww > 1 ) fprintf(stderr," [warp*matrix]") ;
7458 #endif
7459 tarp = IW3D_compose_w1m2(warp,smat,CW_interp) ;
7460 IW3D_destroy(warp) ; warp = tarp ;
7461 }
7462
7463 } else if( NWC_nwarp(nwc,ii) != NULL ){ /* nonlinear warp to apply */
7464
7465 qarp = IW3D_from_dataset(nwc->nwarp[ii],0,0) ;
7466 if( iset == NULL ) iset = nwc->nwarp[ii] ;
7467
7468 if( warp == NULL ){ /* create nonlinear warp at this point */
7469 if( ISIDENT_MAT44(wmat) ){ /* don't compose with identity matrix */
7470 #ifdef DEBUG_CATLIST
7471 if( verb_nww > 1 ) fprintf(stderr," [warp]") ;
7472 #endif
7473 warp = qarp ; qarp = NULL ;
7474 } else { /* compose with previous matrix */
7475 #ifdef DEBUG_CATLIST
7476 if( verb_nww > 1 ) fprintf(stderr," [matrix*warp]") ;
7477 #endif
7478 warp = IW3D_compose_m1w2(wmat,qarp,CW_interp) ;
7479 }
7480 } else { /* already have nonlinear warp, apply new one to it */
7481 #ifdef DEBUG_CATLIST
7482 if( verb_nww > 1 ) fprintf(stderr," [warp*warp]") ;
7483 #endif
7484 tarp = IW3D_compose(warp,qarp,CW_interp) ;
7485 IW3D_destroy(warp) ; warp = tarp ;
7486 }
7487
7488 IW3D_destroy(qarp) ;
7489
7490 } /* end of processing nonlinear warp */
7491
7492 /* a null entry (which could have been reduced away) is just skipped */
7493 #ifdef DEBUG_CATLIST
7494 else if( verb_nww > 1 ) fprintf(stderr," [NULL entry?]") ;
7495 #endif
7496
7497 } /* end of loop over input warps */
7498
7499 #ifdef DEBUG_CATLIST
7500 if( verb_nww > 1 ) fprintf(stderr,"}") ;
7501 #endif
7502
7503 /*--- create output dataset ---*/
7504
7505 if( warp == NULL ){
7506 ERROR_message("IW3D_from_nwarp_catlist: this message should never appear!!") ;
7507 RETURN(NULL) ;
7508 }
7509
7510 IW3D_adopt_dataset( warp , iset ) ;
7511 oset = IW3D_to_dataset( warp , prefix ) ;
7512 IW3D_destroy(warp) ;
7513 if( nwc->flags & NWC_INVERT_MASK ){
7514 qset = THD_nwarp_invert(oset) ;
7515 DSET_delete(qset) ; oset = qset ;
7516 }
7517
7518 DSET_superlock(oset) ; RETURN(oset) ;
7519 }
7520
7521 /*----------------------------------------------------------------------------*/
7522 /* Construct an Nwarp_catlist from a list of strings (filenames).
7523 This function is used in 3dNwarpApply.
7524 *//*--------------------------------------------------------------------------*/
7525
IW3D_read_nwarp_catlist(char * cstr)7526 Nwarp_catlist * IW3D_read_nwarp_catlist( char *cstr )
7527 {
7528 char prefix[64] , *cp ;
7529 mat44 tmat , smat , qmat ;
7530 THD_3dim_dataset *nset ;
7531 NI_str_array *csar ; int ii,kk ;
7532 Nwarp_catlist *nwc=NULL ;
7533 char *geomstring=NULL , *hs ;
7534 int nvar=1 , do_regrid=0 , nbad=0 , gs_mismatch=0 ;
7535 mat44_vec *mvv ; mat44 mmm ;
7536 float dxmax=0.0f,dymax=0.0f,dzmax=0.0f ; float_triple dxyz ;
7537
7538 ENTRY("IW3D_read_nwarp_catlist") ;
7539
7540 if( cstr == NULL || *cstr == '\0' ) RETURN(NULL) ;
7541
7542 #ifdef DEBUG_CATLIST
7543 if( verb_nww > 1 ) INFO_message("Enter IW3D_read_nwarp_catlist( %s )",cstr) ;
7544 #endif
7545
7546 csar = NI_decode_string_list(cstr,";") ;
7547 if( csar == NULL || csar->num < 1 ) RETURN(NULL) ;
7548
7549 if( csar->num > CW_NMAX ){
7550 ERROR_message("Too many components (%d > %d) for IW3D_read_nwarp_catlist",
7551 csar->num , CW_NMAX ) ;
7552 NI_delete_str_array(csar) ;
7553 RETURN(NULL) ;
7554 }
7555
7556 /*-------- simple case of a single dataset input --------*/
7557
7558 if( csar->num == 1 ){
7559 #ifdef DEBUG_CATLIST
7560 if( verb_nww > 1 ) ININFO_message("Open single warp dataset %s",csar->str[0]) ;
7561 #endif
7562 cp = csar->str[0] ;
7563 if( strcasestr(cp,".1D") != NULL || strcasestr(cp,".txt") != NULL ){
7564 ERROR_message("Warp dataset '%s' name is too much like a matrix text file :-?",cp) ;
7565 NI_delete_str_array(csar) ; RETURN(NULL) ;
7566 }
7567 nset = CW_read_dataset_warp(cp) ; /* does INV() etc. */
7568 if( nset == NULL ){
7569 ERROR_message("Can't open 3D warp dataset '%s'",cp) ;
7570 NI_delete_str_array(csar) ; RETURN(NULL) ;
7571 }
7572 DSET_load(nset) ;
7573 if( !DSET_LOADED(nset) ){
7574 ERROR_message("Warp dataset '%s' can't be loaded into memory",cp) ;
7575 NI_delete_str_array(csar) ; DSET_delete(nset) ; RETURN(NULL) ;
7576 }
7577
7578 /* manufacture the very short output list */
7579
7580 nwc = (Nwarp_catlist *)malloc(sizeof(Nwarp_catlist)) ;
7581 nwc->ncat = nwc->nvar = 1 ;
7582 nwc->nwarp = (THD_3dim_dataset **)malloc(sizeof(THD_3dim_dataset *)) ;
7583 nwc->nwarp[0] = nset ;
7584 nwc->awarp = NULL ; /* trivial case == no matrices */
7585 nwc->flags = 0 ;
7586
7587 nwc->xshift = nwc->yshift = nwc->zshift = 0.0f ;
7588 hs = EDIT_get_geometry_string(nset) ;
7589 IW3D_set_geometry_nwarp_catlist( nwc , hs ) ;
7590
7591 NI_delete_str_array(csar) ;
7592 RETURN(nwc) ;
7593 }
7594
7595 /*-------- multiple input datasets / matrices --------*/
7596
7597 nwc = (Nwarp_catlist *)malloc(sizeof(Nwarp_catlist)) ;
7598 nwc->nvar = 1 ; /* no matrices yet: update in loop below */
7599 nwc->ncat = csar->num ;
7600 nwc->awarp = (mat44_vec **)calloc( sizeof(mat44_vec *),csar->num ) ;
7601 nwc->nwarp = (THD_3dim_dataset **)calloc( sizeof(THD_3dim_dataset *),csar->num ) ;
7602 nwc->flags = 0 ;
7603 nwc->actual_geomstring = NULL ; /* must be computed later */
7604 nwc->master_geomstring = NULL ;
7605 nwc->xshift = nwc->yshift = nwc->zshift = 0.0f ;
7606 nwc->xpad = nwc->ypad = nwc->zpad = 0 ;
7607
7608 for( ii=0 ; ii < csar->num ; ii++ ){ /* read them all in, Frank */
7609 cp = csar->str[ii] ;
7610
7611 /*-- read in a vector of matrices (technically a "free module" element) --*/
7612
7613 if( strcasestr(cp,".1D") != NULL || strcasestr(cp,".txt") != NULL ){
7614 #ifdef DEBUG_CATLIST
7615 if( verb_nww > 1 ) ININFO_message("Open matrix file %s",cp) ;
7616 #endif
7617 mvv = CW_read_affine_warp(cp) ;
7618 if( mvv == NULL || mvv->nmar <= 0 || mvv->mar == NULL ){ /* bad bad bad */
7619 ERROR_message("Failed to read affine warp from '%s'",cp) ;
7620 nbad++ ; continue ;
7621 }
7622 dxyz = M44_max_shifts(mvv) ;
7623 #ifdef DEBUG_CATLIST
7624 if( verb_nww > 1 ) ININFO_message(" max shifts = %g %g %g",dxyz.a,dxyz.b,dxyz.c) ;
7625 #endif
7626 dxmax += dxyz.a ; dymax += dxyz.b ; dzmax += dxyz.c ;
7627 if( mvv->nmar > nwc->nvar ) nwc->nvar = mvv->nmar ; /* update of max */
7628
7629 nwc->awarp[ii] = mvv ; /* insert into output */
7630 nwc->nwarp[ii] = NULL ;
7631
7632 } else { /*-- read in a nonlinear 3D warp (from a dataset) --*/
7633
7634 #ifdef DEBUG_CATLIST
7635 if( verb_nww > 1 ) ININFO_message("Open dataset warp %s",cp) ;
7636 #endif
7637 nset = CW_read_dataset_warp(cp) ;
7638 if( nset == NULL ){
7639 ERROR_message("Can't open/process 3D warp dataset '%s'",cp) ;
7640 nbad++ ; continue ;
7641 }
7642
7643 dxyz = THD_nwarp_maxdisp(nset) ;
7644 #ifdef DEBUG_CATLIST
7645 if( verb_nww > 1 ) ININFO_message(" max displacments = %g %g %g",dxyz.a,dxyz.b,dxyz.c) ;
7646 #endif
7647 dxmax += dxyz.a ; dymax += dxyz.b ; dzmax += dxyz.c ;
7648
7649 hs = EDIT_get_geometry_string(nset) ; ;
7650 if( geomstring == NULL ){
7651 geomstring = hs ;
7652 } else if( EDIT_geometry_string_diff(geomstring,hs) > 0.01f ){
7653 #ifdef DEBUG_CATLIST
7654 if( verb_nww > 1 ) ININFO_message(" found geometry mismatch: orig=%s new=%s",geomstring,hs) ;
7655 #endif
7656 gs_mismatch++ ;
7657 }
7658
7659 nwc->nwarp[ii] = nset ; /* insert into output */
7660 nwc->awarp[ii] = NULL ;
7661 }
7662
7663 } /* end of loop over input sub-strings */
7664
7665 NI_delete_str_array(csar) ; /* toss the jetsam */
7666
7667 if( geomstring == NULL || nbad > 0 ){ /* bad inputs */
7668 ERROR_message("Can't compute nonlinear 3D warp from string '%s'",cstr) ;
7669 IW3D_destroy_nwarp_catlist(nwc) ; RETURN(NULL) ;
7670 }
7671
7672 /* save the sum of maximum shifts encountered */
7673
7674 nwc->xshift = dxmax ; nwc->yshift = dymax ; nwc->zshift = dzmax ;
7675 #ifdef DEBUG_CATLIST
7676 if( verb_nww > 1 ) ININFO_message("--- Totalized max displacments = %g %g %g",dxmax,dymax,dzmax) ;
7677 #endif
7678
7679 /* if all nonlinear warps had same grid, save it in the catlist */
7680
7681 if( !gs_mismatch ){
7682 IW3D_set_geometry_nwarp_catlist( nwc , geomstring ) ;
7683 ii = IW3D_reduce_nwarp_catlist( nwc ) ; /* and can 'reduce' it now */
7684 #ifdef DEBUG_CATLIST
7685 if( verb_nww > 1 && ii > 0 ) ININFO_message("Reduced catlist by %d steps",ii) ;
7686 #endif
7687 }
7688
7689 RETURN(nwc) ;
7690 }
7691
7692 #endif /*(C17)*/ /*###########################################################*/
7693
7694 /******************************************************************************/
7695 /******************************************************************************/
7696 #ifdef ALLOW_QWARP /** the following code is for 3dQwarp (duh) **/
7697 /******************************************************************************/
7698 /******************************************************************************/
7699
7700 #if 1
7701 /*============================================================================*/
7702 /** (Q1) Introduction to 3dQwarp, and Global variables for 3dQwarp-ing **/
7703 /*============================================================================*/
7704
7705 /**--------------------------------------------------------------------------**/
7706 /** The basic 3dQwarp run is an invocation of function IW3D_warp_s2bim()
7707 (s2bim == "source to base image"), which returns a struct combining the
7708 final index warp and warped source image. The outline of function calls
7709 follows (some minor calls are omitted -- I'm sure you'll figure them out):
7710
7711 (0) 3dQwarp main() == sets up the global variables (cf. infra) from
7712 the options and inputs, calls IW3D_warp_s2bim(),
7713 and then writes the outputs
7714 (1) IW3D_warp_s2bim() == driver function for IW3D_warpomatic()
7715 and applies the warp to the source image
7716 (2a) IW3D_warpomatic() == produces the optimized warp, by driving
7717 IW3D_improve_warp() over global and then
7718 local (overlapping) patches
7719 (3a) IW3D_setup_for_improvement() == sets up data for warp optimizing;
7720 called at IW3D_warpomatic() start
7721 (4) IW3D_load_energy() == compute 'energy' fields for global warp
7722 as it is now, for warp penalty later
7723 -- if the inital warp (S2BIM_iwarp) is
7724 not NULL, then this step is important
7725 (3b) IW3D_improve_warp() == called many many times over shrinking
7726 patches; this function optimizes the
7727 incremental warp Hwarp and then updates
7728 the global warp Haawarp -- the eventual
7729 output of IW3D_warpomatic()
7730 (4a) H?warp_setup_basis() == sets up basis polynomials for patch warp
7731 (? == Q for quintic, C for cubic)
7732 (4b) INCOR_create() == sets up struct for incremental 'correlation'
7733 calculations -- first computes with the data
7734 outside the optimizing patch and later gets
7735 finalized each time the patch is updated
7736 (4c) HPEN_addup() == initialize warp penalty sum for points outside
7737 the current patch (from the 'energy' fields)
7738 (4c) powell_newuoa_con() == Powell NEWUOA constrained optimizer
7739 which actually optimizes Hwarp by
7740 playing with the coefficients of the
7741 warp basis polynomials (cf. powell_int.c)
7742 (5) IW3D_scalar_costfun() == function being optimized
7743 (5a) Hwarp_apply() == computes patch warp Hwarp, composes it with
7744 current global warp Haawarp, and then applies
7745 it to source image to produce the patch data
7746 (using linear interpolation)
7747 (6) H?warp_eval_X() == evaluate the patch warp given the current
7748 parameters (? == Q or C; X == A or B,
7749 depending on the size of the patch); uses
7750 data setup from H?warp_setup_basis()
7751 (5b) INCOR_evaluate() == completes the 'incomplete' correlation
7752 using the patch data just computed;
7753 this is the measurement of image matching
7754 (5c) HPEN_penalty() == finalizes the penalty given the patch warp
7755 (6) IW3D_load_energy() = computes the 'energy' of the patch warp
7756 (3c) IW3D_cleanup_improvement() == free workspaces created in
7757 IW3D_setup_for_improvement()
7758 (2b) IW3D_warp_floatim() == produces the warped source image
7759 (using wsinc5 interpolation)
7760
7761 There is an unfinished function THD_warpomatic() which was intended to
7762 provide a simple way to warp one dataset to another, but that was never
7763 completed. At present, you have to use IW3D_warp_s2bim(), which requires
7764 MRI_IMAGEs as the inputs, not datasets.
7765
7766 3dQwarp is *supposed* to work with 2D images (nz=1), and there is code
7767 to allow for that case -- but it has never been tested! (OK, once.)
7768
7769 The INCOR_* functions are in thd_incorrelate.c (which is #include-d far
7770 far above), and handles "incomplete correlation" calculations, where part
7771 of the data is fixed (the part outside the patch being optimized), and
7772 part is changing (the part inside the current patch). For speed, the
7773 computations for the fixed part are only done once -- via INCOR_addto()
7774 in function IW3D_improve_warp() before the patch optimization is done.
7775 The "correlation" (cost function) is completed via INCOR_evaluate() in
7776 function IW3D_scalar_costfun() -- which is what powell_newuoa_con()
7777 tries to minimize.
7778 *//**------------------------------------------------------------------------**/
7779
7780 /**--------------------------------------------------------------------------**/
7781 /** Many (but not all) global variables below start with 'H',
7782 which is my notation for the warp patch being optimized;
7783 however, not all 'H' variables are about the patch itself;
7784 some of these get set in 3dQwarp.c to control the warp optimization. **/
7785 /**--------------------------------------------------------------------------**/
7786
7787 /*--- flag masks for types of displacement allowed (for Hflags, etc.) ---*/
7788
7789 #define NWARP_NOXDIS_FLAG 1 /* no displacment in X direction? */
7790 #define NWARP_NOYDIS_FLAG 2
7791 #define NWARP_NOZDIS_FLAG 4
7792
7793 #define NWARP_NODISP_FLAG (NWARP_NOXDIS_FLAG | NWARP_NOYDIS_FLAG | NWARP_NOZDIS_FLAG)
7794
7795 #define NWARP_NOXDEP_FLAG 8 /* no functional dependence of displacment on X? */
7796 #define NWARP_NOYDEP_FLAG 16
7797 #define NWARP_NOZDEP_FLAG 32
7798
7799 /*** We allow 2 types of polynomial patches: cubics and quintics.
7800 Quintic patches have 3*3*3*3=81 parameters, and so
7801 take much more time to evaluate and optimize than
7802 cubic patches, which have only 3*2*2*2 = 24 parameters.
7803 Quintic patches are C2 (2 continuous derivatives),
7804 while cubic patches are C1 (1 continuous derivative). ***/
7805
7806 /*--- Hermite polynomial basis arrays for each direction: x,y,z. ---*/
7807 /* ('c' for cubic, 'q' for quintic) */
7808
7809 static int nbcx=0, nbcy=0, nbcz=0 , nbcxy=0 ; /* dimensions of patch */
7810 static int nbqx=0, nbqy=0, nbqz=0 , nbqxy=0 ; /* dimensions of patch */
7811 static int nbsx=0, nbsy=0, nbsz=0 , nbsxy=0 ;
7812
7813 /* 1D basis arrays for 2 cubics (#0 and #1) */
7814
7815 static float *bc0x=NULL, *bc1x=NULL, dxci=0.0f ;
7816 static float *bc0y=NULL, *bc1y=NULL, dyci=0.0f ;
7817 static float *bc0z=NULL, *bc1z=NULL, dzci=0.0f ;
7818
7819 static float *bc2x=NULL, *bc3x=NULL, *bc4x=NULL ; /* 05 Nov 2015 */
7820 static float *bc2y=NULL, *bc3y=NULL, *bc4y=NULL ;
7821 static float *bc2z=NULL, *bc3z=NULL, *bc4z=NULL ;
7822 static float *bcxx[5] , *bcyy[5] , *bczz[5] ;
7823
7824 /* for compact sinc */
7825
7826 static float *bs0x=NULL , *bs0y=NULL , *bs0z=NULL , dxsi=0.0f,dysi=0.0f,dzsi=0.0f ;
7827
7828 /* 1D basis arrays for 3 quintics (#0, #1, and #2) */
7829
7830 static float *bq0x=NULL, *bq1x=NULL, *bq2x=NULL, dxqi=0.0f ;
7831 static float *bq0y=NULL, *bq1y=NULL, *bq2y=NULL, dyqi=0.0f ;
7832 static float *bq0z=NULL, *bq1z=NULL, *bq2z=NULL, dzqi=0.0f ;
7833
7834 /* 3D basis arrays for cubics and quintics (used for 'small' patches) */
7835
7836 static int nbbcxyz = 0 ;
7837 static int nbbbcar = 0 ;
7838 static float **bbbcar = NULL ;
7839 static int nbbqxyz = 0 ;
7840 static float **bbbqar = NULL ;
7841
7842 static int nbbsxyz = 0 ;
7843 static float *bbbsar = NULL ;
7844
7845 #undef MEGA
7846 #define MEGA 1048576 /* 2^20 = definition of 'small' */
7847
7848 #undef GIGA
7849 #define GIGA (1000.0f*(float)MEGA) /* 1000 mega (float to avoid int overflow) */
7850
7851 static float HmaxmemG = 1.0f ; /* gigabytes allowed for basis func storage */
7852
7853 /*--- local (small) warp over region we are optimizing ---*/
7854
7855 static IndexWarp3D *Hwarp = NULL ; /* Hermite patch increment = H(x) = 'patch warp' */
7856 static IndexWarp3D *AHwarp = NULL ; /* local version of global warp = A(H(x)) */
7857 static int need_AH = 1 ; /* flag to compute AHwarp (not always needed) */
7858 static int Hflags = 0 ; /* flags for this patch */
7859 static int Hgflags = 0 ; /* flags for global warp */
7860 static int Himeth = MRI_LINEAR ; /* interpolation method for warped data */
7861 /* MRI_LINEAR or MRI_NN */
7862
7863 static int Hnx=0,Hny=0,Hnz=0,Hnxy=0,Hnxyz=0 ; /* dimensions of base image */
7864
7865 /* defines the patch region index ranges (inclusive);
7866 e.g., Hibot and Hitop are in range 0..Hnx-1, and so forth */
7867
7868 static int Hibot,Hitop , Hjbot,Hjtop , Hkbot,Hktop ;
7869
7870 static int Hmatch_code = 0 ; /* how 'correlation' is computed (INCOR_) */
7871 static int Hbasis_code = 0 ; /* quintic or cubic patches? */
7872
7873 #define MRI_CUBIC_PLUS_1 301 /* 05 Nov 2015 -- extra basis function codes */
7874 #define MRI_CUBIC_PLUS_2 302 /* for the BASIS5 methods */
7875 #define MRI_CUBIC_PLUS_3 303
7876
7877 #define MRI_SINCC 311 /* 05 Nov 2019 -- 3 basis funcs (sinc compact) */
7878
7879 /* Reduced rank basis funcs (from Guangzhou) */
7880 #define MRI_CUBIC_LITE 398 /* 06 Dec 2018 -- 12 instead of 24 */
7881 #define MRI_QUINTIC_LITE 399 /* 11 Dec 2018 -- 30 instead of 81 */
7882
7883 static int H5nparm = 0 ; /* number of basis function for BASIS5 methods */
7884 static int H5final = 0 ; /* Use basis5 at final level (1 or 2 or 3) */
7885 static int H5zero = 0 ; /* 14 Dec 2018 */
7886
7887 static double Hbasis_parmax = 0.0 ; /* max warp parameter allowed */
7888
7889 static floatvec *Hmpar = NULL ; /* parameters for 'correlation' match */
7890
7891 static const int Hlocalstat = 0 ; /* this should not be used !!! */
7892
7893 /* these values are for the purpose of printing out some statistics */
7894
7895 static int Hskipped = 0 ; /* number of patches skipped */
7896 static int Hdone = 0 ; /* number of patches optimized */
7897
7898 /*--- Other stuff for incremental warping ---*/
7899
7900 #undef USE_HLOADER /* Define this for 'all-at-once' Hwarp load vs. incremental */
7901 /* Tests show incremental is about 10% faster, with OpenMP. */
7902 /* (incremental means to compute as needed, vs. pre-compute) */
7903 /* Note that USE_HLOADER does NOT work with the basis5 funcs */
7904 /* [Dec 2018] nor with the _LITE functions */
7905
7906 static int Hnpar = 0 ; /* num params for warp */
7907 static int Hnpar_sum = 0 ; /* total num params used in course of run */
7908 static float *Hpar = NULL ; /* param vector for local warp definition */
7909 static float *Hxpar ; /* sub-array of params for x displacement */
7910 static float *Hypar ;
7911 static float *Hzpar ;
7912 static int Hdox ; /* do we compute x displacements? */
7913 static int Hdoy ;
7914 static int Hdoz ;
7915 static int *Hparmap = NULL ; /* map from active to all params */
7916 static int Hnparmap = 0 ;
7917 static int Hnegate = 0 ; /* negate correlation function? */
7918 static int Hnval = 0 ; /* number of voxels in local patch */
7919 static float *Hwval = NULL ; /* warped image values in local patch */
7920 static float *Haawt = NULL ; /* weight iamge (sic) in local patch */
7921 static float *Hbval = NULL ; /* base image in local patch */
7922 static MRI_IMAGE *Hsrcim = NULL ; /* source image to warp (global) */
7923 static MRI_IMAGE *Hsrcim_blur = NULL ; /* blurred source image */
7924 static float Hblur_b = 0.0f ; /* amount to blur base */
7925 static float Hblur_s = 0.0f ; /* amount to blur source */
7926 static float Hpblur_b = 0.0f ; /* progressive blur fraction: base */
7927 static float Hpblur_s = 0.0f ; /* progressive blur fraction: source */
7928 static int Hforce = 0 ; /* force an iterative warp update? */
7929 static int Hzeasy = 0 ; /* take it easy at the zero level? */
7930 static int Hznoq = 0 ; /* don't do quintic warp at the zero level? */
7931 static float Hfactor = 1.0f ; /* fraction of maximum warp size allowed */
7932 static float Hfactor_q = 1.0f ; /* set in 3dQwarp for rescaling Hfactor */
7933 static float Hshrink = 0.749999f ; /* shrink factor for patches between levels */
7934 static int Hngmin = 25 ; /* min patch size allowed in current run */
7935 static IndexWarp3D *Haawarp = NULL ; /* global warp we are improving = A(x) */
7936 /* AHwarp above is the patch copy of this */
7937 static void *Hincor = NULL ; /* INCOR 'correlation' struct */
7938 static MRI_IMAGE *Haasrcim = NULL ; /* warped source image (global) */
7939
7940 static MRI_IMAGE *Hbasim = NULL ; /* base image (global) */
7941 static MRI_IMAGE *Hbasim_blur = NULL ; /* blurred base image */
7942 static MRI_IMAGE *Hwtim = NULL ; /* weight image (global) */
7943 static float Hwbar = 0.0f ; /* average weight value */
7944 static byte *Hbmask = NULL ; /* mask for base image (global) */
7945 static byte *Hemask = NULL ; /* mask of voxels to EXCLUDE */
7946
7947 /*** these variables are inputs from the -xyzmatch option of 3dQwarp ***/
7948
7949 static int Hxyzmatch_num = 0 ; /* num xyz triples for pointwise match */
7950 static double Hxyzmatch_fac = 0.0 ; /* factor for sum */
7951 static int Hxyzmatch_pow = 2 ; /* 1 for L1, 2 for L2 */
7952 static double Hxyzmatch_cost = 0.0 ;
7953 static float *Hxyzmatch_bas[3] = {NULL,NULL,NULL} ; /* xyz triples in base */
7954 static float *Hxyzmatch_src[3] = {NULL,NULL,NULL} ; /* xyz triples in source */
7955 static float *Hxyzmatch_b2s[3] = {NULL,NULL,NULL} ; /* warped base triples */
7956
7957 /*** these variables affect how the iteration starts, stops, and proceeds ***/
7958
7959 static int Hlev_start = 0 ; /* initial level of patches */
7960 static int Hlev_end = 666 ; /* final level of patches to head towards */
7961 static int Hlev_final = 0 ; /* final level actually reached */
7962 static int Hlev_now = 0 ; /* the level we are playing with at the moment */
7963 static int Hfinal = 0 ; /* is this the final level we are working on now? */
7964 static int Hworkhard1 = 0 ; /* workhard stuff (but who wants to work hard?) */
7965 static int Hworkhard2 = -1 ;
7966
7967 static int Hopt_ball = 0 ; /* 'BALL' optimization strategy? [13 Jan 2015] */
7968
7969 static int Hgridlist_num = 0 ; /* 31 Dec 2014 */
7970 static int *Hgridlist = NULL ;
7971 #define HAVE_HGRID ( Hgridlist_num > 0 && Hgridlist != NULL )
7972 #define HGRID(gg) \
7973 ( ( HAVE_HGRID && (gg) >= 0 && (gg) < Hgridlist_num ) ? Hgridlist[gg] : -666 )
7974
7975 static int Hsave_allwarps = 0 ; /* 02 Jan 2015 */
7976 static int Hsave_num = 0 ;
7977 static IndexWarp3D **Hsave_iwarp = NULL ;
7978 static char **Hsave_iname = NULL ;
7979
7980 static void (*Hsave_callback_func)(IndexWarp3D * , char *) = NULL ; /* 13 Mar 2018 */
7981
7982 #define HSAVE_DESTROY \
7983 do{ if( Hsave_num > 0 ){ \
7984 if( Hsave_iwarp != NULL ){ \
7985 int ii ; \
7986 for( ii=0 ; ii < Hsave_num ; ii++ ) IW3D_destroy(Hsave_iwarp[ii]) ; \
7987 free(Hsave_iwarp) ; Hsave_iwarp = NULL ; \
7988 } \
7989 Hsave_num = 0 ; \
7990 } } while(0)
7991
7992 #define HSAVE_ADDTO(iww,inn) \
7993 do{ if( Hsave_callback_func != NULL ){ \
7994 Hsave_callback_func(iww,inn) ; \
7995 } else { \
7996 Hsave_iwarp = (IndexWarp3D **)realloc(Hsave_iwarp,sizeof(IndexWarp3D *)*(Hsave_num+1)) ; \
7997 Hsave_iname = (char **)realloc(Hsave_iname,sizeof(char *)*(Hsave_num+1)) ; \
7998 Hsave_iwarp[Hsave_num] = IW3D_copy(iww,1.0f) ; \
7999 Hsave_iname[Hsave_num] = strdup(inn) ; Hsave_num++ ; \
8000 } \
8001 } while(0)
8002
8003 static int Hfirsttime = 0 ; /* for fun only (to print stuff out in cost func) */
8004 static float Hfirstcost = 666.0f;
8005
8006 static int Hsuperhard1 = 0 ; /* Oh come on, who want to work super hard? */
8007 static int Hsuperhard2 = -1 ; /* Certainly not us government employees!!! */
8008
8009 static float Hstopcost = -666666.6f ; /* stop if 'correlation' cost goes below this */
8010 static int Hstopped = 0 ; /* indicate that iterations were stopped */
8011
8012 #include <setjmp.h>
8013 static volatile int Hquitting = 0 ; /* set by signal handler to indicate 'quit NOW' */
8014 static volatile int Hquitting_do_jump = 0 ; /* 21 Apr 2021 */
8015 static volatile int Hquitting_sig = 0 ;
8016 static jmp_buf Hquitting_jmp_buf ;
8017
8018 static int Hqfinal = 0 ; /* do quintic at the final level? */
8019 static int Hqonly = 0 ; /* do quintic at all levels? (very slow) */
8020 static int Hqhard = 0 ; /* do quintic in second pass of 'workhard'? */
8021
8022 #define Huse_sincc 0 /* disable sincc mode except at lev=0 */
8023 #ifndef Huse_sincc
8024 static int Huse_sincc = 0 ; /* use sincc mode after lev=0 */
8025 #endif
8026 #define MIN_SINCC 59319 /* smallest patch size for sincc mode [3D voxels] */
8027
8028 #undef WORKHARD /* work hard at level #lll? */
8029 #define WORKHARD(lll) ( (lll) >= Hworkhard1 && (lll) <= Hworkhard2 )
8030
8031 #undef SUPERHARD /* work superhard at level #lll? */
8032 #define SUPERHARD(lll) ( (lll) >= Hsuperhard1 && (lll) <= Hsuperhard2 )
8033
8034 static float Hcost = 666.666f ; /* current 'correlation' cost */
8035 static float Hpenn = 0.0f ;
8036 static float Hcostt = 0.0f ; /* 'pure' cost */
8037
8038 static float Hcostt_zero = 0.0f ; /* 10 Mar 2020 */
8039 static float save_H_zero = 0 ;
8040
8041 #undef SRCIM /* macro for which source image to use */
8042 #define SRCIM ( (Hsrcim_blur != NULL ) ? Hsrcim_blur : Hsrcim )
8043
8044 #undef BASIM /* macro for which base image to use */
8045 #define BASIM ( (Hbasim_blur != NULL ) ? Hbasim_blur : Hbasim )
8046
8047 /*---------- Code and variables for '-inedge' enhancement [Jul 2018] ---------*/
8048
8049 #undef ALLOW_INEDGE
8050
8051 #ifdef ALLOW_INEDGE
8052 # include "mri_intedge.c"
8053 static int Hinedge_erode = 3 ;
8054 static float Hinedge_frac = 0.333f ;
8055 static int Hinedge_doit = 0 ;
8056 #endif
8057
8058 /*----------------------------------------------------------------------------*/
8059 /* Process the QUIT signal, as in 'kill -s QUIT <processID>' */
8060 /* And now the ALRM signal -- for OpenMP freezes [21 Apr 2021] */
8061 /* This signal handler will longjmp() back into the "it failed" branch
8062 of the setjmp() conditional where the optimizer/OpenMP sometimes
8063 freezes up -- if Hquitting_do_jump is set. Otherwise, it just
8064 returns to where the program was when the signal was invoked.
8065 It also sets Hquitting on, which is the flag for the warping control
8066 function IW3D_warpomatic() to end work NOW NOW NOW (but gracefully). */
8067
8068 #include <signal.h>
IW3D_signal_quit(int sig)8069 void IW3D_signal_quit(int sig)
8070 {
8071 static volatile int fff=0 ;
8072 if( fff ) _exit(1) ; else fff = 1 ; /* stop recursion (shouldn't happen) */
8073 #ifdef USE_OMP
8074 (void)alarm(0) ; /* disable ALRM signal */
8075 #endif
8076 Hquitting = 1 ; Hquitting_sig = sig ; /* set external variables */
8077 fprintf(stderr,"\n** %s signal received -- trying to die gracefully **\n" ,
8078 (sig==SIGQUIT) ? "QUIT"
8079 :(sig==SIGALRM) ? "ALRM"
8080 :(sig==SIGSEGV) ? "SEGV"
8081 :(sig==SIGBUS ) ? "BUS"
8082 : "unknown" ) ;
8083 /* lonjmp() out if ordered to do so */
8084 if( Hquitting_do_jump ){ Hquitting_do_jump = 0 ; longjmp(Hquitting_jmp_buf,666) ; }
8085 /* otherwise, go back to whence we came */
8086 return ;
8087 }
8088
8089 /*---------------------------------------------*/
8090 /* Function to engage the signal handler above */
8091 /* [Called from 3dQwarp.c] */
8092 /*---------------------------------------------*/
8093
IW3D_setup_signal_quit(void)8094 void IW3D_setup_signal_quit(void){
8095 signal(SIGQUIT,IW3D_signal_quit); /* user sent QUIT signal */
8096 #ifdef USE_OMP
8097 signal(SIGALRM,IW3D_signal_quit); /* 21 Apr 2021 -- timeout */
8098 #endif
8099 return;
8100 }
8101
8102 /*----------------------------------------------------------------------------*/
8103 /* Make the displacement flags coherent and legal. If impossible, return -1. */
8104
IW3D_munge_flags(int nx,int ny,int nz,int flags)8105 int IW3D_munge_flags( int nx , int ny , int nz , int flags )
8106 {
8107 int iflags = flags ;
8108
8109 if( nx < 1 || ny < 1 || nz < 1 ) return -1 ; /* bad bad bad */
8110
8111 /* don't allow x-displacments if x size is too small,
8112 or if displacements aren't allowed to depend on x coordinate */
8113
8114 if( nx < NGMIN || (flags & NWARP_NOXDEP_FLAG) )
8115 flags |= (NWARP_NOXDIS_FLAG | NWARP_NOXDEP_FLAG) ;
8116
8117 /* same for y and z */
8118
8119 if( ny < NGMIN || (flags & NWARP_NOYDEP_FLAG) )
8120 flags |= (NWARP_NOYDIS_FLAG | NWARP_NOYDEP_FLAG) ;
8121
8122 if( nz < NGMIN || (flags & NWARP_NOZDEP_FLAG) )
8123 flags |= (NWARP_NOZDIS_FLAG | NWARP_NOZDEP_FLAG) ;
8124
8125 /* set flags to -1 (indicating error) if nothing is left */
8126
8127 if( (flags & NWARP_NODISP_FLAG) == NWARP_NODISP_FLAG ) flags = -1 ;
8128
8129 #if 0
8130 if( Hverb && iflags != flags )
8131 ININFO_message(" Flags:: input: x=%c y=%c z=%c output: x=%c y=%c z=%c",
8132 (iflags & NWARP_NOXDIS_FLAG) ? 'N' : 'Y' ,
8133 (iflags & NWARP_NOYDIS_FLAG) ? 'N' : 'Y' ,
8134 (iflags & NWARP_NOZDIS_FLAG) ? 'N' : 'Y' ,
8135 ( flags & NWARP_NOXDIS_FLAG) ? 'N' : 'Y' ,
8136 ( flags & NWARP_NOYDIS_FLAG) ? 'N' : 'Y' ,
8137 ( flags & NWARP_NOZDIS_FLAG) ? 'N' : 'Y' ) ;
8138 #endif
8139
8140 return flags ;
8141 }
8142
8143 #endif /*(Q1)*/ /*############################################################*/
8144
8145 #if 1
8146 /*============================================================================*/
8147 /** (Q2) Basis functions (cubic and quintic) for warping **/
8148 /*============================================================================*/
8149
8150 /*----------------------------------------------------------------------------*/
8151 /* C1 Hermite cubic basis functions over [-1..1] -- that is, the 2 functions
8152 and their first derivatives go to 0 at x=1 and x=-1.
8153 The first function (ee.a) is such that f(0)=1, and f'(0)=0.
8154 The second function (ee.b) is such that f(0)=0, and f'(0)=6.75.
8155 Scale factors are adjusted so that the functions' peak values are all 1.
8156 Return value is a float_pair comprising the 2 function values.
8157 This set of functions is the default for 3dQwarp patches.
8158 *//*--------------------------------------------------------------------------*/
8159
HCwarp_eval_basis(float x)8160 static INLINE float_pair HCwarp_eval_basis( float x )
8161 {
8162 register float aa , bb ; float_pair ee ;
8163
8164 aa = fabsf(x) ;
8165 if( aa >= 1.0f ){ /* outside range ==> 0 */
8166 ee.a = ee.b = 0.0f ;
8167 } else {
8168 bb = 1.0f - aa ; bb = bb*bb ;
8169 ee.a = bb * (1.0f+2.0f*aa) ; /* f(0) = 1 ; */
8170 ee.b = bb * x * 6.75f ; /* f'(0) = 1 * 6.75 */
8171 }
8172 return ee ;
8173 }
8174
8175 /*----------------------------------------------------------------------------*/
8176 /* Sinc compact (sincc) basis function [05 Nov 2019] --
8177 sinc() is defined far above as sinc(x) = sin(PI*x)/(PI*x) x >= 0
8178 This basis function 'set' has only one function, so in 3D there are just
8179 3 parameters to optimize; the purpose of this is to provide the very first
8180 alignment step at lev=0 to adjust alignment crudely and quickly.
8181 *//*--------------------------------------------------------------------------*/
8182
HSCwarp_eval_basis(float x)8183 static INLINE float HSCwarp_eval_basis( float x )
8184 {
8185 float aa = fabsf(x) ;
8186 if( aa >= 1.0f ) return 0.0f ;
8187 return ( 0.5f * ( 1.0f + sinc(aa) - sinc(1.0f-aa) ) ) ;
8188 }
8189
8190 #ifdef ALLOW_BASIS5
8191 /*----------------------------------------------------------------------------*/
8192 /* The following functions add 3 more functions to the C1 basis set,
8193 for 'p' type refinement of the warp -- HCwarp_eval_'basisX' provides
8194 X basis functions, for X=3, 4, 5. These additional functions are chosen
8195 to provide more oscillations in the basic [-1..1] interval.
8196 function #1 (ee.a) = no internal zero crossings
8197 function #2 (ee.b) = 1 internal zero crossing
8198 function #3 (ee.c) = 2 internal zero crossings
8199 function #4 (ee.d) = 3 internal zero crossings
8200 function #5 (ee.e) = 4 internal zero crossings
8201 Functions #3-5 integrate to 0 over the interval [0..1] (orthogonal to 1).
8202 All functions are scaled so that their peak value is 1.
8203 Function #5 is also orthogonal to x over [0..1]. [02 Nov 2015]
8204
8205 These functions don't seem to be worth the extra CPU time they require;
8206 they provide more detailed warping within a patch, but at the cost of
8207 optimizing over more parameters. It seems generally better simply to
8208 run down to smaller patches instead.
8209 *//*--------------------------------------------------------------------------*/
8210
8211 #if 0 /* basis3 and basis4 funcs aren't needed, since basis5 func exists */
8212 static INLINE float_triple HCwarp_eval_basis3( float x )
8213 {
8214 register float aa , bb ; float_triple ee ;
8215
8216 aa = fabsf(x) ;
8217 if( aa >= 1.0f ){ /* outside range ==> 0 */
8218 ee.a = ee.b = ee.c = 0.0f ;
8219 } else {
8220 bb = 1.0f - aa ; bb = bb*bb ;
8221 ee.a = bb * (1.0f+2.0f*aa) ; /* f(0) = 1 ; */
8222 ee.b = bb * x * 6.75f ; /* f'(0) = 1 * 6.75 f(1/3) = 1 */
8223 ee.c = bb * (1.0f+2.0f*aa-15.0f*aa*aa) ;
8224 }
8225 return ee ;
8226 }
8227
8228 /*............................................................*/
8229
8230 static INLINE float_quad HCwarp_eval_basis4( float x )
8231 {
8232 register float aa , bb ; float_quad ee ;
8233
8234 aa = fabsf(x) ;
8235 if( aa >= 1.0f ){ /* outside range ==> 0 */
8236 ee.a = ee.b = ee.c = ee.d = 0.0f ;
8237 } else {
8238 bb = 1.0f - aa ; bb = bb*bb ;
8239 ee.a = bb * (1.0f+2.0f*aa) ; /* f(0) = 1 ; */
8240 ee.b = bb * x * 6.75f ; /* f'(0) = 1 * 6.75 */
8241 ee.c = bb * (1.0f+2.0f*aa-aa*aa*15.0f) ;
8242 ee.d = bb * x * (1.0f-aa*aa*5.0f) * 9.75f ;
8243 }
8244 return ee ;
8245 }
8246 #endif /* end of skipping _basis3 and _basis4 funcs */
8247
8248 /*............................................................*/
8249 /* This func does the MRI_CUBIC_PLUS_3 (AKA cubc105) basis
8250 functions, which also include the PLUS_1 and PLUS_2 funcs
8251 *//*..........................................................*/
8252
HCwarp_eval_basis5(float x)8253 static INLINE float_quint HCwarp_eval_basis5( float x )
8254 {
8255 register float aa , bb ; float_quint ee ;
8256
8257 aa = fabsf(x) ;
8258 if( aa >= 1.0f ){ /* outside range ==> 0 */
8259 ee.a = ee.b = ee.c = ee.d = ee.e = 0.0f ;
8260 } else {
8261 bb = 1.0f - aa ; bb = bb*bb ;
8262 ee.a = bb * (1.0f+2.0f*aa) ; /* f(0) = 1 ; */
8263 ee.b = bb * x * 6.75f ; /* f'(0) = 1 * 6.75 */
8264 ee.c = bb * (1.0f+2.0f*aa-aa*aa*15.0f) ;
8265 ee.d = bb * x * (1.0f-aa*aa*5.0f) * 9.75f ;
8266 ee.e = bb * (1.0f+2.0f*aa-aa*aa*57.0f+aa*aa*aa*84.0f) ;
8267 }
8268 return ee ;
8269 }
8270
8271 /***************************************************************************
8272 ## brute force csh script to plot the above 5 functions:
8273 \rm e?.1D
8274 foreach ii ( `count -dig 1 0 200` )
8275 set xx = `ccalc "-1+0.01*$ii"`
8276 set aa = `ccalc "abs($xx)"`
8277 set bb = `ccalc "(1.0-$aa)^2"`
8278 set ea = `ccalc "$bb * (1.0+2.0*$aa)"`
8279 set eb = `ccalc "$bb * $xx * 6.75"`
8280 set ec = `ccalc "$bb * (1.0+2.0*$aa-$aa*$aa*15.0)"`
8281 set ed = `ccalc "$bb * $xx * (1.0-$aa*$aa*5.0) * 9.75"`
8282 set ee = `ccalc "$bb * (1.0+2.0*$aa-$aa*$aa*57.0+$aa*$aa*$aa*84.0)"`
8283 echo $ea >> ea.1D
8284 echo $eb >> eb.1D
8285 echo $ec >> ec.1D
8286 echo $ed >> ed.1D
8287 echo $ee >> ee.1D
8288 end
8289 1dplot -del 0.01 -xzero -1 -sepscl e?.1D
8290 ****************************************************************************/
8291 #endif /* ALLOW_BASIS5 */
8292
8293 /*----------------------------------------------------------------------------*/
8294 /* C2 Hermite quintic basis functions over [-1..1] -- that is, the 3 functions
8295 and their first 2 derivatives go to 0 at x=1 and x=-1.
8296 The first function (ee.a) is such that f(0) != 0, and f'(0)=f''(0)=0.
8297 The second function (ee.b) is such that f(0)=f''(0)=0, and f'(0) != 0.
8298 The third function (ee.c) is such fhat f(0)=f'(0)=0, and f''(0) != 0.
8299 Scale factors are adjusted so that the functions' peak values are all 1.
8300 Return value is a float_triple comprising the 3 function values.
8301
8302 These functions are used at lev=0, and if Qonly or Qfinal or WORKHARD
8303 options are enabled in 3dQwarp. They provide more detail within a patch
8304 warp than the cubic polynomials, but at the cost of CPU time.
8305 *//*--------------------------------------------------------------------------*/
8306
HQwarp_eval_basis(float x)8307 static INLINE float_triple HQwarp_eval_basis( float x )
8308 {
8309 register float aa , bb , aq ; float_triple ee ;
8310
8311 aa = fabsf(x) ;
8312 if( aa >= 1.0f ){ /* outside range ==> 0 */
8313 ee.a = ee.b = ee.c = 0.0f ;
8314 } else {
8315 bb = 1.0f - aa ; bb = bb*bb*bb ; aq = aa*aa ;
8316 ee.a = bb * ( (6.0f*aq+3.0f)*aa + 1.0f ) ; /* f(0) = 1 */
8317 ee.b = bb * x * (3.0f*aa+1.0f) * 5.0625f ; /* f'(0) = 1 * 5.0625 */
8318 ee.c = aq * bb * 28.935f ; /* f''(0) = 1 * 28.935 */
8319 }
8320 return ee ;
8321 }
8322
8323 /*----------------------------------------------------------------------------*/
8324 /* Macro to compute grid location coefficients inside interval x in [-1,1].
8325 * The leftmost index (that maps to x=-1) is ILEFT.
8326 * The rightmost index (that maps to x=+1) is IRGHT(n), where 'n' is the
8327 number of grid points, indexed from i=0 to i=n-1.
8328 * Outputs of COMPUTE_CAB() are ca and cb such that x = ca +cb*i.
8329 * This function is so we can use the cubic and quintic eval_basis()
8330 funcs above, which are defined over the domain [-1,1], when setting
8331 up the warp basis arrays over a range of integer indexes.
8332 *//*--------------------------------------------------------------------------*/
8333
8334 /* Rational possibilities here are:
8335 ILEFT = 0 IRGHT = n-1 :: 0 at last grid point
8336 ILEFT = -1/2 IRGHT = n-1/2 :: 0 halfway outside last grid point
8337 ILEFT = -1 IRGHT = n :: 0 at next grid point outside patch
8338 Note that all the basis functions are 0 when evaluated at x=-1 or +1. */
8339
8340 #define ILEFT (-0.5f) /* and these are the choices made by Zhark */
8341 #define IRGHT(n) ((n)-0.5f) /* are they "best"? only The Shadow knows! */
8342
8343 #define COMPUTE_CAB(n) \
8344 do{ cb = 2.0f / (IRGHT(n)-ILEFT) ; ca = -1.0f - cb*ILEFT ; } while(0)
8345
8346 /*----------------------------------------------------------------------------*/
8347 /* Setup cubic basis arrays for each dimension for patch = nx X ny X nz */
8348
HCwarp_setup_basis(int nx,int ny,int nz,int flags)8349 void HCwarp_setup_basis( int nx , int ny , int nz , int flags )
8350 {
8351 float_pair ee ; int ii ; float ca,cb,ccc ;
8352
8353 ENTRY("HCwarp_setup_basis") ;
8354
8355 /* if not going to use all 3D displacements,
8356 create map from active set of parameters to total set of parameters:
8357 Hparmap[j] = index into list 0..23 of the 'true' parameter location;
8358 We have to do this since the Powell optimization function requires
8359 input of all parameters in a contiguous array, but we want to keep
8360 them in their logical place even if some of them aren't being used,
8361 so this mapping will let us translate between these arrays (if needed) */
8362
8363 Hflags = IW3D_munge_flags(nx,ny,nz,flags) ;
8364 FREEIFNN(Hparmap) ;
8365
8366 if( (Hflags & NWARP_NODISP_FLAG) != 0 ){ /* not all params being used */
8367 int pm = 0 ;
8368 Hparmap = (int *)calloc(sizeof(int),24) ; /* 24 = max num params */
8369
8370 if( Hbasis_code == MRI_CUBIC ){ /* 8 params per dimension */
8371 if( !(Hflags & NWARP_NOXDIS_FLAG) ){
8372 for( ii=0 ; ii < 8 ; ii++ ) Hparmap[pm++] = ii ; /* x params */
8373 }
8374 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8375 for( ii=0 ; ii < 8 ; ii++ ) Hparmap[pm++] = ii+8 ; /* y params */
8376 }
8377 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8378 for( ii=0 ; ii < 8 ; ii++ ) Hparmap[pm++] = ii+16 ; /* z params */
8379 }
8380 Hnparmap = pm ;
8381 if( Hnparmap == 24 ){ free(Hparmap) ; Hparmap = NULL ; }
8382
8383 } else { /* MRI_CUBIC_LITE = 4 params per dimension */
8384
8385 if( !(Hflags & NWARP_NOXDIS_FLAG) ){
8386 for( ii=0 ; ii < 4 ; ii++ ) Hparmap[pm++] = ii ; /* x params */
8387 }
8388 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8389 for( ii=0 ; ii < 4 ; ii++ ) Hparmap[pm++] = ii+4 ; /* y params */
8390 }
8391 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8392 for( ii=0 ; ii < 4 ; ii++ ) Hparmap[pm++] = ii+8 ; /* z params */
8393 }
8394 Hnparmap = pm ;
8395 if( Hnparmap == 12 ){ free(Hparmap) ; Hparmap = NULL ; }
8396 }
8397
8398 } else {
8399
8400 Hnparmap = (Hbasis_code == MRI_CUBIC) ? 24 : 12 ;
8401 Hparmap = NULL ; /* no index translation needed */
8402
8403 }
8404
8405 /* if everything is already cool from a previous call,
8406 just zero out the warp patches and vamoose the ranch */
8407
8408 if( nx == nbcx && ny == nbcy && nz == nbcz &&
8409 Hwarp != NULL && AHwarp != NULL &&
8410 nx == Hwarp->nx && ny == Hwarp->ny && nz == Hwarp->nz ){
8411 IW3D_zero_fill(Hwarp) ; IW3D_zero_fill(AHwarp) ; EXRETURN ;
8412 }
8413
8414 /* otherwise cleanup old stuff (recall that the 'c' arrays are for cubics) */
8415
8416 if( Hwarp != NULL ){ IW3D_destroy( Hwarp); Hwarp = NULL; }
8417 if( AHwarp != NULL ){ IW3D_destroy(AHwarp); AHwarp = NULL; }
8418
8419 FREEIFNN(bc0x); FREEIFNN(bc1x); nbcx=0;
8420 FREEIFNN(bc0y); FREEIFNN(bc1y); nbcy=0;
8421 FREEIFNN(bc0z); FREEIFNN(bc1z); nbcz=0;
8422 FREEIFNN(bc2x); FREEIFNN(bc3x); FREEIFNN(bc4x);
8423 FREEIFNN(bc2y); FREEIFNN(bc3y); FREEIFNN(bc4y);
8424 FREEIFNN(bc2z); FREEIFNN(bc3z); FREEIFNN(bc4z);
8425
8426 if( bbbcar != NULL ){
8427 for( ii=0 ; ii < nbbbcar ; ii++ ) FREEIFNN(bbbcar[ii]) ;
8428 free(bbbcar) ; nbbcxyz = nbbbcar = 0 ; bbbcar = NULL ;
8429 }
8430
8431 if( Hflags < 0 ) EXRETURN ; /* this should not happen */
8432
8433 /* create new arrays for storage of 1D basis functions */
8434 /* (they will be turned into 3D basis functions at a later date) */
8435
8436 nbcx = nx ;
8437 bc0x = (float *)malloc(sizeof(float)*nbcx) ; /* 1D basis arrays */
8438 bc1x = (float *)malloc(sizeof(float)*nbcx) ;
8439 nbcy = ny ;
8440 bc0y = (float *)malloc(sizeof(float)*nbcy) ;
8441 bc1y = (float *)malloc(sizeof(float)*nbcy) ;
8442 nbcz = nz ;
8443 bc0z = (float *)malloc(sizeof(float)*nbcz) ;
8444 bc1z = (float *)malloc(sizeof(float)*nbcz) ;
8445
8446 nbcxy = nbcx*nbcy ; /* for indexing */
8447
8448 /* fill arrays for x direction */
8449
8450 if( Hflags & NWARP_NOXDEP_FLAG ){
8451 dxci = 0.0f ;
8452 for( ii=0 ; ii < nbcx ; ii++ ){
8453 bc0x[ii] = 1.0f ; bc1x[ii] = 0.0f ; /* no x parameters */
8454 }
8455 } else {
8456 COMPUTE_CAB(nbcx) ; dxci = 1.0f/cb ; /* dxci = half-width of patch */
8457 for( ii=0 ; ii < nbcx ; ii++ ){
8458 ccc = ca + ii*cb ; ee = HCwarp_eval_basis(ccc) ;
8459 bc0x[ii] = ee.a ; bc1x[ii] = ee.b ;
8460 }
8461 }
8462
8463 /* fill arrays for y direction */
8464
8465 if( Hflags & NWARP_NOYDEP_FLAG ){
8466 dyci = 0.0f ;
8467 for( ii=0 ; ii < nbcy ; ii++ ){
8468 bc0y[ii] = 1.0f ; bc1y[ii] = 0.0f ; /* no y parameters */
8469 }
8470 } else {
8471 COMPUTE_CAB(nbcy) ; dyci = 1.0f/cb ;
8472 for( ii=0 ; ii < nbcy ; ii++ ){
8473 ccc = ca + ii*cb ; ee = HCwarp_eval_basis(ccc) ;
8474 bc0y[ii] = ee.a ; bc1y[ii] = ee.b ;
8475 }
8476 }
8477
8478 /* fill arrays for z direction */
8479
8480 if( Hflags & NWARP_NOZDEP_FLAG ){
8481 dzci = 0.0f ;
8482 for( ii=0 ; ii < nbcz ; ii++ ){
8483 bc0z[ii] = 1.0f ; bc1z[ii] = 0.0f ; /* no z parameters */
8484 }
8485 } else {
8486 COMPUTE_CAB(nbcz) ; dzci = 1.0f/cb ;
8487 for( ii=0 ; ii < nbcz ; ii++ ){
8488 ccc = ca + ii*cb ; ee = HCwarp_eval_basis(ccc) ;
8489 bc0z[ii] = ee.a ; bc1z[ii] = ee.b ;
8490 }
8491 }
8492
8493 /* build 3D versions for small enough warp fields (will be faster) */
8494
8495 /** 4 bytes per float, times 8 arrays **/
8496
8497 nbbcxyz = nbcx * nbcy * nbcz ; /* array size of 3D warp patch */
8498 if( sizeof(float)*8.0f*nbbcxyz/GIGA <= HmaxmemG ){ /* max size of 3D patch storage */
8499 int jj , kk , qq ;
8500 bbbcar = (float **)malloc(sizeof(float *)*8) ; nbbbcar = 8 ;
8501 for( ii=0 ; ii < 8 ; ii++ )
8502 bbbcar[ii] = (float *)malloc(sizeof(float)*nbbcxyz) ; /* allocate patch */
8503 for( qq=kk=0 ; kk < nbcz ; kk++ ){
8504 for( jj=0 ; jj < nbcy ; jj++ ){
8505 #pragma ivdep /* for Intel icc compiler */
8506 for( ii=0 ; ii < nbcx ; ii++,qq++ ){
8507 bbbcar[0][qq] = bc0z[kk]*bc0y[jj]*bc0x[ii] ; /* this saves */
8508 bbbcar[1][qq] = bc1z[kk]*bc0y[jj]*bc0x[ii] ; /* having to */
8509 bbbcar[2][qq] = bc0z[kk]*bc1y[jj]*bc0x[ii] ; /* do all these */
8510 bbbcar[3][qq] = bc1z[kk]*bc1y[jj]*bc0x[ii] ; /* multiplies */
8511 bbbcar[4][qq] = bc0z[kk]*bc0y[jj]*bc1x[ii] ; /* over and over */
8512 bbbcar[5][qq] = bc1z[kk]*bc0y[jj]*bc1x[ii] ; /* when iterating */
8513 bbbcar[6][qq] = bc0z[kk]*bc1y[jj]*bc1x[ii] ;
8514 bbbcar[7][qq] = bc1z[kk]*bc1y[jj]*bc1x[ii] ;
8515 }}}
8516 }
8517
8518 /* create empty patch warp, to be populated in HCwarp_load,
8519 given these basis function arrays and the warp parameters */
8520
8521 Hwarp = IW3D_create(nbcx,nbcy,nbcz) ; /* incremental patch warp */
8522 AHwarp = IW3D_create(nbcx,nbcy,nbcz) ; /* global warp(patch warp) in patch */
8523
8524 EXRETURN ;
8525 }
8526
8527 #ifdef ALLOW_BASIS5
8528 /*----------------------------------------------------------------------------*/
8529 /* Setup cubic basis arrays for each dimension for patch = nx X ny X nz,
8530 but with some extra basis functions (1 or 2 or 3) given by nplus.
8531 This function ALWAYS sets up 3D basis arrays for speed, so only should
8532 be used on small-ish patches -- say 25x25x25 or smaller. [05 Nov 2015]
8533 *//*--------------------------------------------------------------------------*/
8534
HCwarp_setup_basis345(int nx,int ny,int nz,int flags,int nplus)8535 int HCwarp_setup_basis345( int nx , int ny , int nz , int flags , int nplus )
8536 {
8537 float_quint ee ; int ii,jj,kk,pp,qq,rr,ss,hh,nb5,nparm ; float ca,cb,ccc ;
8538
8539 ENTRY("HCwarp_setup_basis345") ;
8540
8541 if( nplus <= 0 ){
8542 if( bbbcar != NULL ){
8543 for( ii=0 ; ii < nbbbcar ; ii++ ) FREEIFNN(bbbcar[ii]) ;
8544 free(bbbcar) ; nbbcxyz = nbbbcar = 0 ; bbbcar = NULL ;
8545 }
8546 RETURN(0) ;
8547 }
8548
8549 if( nplus > 3 )
8550 ERROR_exit("nplus=%d in call to HCwarp_setup_basis345 :-(",nplus) ;
8551
8552 nb5 = 2+nplus ; /* nb5 = 3 or 4 or 5 */
8553
8554 /* count number of parameters (for each spatial dimension) */
8555 for( ss=rr=0 ; rr < nb5 ; rr++ ){ /* 3 loops over basis func order */
8556 for( qq=0 ; qq < nb5 ; qq++ ){
8557 for( pp=0 ; pp < nb5 ; pp++ ){
8558 if( rr+qq+pp < nb5 ) ss++ ; /* this is 'lite' stuff */
8559 }}}
8560 H5nparm = nparm = ss ;
8561 /*** INFO_message("cubic+%d nparm=%d",nplus,nparm) ; ***/
8562
8563 /* if not going to use all 3D displacements,
8564 create map from active set of parameters to total set of parameters:
8565 Hparmap[j] = index into list 0..3*nb5*nb5*nb5-1 of the 'true'
8566 parameter location;
8567 We have to do this since the Powell optimization function requires
8568 input of all parameters in a contiguous array, but we want to keep
8569 them in their logical place even if some of them aren't being used,
8570 so this mapping will let us translate between these arrays (if needed) */
8571
8572 Hflags = IW3D_munge_flags(nx,ny,nz,flags) ;
8573 FREEIFNN(Hparmap) ;
8574
8575 if( (Hflags & NWARP_NODISP_FLAG) != 0 ){ /* not all params being used */
8576 int pm = 0 ;
8577 Hparmap = (int *)calloc(sizeof(int),3*nparm) ;
8578 if( !(Hflags & NWARP_NOXDIS_FLAG) ){
8579 for( ii=0 ; ii < nparm ; ii++ ) Hparmap[pm++] = ii ; /* x params */
8580 }
8581 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8582 for( ii=0 ; ii < nparm ; ii++ ) Hparmap[pm++] = ii+nparm ; /* y params */
8583 }
8584 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8585 for( ii=0 ; ii < nparm ; ii++ ) Hparmap[pm++] = ii+2*nparm ; /* z params */
8586 }
8587 Hnparmap = pm ;
8588 if( Hnparmap == 3*nparm ){ free(Hparmap) ; Hparmap = NULL ; }
8589 } else {
8590 Hnparmap = 3*nparm ; Hparmap = NULL ; /* no translation needed */
8591 }
8592
8593 /* if everything is already cool, just zero out warp patches and exit */
8594
8595 if( nx == nbcx && ny == nbcy && nz == nbcz &&
8596 Hwarp != NULL && AHwarp != NULL && nparm == nbbbcar &&
8597 nx == Hwarp->nx && ny == Hwarp->ny && nz == Hwarp->nz ){
8598 STATUS("everything is cool") ;
8599 IW3D_zero_fill(Hwarp) ; IW3D_zero_fill(AHwarp) ; RETURN(nparm) ;
8600 }
8601
8602 /* cleanup old stuff (recall that the 'c' arrays are for cubics) */
8603
8604 STATUS("cleanup old stuff") ;
8605 if( Hwarp != NULL ){ IW3D_destroy( Hwarp); Hwarp = NULL; }
8606 if( AHwarp != NULL ){ IW3D_destroy(AHwarp); AHwarp = NULL; }
8607
8608 FREEIFNN(bc0x); FREEIFNN(bc1x); nbcx=0;
8609 FREEIFNN(bc0y); FREEIFNN(bc1y); nbcy=0;
8610 FREEIFNN(bc0z); FREEIFNN(bc1z); nbcz=0;
8611 FREEIFNN(bc2x); FREEIFNN(bc3x); FREEIFNN(bc4x);
8612 FREEIFNN(bc2y); FREEIFNN(bc3y); FREEIFNN(bc4y);
8613 FREEIFNN(bc2z); FREEIFNN(bc3z); FREEIFNN(bc4z);
8614
8615 if( bbbcar != NULL ){
8616 for( ii=0 ; ii < nbbbcar ; ii++ ) FREEIFNN(bbbcar[ii]) ;
8617 free(bbbcar) ; nbbcxyz = nbbbcar = 0 ; bbbcar = NULL ;
8618 }
8619
8620 if( Hflags < 0 ) RETURN(0) ; /* this should not happen */
8621
8622 /* create new stuff */
8623
8624 nbcx = nx ;
8625 bc0x = (float *)malloc(sizeof(float)*nbcx) ; /* 1D basis arrays */
8626 bc1x = (float *)malloc(sizeof(float)*nbcx) ;
8627 bc2x = (float *)malloc(sizeof(float)*nbcx) ;
8628 bc3x = (float *)malloc(sizeof(float)*nbcx) ;
8629 bc4x = (float *)malloc(sizeof(float)*nbcx) ;
8630 nbcy = ny ;
8631 bc0y = (float *)malloc(sizeof(float)*nbcy) ;
8632 bc1y = (float *)malloc(sizeof(float)*nbcy) ;
8633 bc2y = (float *)malloc(sizeof(float)*nbcy) ;
8634 bc3y = (float *)malloc(sizeof(float)*nbcy) ;
8635 bc4y = (float *)malloc(sizeof(float)*nbcy) ;
8636 nbcz = nz ;
8637 bc0z = (float *)malloc(sizeof(float)*nbcz) ;
8638 bc1z = (float *)malloc(sizeof(float)*nbcz) ;
8639 bc2z = (float *)malloc(sizeof(float)*nbcz) ;
8640 bc3z = (float *)malloc(sizeof(float)*nbcz) ;
8641 bc4z = (float *)malloc(sizeof(float)*nbcz) ;
8642
8643 nbcxy = nbcx*nbcy ; /* for indexing */
8644
8645 /* fill arrays for x direction basis functions */
8646
8647 STATUS("fill arrays") ;
8648 if( Hflags & NWARP_NOXDEP_FLAG ){
8649 dxci = 0.0f ;
8650 for( ii=0 ; ii < nbcx ; ii++ ){
8651 bc0x[ii] = 1.0f ; bc1x[ii] = bc2x[ii] = bc3x[ii] = bc4x[ii] = 0.0f ;
8652 }
8653 } else {
8654 COMPUTE_CAB(nbcx) ; dxci = 1.0f/cb ; /* dxci = half-width of patch */
8655 /* INFO_message("basis5 for nbcx=%d",nbcx) ; */
8656 for( ii=0 ; ii < nbcx ; ii++ ){
8657 ccc = ca + ii*cb ; ee = HCwarp_eval_basis5(ccc) ;
8658 bc0x[ii] = ee.a ; bc1x[ii] = ee.b ;
8659 bc2x[ii] = ee.c ; bc3x[ii] = ee.d ; bc4x[ii] = ee.e ;
8660 /* ININFO_message(" x=%7.4f %8.4f %8.4f %8.4f %8.4f %8.4f",ccc,ee.a,ee.b,ee.c,ee.d,ee.e) ; */
8661 }
8662 }
8663
8664 /* fill arrays for y direction basis functions */
8665
8666 if( Hflags & NWARP_NOYDEP_FLAG ){
8667 dyci = 0.0f ;
8668 for( ii=0 ; ii < nbcy ; ii++ ){
8669 bc0y[ii] = 1.0f ; bc1y[ii] = bc2y[ii] = bc3y[ii] = bc4y[ii] = 0.0f ;
8670 }
8671 } else {
8672 COMPUTE_CAB(nbcy) ; dyci = 1.0f/cb ;
8673 /* INFO_message("basis5 for nbcy=%d",nbcy) ; */
8674 for( ii=0 ; ii < nbcy ; ii++ ){
8675 ccc = ca + ii*cb ; ee = HCwarp_eval_basis5(ccc) ;
8676 bc0y[ii] = ee.a ; bc1y[ii] = ee.b ;
8677 bc2y[ii] = ee.c ; bc3y[ii] = ee.d ; bc4y[ii] = ee.e ;
8678 /* ININFO_message(" y=%7.4f %8.4f %8.4f %8.4f %8.4f %8.4f",ccc,ee.a,ee.b,ee.c,ee.d,ee.e) ; */
8679 }
8680 }
8681
8682 /* fill arrays for z direction basis functions */
8683
8684 if( Hflags & NWARP_NOZDEP_FLAG ){
8685 dzci = 0.0f ;
8686 for( ii=0 ; ii < nbcz ; ii++ ){
8687 bc0z[ii] = 1.0f ; bc1z[ii] = bc2z[ii] = bc3z[ii] = bc4z[ii] = 0.0f ;
8688 }
8689 } else {
8690 COMPUTE_CAB(nbcz) ; dzci = 1.0f/cb ;
8691 /* INFO_message("basis5 for nbcz=%d",nbcz) ; */
8692 for( ii=0 ; ii < nbcz ; ii++ ){
8693 ccc = ca + ii*cb ; ee = HCwarp_eval_basis5(ccc) ;
8694 bc0z[ii] = ee.a ; bc1z[ii] = ee.b ;
8695 bc2z[ii] = ee.c ; bc3z[ii] = ee.d ; bc4z[ii] = ee.e ;
8696 /* ININFO_message(" z=%7.4f %8.4f %8.4f %8.4f %8.4f %8.4f",ccc,ee.a,ee.b,ee.c,ee.d,ee.e) ; */
8697 }
8698 }
8699
8700 bcxx[0] = bc0x; bcxx[1] = bc1x; bcxx[2] = bc2x; bcxx[3] = bc3x; bcxx[4] = bc4x;
8701 bcyy[0] = bc0y; bcyy[1] = bc1y; bcyy[2] = bc2y; bcyy[3] = bc3y; bcyy[4] = bc4y;
8702 bczz[0] = bc0z; bczz[1] = bc1z; bczz[2] = bc2z; bczz[3] = bc3z; bczz[4] = bc4z;
8703
8704 /* always use 3D arrays */
8705
8706 nbbcxyz = nbcx * nbcy * nbcz ; /* size of 3D patch */
8707 nbbbcar = nparm ;
8708 /* INFO_message("allocate %d 3D arrays of size %d voxels",nbbbcar,nbbcxyz) ; */
8709 bbbcar = (float **)malloc(sizeof(float *)*nbbbcar) ;
8710 for( ii=0 ; ii < nbbbcar ; ii++ )
8711 bbbcar[ii] = (float *)malloc(sizeof(float)*nbbcxyz) ;
8712
8713 STATUS("fill bbbcar") ;
8714 for( hh=kk=0 ; kk < nbcz ; kk++ ){ /* 3 loops over z,y,x */
8715 for( jj=0 ; jj < nbcy ; jj++ ){
8716 for( ii=0 ; ii < nbcx ; ii++,hh++ ){
8717 for( ss=rr=0 ; rr < nb5 ; rr++ ){ /* 3 loops over basis func order */
8718 for( qq=0 ; qq < nb5 ; qq++ ){
8719 for( pp=0 ; pp < nb5 ; pp++ ){
8720 if( rr+qq+pp < nb5 ){
8721 bbbcar[ss][hh] = bczz[pp][kk] * bcyy[qq][jj] * bcxx[rr][ii] ;
8722 ss++ ;
8723 }
8724 }}}
8725 }}}
8726
8727 /* create empty patch warp, to be populated in HCwarp_load,
8728 given these basis function arrays and the warp parameters */
8729
8730 STATUS("create empty warps") ;
8731 Hwarp = IW3D_create(nbcx,nbcy,nbcz) ; /* incremental patch warp */
8732 AHwarp = IW3D_create(nbcx,nbcy,nbcz) ; /* global warp(patch warp) in patch */
8733
8734 RETURN(nparm) ;
8735 }
8736 #endif /* ALLOW_BASIS5 */
8737
8738 /*----------------------------------------------------------------------------*/
8739 /*! Setup quintic basis arrays: like HCwarp_setup_basis(), but bigger */
8740
HQwarp_setup_basis(int nx,int ny,int nz,int flags)8741 void HQwarp_setup_basis( int nx , int ny , int nz , int flags )
8742 {
8743 float_triple ee ; int ii ; float ca,cb,ccc ;
8744
8745 ENTRY("HQwarp_setup_basis") ;
8746
8747 Hflags = IW3D_munge_flags(nx,ny,nz,flags) ;
8748 FREEIFNN(Hparmap) ;
8749
8750 if( (Hflags & NWARP_NODISP_FLAG) != 0 ){
8751 int pm = 0 ;
8752 Hparmap = (int *)calloc(sizeof(int),81) ;
8753 if( Hbasis_code == MRI_QUINTIC ){ /* 27 params per dimension */
8754 if( !(Hflags & NWARP_NOXDIS_FLAG) ){
8755 for( ii=0 ; ii < 27 ; ii++ ) Hparmap[pm++] = ii ;
8756 }
8757 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8758 for( ii=0 ; ii < 27 ; ii++ ) Hparmap[pm++] = ii+27 ;
8759 }
8760 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8761 for( ii=0 ; ii < 27 ; ii++ ) Hparmap[pm++] = ii+54 ;
8762 }
8763 Hnparmap = pm ;
8764 if( Hnparmap == 81 ){ free(Hparmap) ; Hparmap = NULL ; }
8765 } else { /* MRI_QUINTIC_LITE = 10 parameters per dimension */
8766 if( !(Hflags & NWARP_NOXDIS_FLAG) ){
8767 for( ii=0 ; ii < 10 ; ii++ ) Hparmap[pm++] = ii ;
8768 }
8769 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8770 for( ii=0 ; ii < 10 ; ii++ ) Hparmap[pm++] = ii+10 ;
8771 }
8772 if( !(Hflags & NWARP_NOYDIS_FLAG) ){
8773 for( ii=0 ; ii < 10 ; ii++ ) Hparmap[pm++] = ii+20 ;
8774 }
8775 Hnparmap = pm ;
8776 if( Hnparmap == 30 ){ free(Hparmap) ; Hparmap = NULL ; }
8777 }
8778 } else {
8779 Hparmap = NULL ;
8780 Hnparmap = (Hbasis_code == MRI_QUINTIC) ? 81 : 30 ;
8781 }
8782
8783 if( nx == nbqx && ny == nbqy && nz == nbqz &&
8784 Hwarp != NULL && AHwarp != NULL &&
8785 nx == Hwarp->nx && ny == Hwarp->ny && nz == Hwarp->nz ){
8786
8787 IW3D_zero_fill(Hwarp) ; IW3D_zero_fill(AHwarp) ; EXRETURN ;
8788 }
8789
8790 if( Hwarp != NULL ){ IW3D_destroy( Hwarp); Hwarp = NULL; }
8791 if( AHwarp != NULL ){ IW3D_destroy(AHwarp); AHwarp = NULL; }
8792
8793 FREEIFNN(bq0x); FREEIFNN(bq1x); FREEIFNN(bq2x); nbqx=0;
8794 FREEIFNN(bq0y); FREEIFNN(bq1y); FREEIFNN(bq2y); nbqy=0;
8795 FREEIFNN(bq0z); FREEIFNN(bq1z); FREEIFNN(bq2z); nbqz=0;
8796
8797 if( bbbqar != NULL ){
8798 for( ii=0 ; ii < 27 ; ii++ ) FREEIFNN(bbbqar[ii]) ;
8799 free(bbbqar) ; nbbqxyz = 0 ; bbbqar = NULL ;
8800 }
8801
8802 if( Hflags < 0 ) EXRETURN ;
8803
8804 nbqx = nx ;
8805 bq0x = (float *)malloc(sizeof(float)*nbqx) ;
8806 bq1x = (float *)malloc(sizeof(float)*nbqx) ;
8807 bq2x = (float *)malloc(sizeof(float)*nbqx) ;
8808 nbqy = ny ;
8809 bq0y = (float *)malloc(sizeof(float)*nbqy) ;
8810 bq1y = (float *)malloc(sizeof(float)*nbqy) ;
8811 bq2y = (float *)malloc(sizeof(float)*nbqy) ;
8812 nbqz = nz ;
8813 bq0z = (float *)malloc(sizeof(float)*nbqz) ;
8814 bq1z = (float *)malloc(sizeof(float)*nbqz) ;
8815 bq2z = (float *)malloc(sizeof(float)*nbqz) ;
8816
8817 nbqxy = nbqx*nbqy ;
8818
8819 if( Hflags & NWARP_NOXDEP_FLAG ){
8820 dxqi = 0.0f ;
8821 for( ii=0 ; ii < nbqx ; ii++ ){
8822 bq0x[ii] = 1.0f ; bq1x[ii] = bq2x[ii] = 0.0f ;
8823 }
8824 } else {
8825 COMPUTE_CAB(nbqx) ; dxqi = 1.0f/cb ;
8826 for( ii=0 ; ii < nbqx ; ii++ ){
8827 ccc = ca + ii*cb ; ee = HQwarp_eval_basis(ccc) ;
8828 bq0x[ii] = ee.a ; bq1x[ii] = ee.b ; bq2x[ii] = ee.c ;
8829 }
8830 }
8831
8832 if( Hflags & NWARP_NOYDEP_FLAG ){
8833 dyqi = 0.0f ;
8834 for( ii=0 ; ii < nbqy ; ii++ ){
8835 bq0y[ii] = 1.0f ; bq1y[ii] = bq2y[ii] = 0.0f ;
8836 }
8837 } else {
8838 COMPUTE_CAB(nbqy) ; dyqi = 1.0f/cb ;
8839 for( ii=0 ; ii < nbqy ; ii++ ){
8840 ccc = ca + ii*cb ; ee = HQwarp_eval_basis(ccc) ;
8841 bq0y[ii] = ee.a ; bq1y[ii] = ee.b ; bq2y[ii] = ee.c ;
8842 }
8843 }
8844
8845 if( Hflags & NWARP_NOZDEP_FLAG ){
8846 dzqi = 0.0f ;
8847 for( ii=0 ; ii < nbqz ; ii++ ){
8848 bq0z[ii] = 1.0f ; bq1z[ii] = bq2z[ii] = 0.0f ;
8849 }
8850 } else {
8851 COMPUTE_CAB(nbqz) ; dzqi = 1.0f/cb ;
8852 for( ii=0 ; ii < nbqz ; ii++ ){
8853 ccc = ca + ii*cb ; ee = HQwarp_eval_basis(ccc) ;
8854 bq0z[ii] = ee.a ; bq1z[ii] = ee.b ; bq2z[ii] = ee.c ;
8855 }
8856 }
8857
8858 /* 3D versions? */
8859
8860 /** 4 bytes per float, times 27 arrays **/
8861
8862 nbbqxyz = nbqx * nbqy * nbqz ; /* array size for 3D patch */
8863 if( sizeof(float)*27.0f*nbbqxyz/GIGA <= HmaxmemG ){ /* max size of 3D patch storage */
8864 int jj , kk , qq ;
8865 bbbqar = (float **)malloc(sizeof(float *)*27) ;
8866 for( ii=0 ; ii < 27 ; ii++ )
8867 bbbqar[ii] = (float *)malloc(sizeof(float)*nbbqxyz) ;
8868 for( qq=kk=0 ; kk < nbqz ; kk++ ){
8869 for( jj=0 ; jj < nbqy ; jj++ ){
8870 #pragma ivdep /* for Intel icc compiler */
8871 for( ii=0 ; ii < nbqx ; ii++,qq++ ){
8872 bbbqar[ 0][qq] = bq0z[kk]*bq0y[jj]*bq0x[ii];
8873 bbbqar[ 1][qq] = bq1z[kk]*bq0y[jj]*bq0x[ii];
8874 bbbqar[ 2][qq] = bq2z[kk]*bq0y[jj]*bq0x[ii];
8875 bbbqar[ 3][qq] = bq0z[kk]*bq1y[jj]*bq0x[ii];
8876 bbbqar[ 4][qq] = bq1z[kk]*bq1y[jj]*bq0x[ii];
8877 bbbqar[ 5][qq] = bq2z[kk]*bq1y[jj]*bq0x[ii];
8878 bbbqar[ 6][qq] = bq0z[kk]*bq2y[jj]*bq0x[ii];
8879 bbbqar[ 7][qq] = bq1z[kk]*bq2y[jj]*bq0x[ii];
8880 bbbqar[ 8][qq] = bq2z[kk]*bq2y[jj]*bq0x[ii];
8881 bbbqar[ 9][qq] = bq0z[kk]*bq0y[jj]*bq1x[ii];
8882 bbbqar[10][qq] = bq1z[kk]*bq0y[jj]*bq1x[ii];
8883 bbbqar[11][qq] = bq2z[kk]*bq0y[jj]*bq1x[ii];
8884 bbbqar[12][qq] = bq0z[kk]*bq1y[jj]*bq1x[ii];
8885 bbbqar[13][qq] = bq1z[kk]*bq1y[jj]*bq1x[ii];
8886 bbbqar[14][qq] = bq2z[kk]*bq1y[jj]*bq1x[ii];
8887 bbbqar[15][qq] = bq0z[kk]*bq2y[jj]*bq1x[ii];
8888 bbbqar[16][qq] = bq1z[kk]*bq2y[jj]*bq1x[ii];
8889 bbbqar[17][qq] = bq2z[kk]*bq2y[jj]*bq1x[ii];
8890 bbbqar[18][qq] = bq0z[kk]*bq0y[jj]*bq2x[ii];
8891 bbbqar[19][qq] = bq1z[kk]*bq0y[jj]*bq2x[ii];
8892 bbbqar[20][qq] = bq2z[kk]*bq0y[jj]*bq2x[ii];
8893 bbbqar[21][qq] = bq0z[kk]*bq1y[jj]*bq2x[ii];
8894 bbbqar[22][qq] = bq1z[kk]*bq1y[jj]*bq2x[ii];
8895 bbbqar[23][qq] = bq2z[kk]*bq1y[jj]*bq2x[ii];
8896 bbbqar[24][qq] = bq0z[kk]*bq2y[jj]*bq2x[ii];
8897 bbbqar[25][qq] = bq1z[kk]*bq2y[jj]*bq2x[ii];
8898 bbbqar[26][qq] = bq2z[kk]*bq2y[jj]*bq2x[ii];
8899 }}}
8900 }
8901
8902 Hwarp = IW3D_create(nbqx,nbqy,nbqz) ;
8903 AHwarp = IW3D_create(nbqx,nbqy,nbqz) ;
8904
8905 EXRETURN ;
8906 }
8907
8908 /*----------------------------------------------------------------------------*/
8909 /* Setup sinc compact basis arrays for patch = nx X ny X nz [05 Nov 2019] */
8910
HSCwarp_setup_basis(int nx,int ny,int nz,int flags)8911 void HSCwarp_setup_basis( int nx , int ny , int nz , int flags )
8912 {
8913 int ii ; float ca,cb,ccc ;
8914
8915 ENTRY("HSCwarp_setup_basis") ;
8916
8917 Hflags = IW3D_munge_flags(nx,ny,nz,flags) ;
8918 FREEIFNN(Hparmap) ;
8919
8920 if( (Hflags & NWARP_NODISP_FLAG) != 0 ){ /* not all params being used */
8921 int pm = 0 ;
8922 Hparmap = (int *)calloc(sizeof(int),3) ;
8923 if( !(Hflags & NWARP_NOXDIS_FLAG) ) Hparmap[pm++] ++ ; /* x param */
8924 if( !(Hflags & NWARP_NOYDIS_FLAG) ) Hparmap[pm++] ++ ; /* y param */
8925 if( !(Hflags & NWARP_NOYDIS_FLAG) ) Hparmap[pm++] ++ ; /* z param */
8926 Hnparmap = pm ;
8927 if( Hnparmap == 3 ){ free(Hparmap) ; Hparmap = NULL ; }
8928 } else {
8929 Hnparmap = 3 ;
8930 Hparmap = NULL ; /* no index translation needed */
8931 }
8932
8933 if( nx == nbsx && ny == nbsy && nz == nbsz &&
8934 Hwarp != NULL && AHwarp != NULL &&
8935 nx == Hwarp->nx && ny == Hwarp->ny && nz == Hwarp->nz ){
8936 IW3D_zero_fill(Hwarp) ; IW3D_zero_fill(AHwarp) ; EXRETURN ;
8937 }
8938
8939 if( Hwarp != NULL ){ IW3D_destroy( Hwarp); Hwarp = NULL; }
8940 if( AHwarp != NULL ){ IW3D_destroy(AHwarp); AHwarp = NULL; }
8941
8942 FREEIFNN(bs0x); nbsx=0;
8943 FREEIFNN(bs0y); nbsy=0;
8944 FREEIFNN(bs0z); nbsz=0;
8945
8946 if( bbbsar != NULL ){ free(bbbsar) ; nbbsxyz = 0 ; bbbsar = NULL ; }
8947
8948 if( Hflags < 0 ) EXRETURN ; /* this should not happen */
8949
8950 nbsx = nx ;
8951 bs0x = (float *)malloc(sizeof(float)*nbsx) ; /* 1D basis arrays */
8952 nbsy = ny ;
8953 bs0y = (float *)malloc(sizeof(float)*nbsy) ;
8954 nbsz = nz ;
8955 bs0z = (float *)malloc(sizeof(float)*nbsz) ;
8956
8957 nbsxy = nbsx*nbsy ; /* for indexing */
8958
8959 if( Hflags & NWARP_NOXDEP_FLAG ){
8960 dxsi = 0.0f ;
8961 for( ii=0 ; ii < nbsx ; ii++ ){
8962 bs0x[ii] = 1.0f ;
8963 }
8964 } else {
8965 COMPUTE_CAB(nbsx) ; dxsi = 1.0f/cb ; /* dxsi = half-width of patch */
8966 for( ii=0 ; ii < nbsx ; ii++ ){
8967 ccc = ca + ii*cb ; bs0x[ii] = HSCwarp_eval_basis(ccc) ;
8968 }
8969 }
8970
8971 if( Hflags & NWARP_NOYDEP_FLAG ){
8972 dysi = 0.0f ;
8973 for( ii=0 ; ii < nbsy ; ii++ ){
8974 bs0y[ii] = 1.0f ;
8975 }
8976 } else {
8977 COMPUTE_CAB(nbsy) ; dysi = 1.0f/cb ;
8978 for( ii=0 ; ii < nbsy ; ii++ ){
8979 ccc = ca + ii*cb ; bs0y[ii] = HSCwarp_eval_basis(ccc) ;
8980 }
8981 }
8982
8983 if( Hflags & NWARP_NOZDEP_FLAG ){
8984 dzsi = 0.0f ;
8985 for( ii=0 ; ii < nbsz ; ii++ ){
8986 bs0z[ii] = 1.0f ;
8987 }
8988 } else {
8989 COMPUTE_CAB(nbsz) ; dzsi = 1.0f/cb ;
8990 for( ii=0 ; ii < nbsz ; ii++ ){
8991 ccc = ca + ii*cb ; bs0z[ii] = HSCwarp_eval_basis(ccc) ;
8992 }
8993 }
8994
8995 nbbsxyz = nbsx * nbsy * nbsz ; /* array size of 3D patch */
8996 if( sizeof(float)*nbbsxyz/GIGA <= HmaxmemG ){ /* max size of 3D patch storage */
8997 int jj , kk , qq ;
8998 bbbsar = (float *)malloc(sizeof(float)*nbbsxyz) ;
8999 for( qq=kk=0 ; kk < nbsz ; kk++ ){
9000 for( jj=0 ; jj < nbsy ; jj++ ){
9001 #pragma ivdep /* for Intel icc compiler */
9002 for( ii=0 ; ii < nbsx ; ii++,qq++ ){
9003 bbbsar[qq] = bs0z[kk]*bs0y[jj]*bs0x[ii] ; /* only 1 3D patch here */
9004 }}}
9005 }
9006
9007 /* create empty patch warp, to be populated in HCwarp_load,
9008 given these basis function arrays and the warp parameters */
9009
9010 Hwarp = IW3D_create(nbsx,nbsy,nbsz) ; /* incremental patch warp */
9011 AHwarp = IW3D_create(nbsx,nbsy,nbsz) ; /* global warp(patch warp) in patch */
9012
9013 EXRETURN ;
9014 }
9015
9016 #endif /*(Q2)*/ /*############################################################*/
9017
9018 #if 1
9019 /*============================================================================*/
9020 /** (Q3) Functions to create a patch warp from basis functions and parameters */
9021 /** These functions look complicated, partly because there are 2 sets of
9022 functions and partly because there are separate cases for quintic and
9023 cubic bases, with special sub-cases further broken out for increased
9024 efficiency. But the underlying code is actually pretty simple:
9025
9026 H(x,y,z) = sum { param#p * basisfunc#p(x,y,z) }
9027 p */
9028 /*============================================================================*/
9029
9030 /*** Compute warp displacements at one voxel (index = qq);
9031 There are 4 (2x2) routines here:
9032 for cubic (HCwarp) and quintic (HQwarp);
9033 for using 1D basis arrays or 3D basis arrays. **/
9034
9035 /*** Add routine for basis5 functions,
9036 which always use 3D basis arrays [05 Nov 2015] **/
9037
9038 /*-----=====-----=====-----=====-----=====-----=====-----=====-----=====-----*/
9039 /* evaluate full 3D cubic warp the slower way (from 1D basis arrays) */
9040
HCwarp_eval_A(int qq,float * xx,float * yy,float * zz)9041 void HCwarp_eval_A( int qq , float *xx , float *yy , float *zz )
9042 {
9043 int ii,jj,kk ;
9044 float b0zb0yb0x,b1zb0yb0x, b0zb1yb0x,b1zb1yb0x,
9045 b0zb0yb1x,b1zb0yb1x, b0zb1yb1x,b1zb1yb1x ;
9046
9047 /* in this function, the 'b' values (3D warp components) are
9048 evaluated as tensor products of the underlying 1D functions */
9049
9050 /* convert 1D index to 3D index */
9051
9052 ii = qq % nbcx ; kk = qq / nbcxy ; jj = (qq-kk*nbcxy) / nbcx ;
9053
9054 /* multiply 1D basis functions to get 3D basis functions (2x2x2=8 of them) */
9055
9056 b0zb0yb0x = bc0z[kk]*bc0y[jj]*bc0x[ii]; b1zb0yb0x = bc1z[kk]*bc0y[jj]*bc0x[ii];
9057 b0zb1yb0x = bc0z[kk]*bc1y[jj]*bc0x[ii]; b1zb1yb0x = bc1z[kk]*bc1y[jj]*bc0x[ii];
9058 b0zb0yb1x = bc0z[kk]*bc0y[jj]*bc1x[ii]; b1zb0yb1x = bc1z[kk]*bc0y[jj]*bc1x[ii];
9059 b0zb1yb1x = bc0z[kk]*bc1y[jj]*bc1x[ii]; b1zb1yb1x = bc1z[kk]*bc1y[jj]*bc1x[ii];
9060
9061 /* multiply by parameters and sum up */
9062
9063 if( Hdox ) *xx = dxci *
9064 ( b0zb0yb0x*Hxpar[0] + b1zb0yb0x*Hxpar[1] + b0zb1yb0x*Hxpar[2]
9065 + b1zb1yb0x*Hxpar[3] + b0zb0yb1x*Hxpar[4] + b1zb0yb1x*Hxpar[5]
9066 + b0zb1yb1x*Hxpar[6] + b1zb1yb1x*Hxpar[7] ) ; else *xx = 0.0f ;
9067 if( Hdoy ) *yy = dyci *
9068 ( b0zb0yb0x*Hypar[0] + b1zb0yb0x*Hypar[1] + b0zb1yb0x*Hypar[2]
9069 + b1zb1yb0x*Hypar[3] + b0zb0yb1x*Hypar[4] + b1zb0yb1x*Hypar[5]
9070 + b0zb1yb1x*Hypar[6] + b1zb1yb1x*Hypar[7] ) ; else *yy = 0.0f ;
9071 if( Hdoz ) *zz = dzci *
9072 ( b0zb0yb0x*Hzpar[0] + b1zb0yb0x*Hzpar[1] + b0zb1yb0x*Hzpar[2]
9073 + b1zb1yb0x*Hzpar[3] + b0zb0yb1x*Hzpar[4] + b1zb0yb1x*Hzpar[5]
9074 + b0zb1yb1x*Hzpar[6] + b1zb1yb1x*Hzpar[7] ) ; else *zz = 0.0f ;
9075 return ;
9076 }
9077
9078 /*----------------------------------------------------------------------------*/
9079 /* evaluate full 3D cubic warp faster way (from pre-computed 3D basis arrays) */
9080
HCwarp_eval_B(int qq,float * xx,float * yy,float * zz)9081 void HCwarp_eval_B( int qq , float *xx , float *yy , float *zz )
9082 {
9083 float b0zb0yb0x,b1zb0yb0x, b0zb1yb0x,b1zb1yb0x,
9084 b0zb0yb1x,b1zb0yb1x, b0zb1yb1x,b1zb1yb1x ;
9085
9086 /* in this function, the 8 'b' values (warp components) were
9087 pre-evaluated in the bbbcar arrays, and so just need to
9088 be extracted; this method is faster, but obviously takes more memory */
9089
9090 b0zb0yb0x = bbbcar[0][qq] ; b1zb0yb0x = bbbcar[1][qq] ;
9091 b0zb1yb0x = bbbcar[2][qq] ; b1zb1yb0x = bbbcar[3][qq] ;
9092 b0zb0yb1x = bbbcar[4][qq] ; b1zb0yb1x = bbbcar[5][qq] ;
9093 b0zb1yb1x = bbbcar[6][qq] ; b1zb1yb1x = bbbcar[7][qq] ;
9094
9095 if( Hdox ) *xx = dxci *
9096 ( b0zb0yb0x*Hxpar[0] + b1zb0yb0x*Hxpar[1] + b0zb1yb0x*Hxpar[2]
9097 + b1zb1yb0x*Hxpar[3] + b0zb0yb1x*Hxpar[4] + b1zb0yb1x*Hxpar[5]
9098 + b0zb1yb1x*Hxpar[6] + b1zb1yb1x*Hxpar[7] ) ; else *xx = 0.0f ;
9099 if( Hdoy ) *yy = dyci *
9100 ( b0zb0yb0x*Hypar[0] + b1zb0yb0x*Hypar[1] + b0zb1yb0x*Hypar[2]
9101 + b1zb1yb0x*Hypar[3] + b0zb0yb1x*Hypar[4] + b1zb0yb1x*Hypar[5]
9102 + b0zb1yb1x*Hypar[6] + b1zb1yb1x*Hypar[7] ) ; else *yy = 0.0f ;
9103 if( Hdoz ) *zz = dzci *
9104 ( b0zb0yb0x*Hzpar[0] + b1zb0yb0x*Hzpar[1] + b0zb1yb0x*Hzpar[2]
9105 + b1zb1yb0x*Hzpar[3] + b0zb0yb1x*Hzpar[4] + b1zb0yb1x*Hzpar[5]
9106 + b0zb1yb1x*Hzpar[6] + b1zb1yb1x*Hzpar[7] ) ; else *zz = 0.0f ;
9107 return ;
9108 }
9109
9110 /*-----=====-----=====-----=====-----=====-----=====-----=====-----=====-----*/
9111 /* evaluate lite (cubic12) 3D warp the slower way (from 1D basis arrays) */
9112 /* cubic12 = just the 000, 001, 010, and 100 products = 4*3 functions */
9113
HCwarp_eval_AMM(int qq,float * xx,float * yy,float * zz)9114 void HCwarp_eval_AMM( int qq , float *xx , float *yy , float *zz )
9115 {
9116 int ii,jj,kk ;
9117 float b0zb0yb0x, b1zb0yb0x, b0zb1yb0x, b0zb0yb1x ;
9118
9119 /* in this function, the 'b' values (3D warp components) are
9120 evaluated as tensor products of the underlying 1D functions */
9121
9122 /* convert 1D index to 3D index */
9123
9124 ii = qq % nbcx ; kk = qq / nbcxy ; jj = (qq-kk*nbcxy) / nbcx ;
9125
9126 /* multiply 1D basis functions to get 3D basis functions */
9127
9128 b0zb0yb0x = bc0z[kk]*bc0y[jj]*bc0x[ii];
9129 b1zb0yb0x = bc1z[kk]*bc0y[jj]*bc0x[ii];
9130 b0zb1yb0x = bc0z[kk]*bc1y[jj]*bc0x[ii];
9131 b0zb0yb1x = bc0z[kk]*bc0y[jj]*bc1x[ii];
9132
9133 /* multiply by parameters and sum up */
9134
9135 if( Hdox ) *xx = dxci *
9136 ( b0zb0yb0x*Hxpar[0] + b1zb0yb0x*Hxpar[1]
9137 + b0zb1yb0x*Hxpar[2] + b0zb0yb1x*Hxpar[3] ) ; else *xx = 0.0f ;
9138 if( Hdoy ) *yy = dyci *
9139 ( b0zb0yb0x*Hypar[0] + b1zb0yb0x*Hypar[1]
9140 + b0zb1yb0x*Hypar[2] + b0zb0yb1x*Hypar[3] ) ; else *yy = 0.0f ;
9141 if( Hdoz ) *zz = dzci *
9142 ( b0zb0yb0x*Hzpar[0] + b1zb0yb0x*Hzpar[1]
9143 + b0zb1yb0x*Hzpar[2] + b0zb0yb1x*Hzpar[3] ) ; else *zz = 0.0f ;
9144 return ;
9145 }
9146
9147 /*----------------------------------------------------------------------------*/
9148 /* evaluate lite (cubic12) 3D warp the faster way (using 3D basis arrays) */
9149 /* = just the 000, 001, 010, and 100 products = 4*3 functions */
9150
HCwarp_eval_BMM(int qq,float * xx,float * yy,float * zz)9151 void HCwarp_eval_BMM( int qq , float *xx , float *yy , float *zz )
9152 {
9153 float b0zb0yb0x, b1zb0yb0x, b0zb1yb0x, b0zb0yb1x ;
9154
9155 /* in this function, the 8 'b' values (warp components) were
9156 pre-evaluated in the bbbcar arrays, and so just need to
9157 be extracted; this method is faster, but obviously takes more memory */
9158
9159 b0zb0yb0x = bbbcar[0][qq] ; b1zb0yb0x = bbbcar[1][qq] ;
9160 b0zb1yb0x = bbbcar[2][qq] ; b0zb0yb1x = bbbcar[4][qq] ; /* NB [4] not [3] */
9161 /* see calculation of bbbcar above */
9162 if( Hdox ) *xx = dxci *
9163 ( b0zb0yb0x*Hxpar[0] + b1zb0yb0x*Hxpar[1]
9164 + b0zb1yb0x*Hxpar[2] + b0zb0yb1x*Hxpar[3] ) ; else *xx = 0.0f ;
9165 if( Hdoy ) *yy = dyci *
9166 ( b0zb0yb0x*Hypar[0] + b1zb0yb0x*Hypar[1]
9167 + b0zb1yb0x*Hypar[2] + b0zb0yb1x*Hypar[3] ) ; else *yy = 0.0f ;
9168 if( Hdoz ) *zz = dzci *
9169 ( b0zb0yb0x*Hzpar[0] + b1zb0yb0x*Hzpar[1]
9170 + b0zb1yb0x*Hzpar[2] + b0zb0yb1x*Hzpar[3] ) ; else *zz = 0.0f ;
9171 return ;
9172 }
9173
9174 /*-----=====-----=====-----=====-----=====-----=====-----=====-----=====-----*/
9175 /* evaluate sinc compact 3D warp the slower way (from 1D basis arrays) */
9176
HSCwarp_eval_A(int qq,float * xx,float * yy,float * zz)9177 void HSCwarp_eval_A( int qq , float *xx , float *yy , float *zz )
9178 {
9179 int ii,jj,kk ;
9180 float b0zb0yb0x ;
9181
9182 ii = qq % nbsx ; kk = qq / nbsxy ; jj = (qq-kk*nbsxy) / nbsx ;
9183
9184 b0zb0yb0x = bs0z[kk]*bs0y[jj]*bs0x[ii];
9185
9186 /* multiply by parameters and sum up */
9187
9188 if( Hdox ) *xx = dxsi * b0zb0yb0x*Hxpar[0] ; else *xx = 0.0f ;
9189 if( Hdoy ) *yy = dysi * b0zb0yb0x*Hypar[0] ; else *yy = 0.0f ;
9190 if( Hdoz ) *zz = dzsi * b0zb0yb0x*Hzpar[0] ; else *zz = 0.0f ;
9191 return ;
9192 }
9193
9194 /*----------------------------------------------------------------------------*/
9195 /* evaluate sinc compact 3D warp the faster way (using 3D basis arrays) */
9196
HSCwarp_eval_B(int qq,float * xx,float * yy,float * zz)9197 void HSCwarp_eval_B( int qq , float *xx , float *yy , float *zz )
9198 {
9199 float b0zb0yb0x ;
9200
9201 b0zb0yb0x = bbbsar[qq] ;
9202 if( Hdox ) *xx = dxsi * b0zb0yb0x*Hxpar[0] ; else *xx = 0.0f ;
9203 if( Hdoy ) *yy = dysi * b0zb0yb0x*Hypar[0] ; else *yy = 0.0f ;
9204 if( Hdoz ) *zz = dzsi * b0zb0yb0x*Hzpar[0] ; else *zz = 0.0f ;
9205 return ;
9206 }
9207
9208 /*-----=====-----=====-----=====-----=====-----=====-----=====-----=====-----*/
9209 /* evaluate full 3D quintic warp the slower way (from 1D basis arrays) */
9210
HQwarp_eval_A(int qq,float * xx,float * yy,float * zz)9211 void HQwarp_eval_A( int qq , float *xx , float *yy , float *zz )
9212 {
9213 int ii,jj,kk ;
9214 float b0zb0yb0x,b1zb0yb0x, b2zb0yb0x,b0zb1yb0x, b1zb1yb0x,b2zb1yb0x,
9215 b0zb2yb0x,b1zb2yb0x, b2zb2yb0x,b0zb0yb1x, b1zb0yb1x,b2zb0yb1x,
9216 b0zb1yb1x,b1zb1yb1x, b2zb1yb1x,b0zb2yb1x, b1zb2yb1x,b2zb2yb1x,
9217 b0zb0yb2x,b1zb0yb2x, b2zb0yb2x,b0zb1yb2x, b1zb1yb2x,b2zb1yb2x,
9218 b0zb2yb2x,b1zb2yb2x, b2zb2yb2x ;
9219
9220 ii = qq % nbqx; kk = qq / nbqxy; jj = (qq-kk*nbqxy) / nbqx; /* get 3D index */
9221
9222 /* multiply 3x3x3 1D basis functions to get the 27 3D basis functions */
9223
9224 b0zb0yb0x = bq0z[kk]*bq0y[jj]*bq0x[ii]; b1zb0yb0x = bq1z[kk]*bq0y[jj]*bq0x[ii];
9225 b2zb0yb0x = bq2z[kk]*bq0y[jj]*bq0x[ii]; b0zb1yb0x = bq0z[kk]*bq1y[jj]*bq0x[ii];
9226 b1zb1yb0x = bq1z[kk]*bq1y[jj]*bq0x[ii]; b2zb1yb0x = bq2z[kk]*bq1y[jj]*bq0x[ii];
9227 b0zb2yb0x = bq0z[kk]*bq2y[jj]*bq0x[ii]; b1zb2yb0x = bq1z[kk]*bq2y[jj]*bq0x[ii];
9228 b2zb2yb0x = bq2z[kk]*bq2y[jj]*bq0x[ii]; b0zb0yb1x = bq0z[kk]*bq0y[jj]*bq1x[ii];
9229 b1zb0yb1x = bq1z[kk]*bq0y[jj]*bq1x[ii]; b2zb0yb1x = bq2z[kk]*bq0y[jj]*bq1x[ii];
9230 b0zb1yb1x = bq0z[kk]*bq1y[jj]*bq1x[ii]; b1zb1yb1x = bq1z[kk]*bq1y[jj]*bq1x[ii];
9231 b2zb1yb1x = bq2z[kk]*bq1y[jj]*bq1x[ii]; b0zb2yb1x = bq0z[kk]*bq2y[jj]*bq1x[ii];
9232 b1zb2yb1x = bq1z[kk]*bq2y[jj]*bq1x[ii]; b2zb2yb1x = bq2z[kk]*bq2y[jj]*bq1x[ii];
9233 b0zb0yb2x = bq0z[kk]*bq0y[jj]*bq2x[ii]; b1zb0yb2x = bq1z[kk]*bq0y[jj]*bq2x[ii];
9234 b2zb0yb2x = bq2z[kk]*bq0y[jj]*bq2x[ii]; b0zb1yb2x = bq0z[kk]*bq1y[jj]*bq2x[ii];
9235 b1zb1yb2x = bq1z[kk]*bq1y[jj]*bq2x[ii]; b2zb1yb2x = bq2z[kk]*bq1y[jj]*bq2x[ii];
9236 b0zb2yb2x = bq0z[kk]*bq2y[jj]*bq2x[ii]; b1zb2yb2x = bq1z[kk]*bq2y[jj]*bq2x[ii];
9237 b2zb2yb2x = bq2z[kk]*bq2y[jj]*bq2x[ii];
9238
9239 /* multiply by parameters and add up */
9240
9241 if( Hdox ) *xx = dxqi *
9242 ( b0zb0yb0x*Hxpar[ 0] + b1zb0yb0x*Hxpar[ 1] + b2zb0yb0x*Hxpar[ 2]
9243 + b0zb1yb0x*Hxpar[ 3] + b1zb1yb0x*Hxpar[ 4] + b2zb1yb0x*Hxpar[ 5]
9244 + b0zb2yb0x*Hxpar[ 6] + b1zb2yb0x*Hxpar[ 7] + b2zb2yb0x*Hxpar[ 8]
9245 + b0zb0yb1x*Hxpar[ 9] + b1zb0yb1x*Hxpar[10] + b2zb0yb1x*Hxpar[11]
9246 + b0zb1yb1x*Hxpar[12] + b1zb1yb1x*Hxpar[13] + b2zb1yb1x*Hxpar[14]
9247 + b0zb2yb1x*Hxpar[15] + b1zb2yb1x*Hxpar[16] + b2zb2yb1x*Hxpar[17]
9248 + b0zb0yb2x*Hxpar[18] + b1zb0yb2x*Hxpar[19] + b2zb0yb2x*Hxpar[20]
9249 + b0zb1yb2x*Hxpar[21] + b1zb1yb2x*Hxpar[22] + b2zb1yb2x*Hxpar[23]
9250 + b0zb2yb2x*Hxpar[24] + b1zb2yb2x*Hxpar[25] + b2zb2yb2x*Hxpar[26] ) ; else *xx = 0.0f ;
9251 if( Hdoy ) *yy = dyqi *
9252 ( b0zb0yb0x*Hypar[ 0] + b1zb0yb0x*Hypar[ 1] + b2zb0yb0x*Hypar[ 2]
9253 + b0zb1yb0x*Hypar[ 3] + b1zb1yb0x*Hypar[ 4] + b2zb1yb0x*Hypar[ 5]
9254 + b0zb2yb0x*Hypar[ 6] + b1zb2yb0x*Hypar[ 7] + b2zb2yb0x*Hypar[ 8]
9255 + b0zb0yb1x*Hypar[ 9] + b1zb0yb1x*Hypar[10] + b2zb0yb1x*Hypar[11]
9256 + b0zb1yb1x*Hypar[12] + b1zb1yb1x*Hypar[13] + b2zb1yb1x*Hypar[14]
9257 + b0zb2yb1x*Hypar[15] + b1zb2yb1x*Hypar[16] + b2zb2yb1x*Hypar[17]
9258 + b0zb0yb2x*Hypar[18] + b1zb0yb2x*Hypar[19] + b2zb0yb2x*Hypar[20]
9259 + b0zb1yb2x*Hypar[21] + b1zb1yb2x*Hypar[22] + b2zb1yb2x*Hypar[23]
9260 + b0zb2yb2x*Hypar[24] + b1zb2yb2x*Hypar[25] + b2zb2yb2x*Hypar[26] ) ; else *yy = 0.0f ;
9261 if( Hdoz ) *zz = dzqi *
9262 ( b0zb0yb0x*Hzpar[ 0] + b1zb0yb0x*Hzpar[ 1] + b2zb0yb0x*Hzpar[ 2]
9263 + b0zb1yb0x*Hzpar[ 3] + b1zb1yb0x*Hzpar[ 4] + b2zb1yb0x*Hzpar[ 5]
9264 + b0zb2yb0x*Hzpar[ 6] + b1zb2yb0x*Hzpar[ 7] + b2zb2yb0x*Hzpar[ 8]
9265 + b0zb0yb1x*Hzpar[ 9] + b1zb0yb1x*Hzpar[10] + b2zb0yb1x*Hzpar[11]
9266 + b0zb1yb1x*Hzpar[12] + b1zb1yb1x*Hzpar[13] + b2zb1yb1x*Hzpar[14]
9267 + b0zb2yb1x*Hzpar[15] + b1zb2yb1x*Hzpar[16] + b2zb2yb1x*Hzpar[17]
9268 + b0zb0yb2x*Hzpar[18] + b1zb0yb2x*Hzpar[19] + b2zb0yb2x*Hzpar[20]
9269 + b0zb1yb2x*Hzpar[21] + b1zb1yb2x*Hzpar[22] + b2zb1yb2x*Hzpar[23]
9270 + b0zb2yb2x*Hzpar[24] + b1zb2yb2x*Hzpar[25] + b2zb2yb2x*Hzpar[26] ) ; else *zz = 0.0f ;
9271 return ;
9272 }
9273
9274 /*----------------------------------------------------------------------------*/
9275 /* evaluate full 3D quintic warp the faster way (from 3D basis arrays) */
9276
HQwarp_eval_B(int qq,float * xx,float * yy,float * zz)9277 void HQwarp_eval_B( int qq , float *xx , float *yy , float *zz )
9278 {
9279
9280 #if 1 /* this code was faster (a little) */
9281 float t1,t2,t3 ; int jj ;
9282
9283 t1 = t2 = t3 = 0.0f ;
9284 for( jj=0 ; jj < 27 ; jj+=3 ){ /* unrolled by 3 */
9285 t1 += bbbqar[jj][qq]*Hxpar[jj] + bbbqar[jj+1][qq]*Hxpar[jj+1] + bbbqar[jj+2][qq]*Hxpar[jj+2] ;
9286 t2 += bbbqar[jj][qq]*Hypar[jj] + bbbqar[jj+1][qq]*Hypar[jj+1] + bbbqar[jj+2][qq]*Hypar[jj+2] ;
9287 t3 += bbbqar[jj][qq]*Hzpar[jj] + bbbqar[jj+1][qq]*Hzpar[jj+1] + bbbqar[jj+2][qq]*Hzpar[jj+2] ;
9288 }
9289 *xx = (Hdox) ? t1 : 0.0f ;
9290 *yy = (Hdoy) ? t2 : 0.0f ;
9291 *zz = (Hdoz) ? t3 : 0.0f ;
9292 #else
9293 float b0zb0yb0x,b1zb0yb0x, b2zb0yb0x,b0zb1yb0x, b1zb1yb0x,b2zb1yb0x,
9294 b0zb2yb0x,b1zb2yb0x, b2zb2yb0x,b0zb0yb1x, b1zb0yb1x,b2zb0yb1x,
9295 b0zb1yb1x,b1zb1yb1x, b2zb1yb1x,b0zb2yb1x, b1zb2yb1x,b2zb2yb1x,
9296 b0zb0yb2x,b1zb0yb2x, b2zb0yb2x,b0zb1yb2x, b1zb1yb2x,b2zb1yb2x,
9297 b0zb2yb2x,b1zb2yb2x, b2zb2yb2x ;
9298
9299 b0zb0yb0x = bbbqar[ 0][qq] ; b1zb0yb0x = bbbqar[ 1][qq] ; b2zb0yb0x = bbbqar[ 2][qq] ;
9300 b0zb1yb0x = bbbqar[ 3][qq] ; b1zb1yb0x = bbbqar[ 4][qq] ; b2zb1yb0x = bbbqar[ 5][qq] ;
9301 b0zb2yb0x = bbbqar[ 6][qq] ; b1zb2yb0x = bbbqar[ 7][qq] ; b2zb2yb0x = bbbqar[ 8][qq] ;
9302 b0zb0yb1x = bbbqar[ 9][qq] ; b1zb0yb1x = bbbqar[10][qq] ; b2zb0yb1x = bbbqar[11][qq] ;
9303 b0zb1yb1x = bbbqar[12][qq] ; b1zb1yb1x = bbbqar[13][qq] ; b2zb1yb1x = bbbqar[14][qq] ;
9304 b0zb2yb1x = bbbqar[15][qq] ; b1zb2yb1x = bbbqar[16][qq] ; b2zb2yb1x = bbbqar[17][qq] ;
9305 b0zb0yb2x = bbbqar[18][qq] ; b1zb0yb2x = bbbqar[19][qq] ; b2zb0yb2x = bbbqar[20][qq] ;
9306 b0zb1yb2x = bbbqar[21][qq] ; b1zb1yb2x = bbbqar[22][qq] ; b2zb1yb2x = bbbqar[23][qq] ;
9307 b0zb2yb2x = bbbqar[24][qq] ; b1zb2yb2x = bbbqar[25][qq] ; b2zb2yb2x = bbbqar[26][qq] ;
9308
9309 if( Hdox ) *xx = dxqi *
9310 ( b0zb0yb0x*Hxpar[ 0] + b1zb0yb0x*Hxpar[ 1] + b2zb0yb0x*Hxpar[ 2]
9311 + b0zb1yb0x*Hxpar[ 3] + b1zb1yb0x*Hxpar[ 4] + b2zb1yb0x*Hxpar[ 5]
9312 + b0zb2yb0x*Hxpar[ 6] + b1zb2yb0x*Hxpar[ 7] + b2zb2yb0x*Hxpar[ 8]
9313 + b0zb0yb1x*Hxpar[ 9] + b1zb0yb1x*Hxpar[10] + b2zb0yb1x*Hxpar[11]
9314 + b0zb1yb1x*Hxpar[12] + b1zb1yb1x*Hxpar[13] + b2zb1yb1x*Hxpar[14]
9315 + b0zb2yb1x*Hxpar[15] + b1zb2yb1x*Hxpar[16] + b2zb2yb1x*Hxpar[17]
9316 + b0zb0yb2x*Hxpar[18] + b1zb0yb2x*Hxpar[19] + b2zb0yb2x*Hxpar[20]
9317 + b0zb1yb2x*Hxpar[21] + b1zb1yb2x*Hxpar[22] + b2zb1yb2x*Hxpar[23]
9318 + b0zb2yb2x*Hxpar[24] + b1zb2yb2x*Hxpar[25] + b2zb2yb2x*Hxpar[26] ) ; else *xx = 0.0f ;
9319 if( Hdoy ) *yy = dyqi *
9320 ( b0zb0yb0x*Hypar[ 0] + b1zb0yb0x*Hypar[ 1] + b2zb0yb0x*Hypar[ 2]
9321 + b0zb1yb0x*Hypar[ 3] + b1zb1yb0x*Hypar[ 4] + b2zb1yb0x*Hypar[ 5]
9322 + b0zb2yb0x*Hypar[ 6] + b1zb2yb0x*Hypar[ 7] + b2zb2yb0x*Hypar[ 8]
9323 + b0zb0yb1x*Hypar[ 9] + b1zb0yb1x*Hypar[10] + b2zb0yb1x*Hypar[11]
9324 + b0zb1yb1x*Hypar[12] + b1zb1yb1x*Hypar[13] + b2zb1yb1x*Hypar[14]
9325 + b0zb2yb1x*Hypar[15] + b1zb2yb1x*Hypar[16] + b2zb2yb1x*Hypar[17]
9326 + b0zb0yb2x*Hypar[18] + b1zb0yb2x*Hypar[19] + b2zb0yb2x*Hypar[20]
9327 + b0zb1yb2x*Hypar[21] + b1zb1yb2x*Hypar[22] + b2zb1yb2x*Hypar[23]
9328 + b0zb2yb2x*Hypar[24] + b1zb2yb2x*Hypar[25] + b2zb2yb2x*Hypar[26] ) ; else *yy = 0.0f ;
9329 if( Hdoz ) *zz = dzqi *
9330 ( b0zb0yb0x*Hzpar[ 0] + b1zb0yb0x*Hzpar[ 1] + b2zb0yb0x*Hzpar[ 2]
9331 + b0zb1yb0x*Hzpar[ 3] + b1zb1yb0x*Hzpar[ 4] + b2zb1yb0x*Hzpar[ 5]
9332 + b0zb2yb0x*Hzpar[ 6] + b1zb2yb0x*Hzpar[ 7] + b2zb2yb0x*Hzpar[ 8]
9333 + b0zb0yb1x*Hzpar[ 9] + b1zb0yb1x*Hzpar[10] + b2zb0yb1x*Hzpar[11]
9334 + b0zb1yb1x*Hzpar[12] + b1zb1yb1x*Hzpar[13] + b2zb1yb1x*Hzpar[14]
9335 + b0zb2yb1x*Hzpar[15] + b1zb2yb1x*Hzpar[16] + b2zb2yb1x*Hzpar[17]
9336 + b0zb0yb2x*Hzpar[18] + b1zb0yb2x*Hzpar[19] + b2zb0yb2x*Hzpar[20]
9337 + b0zb1yb2x*Hzpar[21] + b1zb1yb2x*Hzpar[22] + b2zb1yb2x*Hzpar[23]
9338 + b0zb2yb2x*Hzpar[24] + b1zb2yb2x*Hzpar[25] + b2zb2yb2x*Hzpar[26] ) ; else *zz = 0.0f ;
9339 #endif
9340
9341 return ;
9342 }
9343
9344 /*-----=====-----=====-----=====-----=====-----=====-----=====-----=====-----*/
9345 /* evaluate lite 3D quintic warp the slower way (from 1D basis arrays) */
9346 /* This just uses 30 parameters = the set of basis
9347 functions whose tensor indexes add up to only 0, 1, or 2 */
9348
HQwarp_eval_AMM(int qq,float * xx,float * yy,float * zz)9349 void HQwarp_eval_AMM( int qq , float *xx , float *yy , float *zz )
9350 {
9351 int ii,jj,kk ;
9352 float b0zb0yb0x,b1zb0yb0x, b2zb0yb0x,b0zb1yb0x, b1zb1yb0x,
9353 b0zb2yb0x,b0zb0yb1x, b1zb0yb1x,b0zb1yb1x, b0zb0yb2x ;
9354
9355 ii = qq % nbqx; kk = qq / nbqxy; jj = (qq-kk*nbqxy) / nbqx; /* get 3D index */
9356
9357 /* multiply 3x3x3 1D basis functions to get the 10 3D basis functions */
9358
9359 b0zb0yb0x = bq0z[kk]*bq0y[jj]*bq0x[ii]; b1zb0yb0x = bq1z[kk]*bq0y[jj]*bq0x[ii];
9360 b2zb0yb0x = bq2z[kk]*bq0y[jj]*bq0x[ii]; b0zb1yb0x = bq0z[kk]*bq1y[jj]*bq0x[ii];
9361 b1zb1yb0x = bq1z[kk]*bq1y[jj]*bq0x[ii]; b0zb2yb0x = bq0z[kk]*bq2y[jj]*bq0x[ii];
9362 b0zb0yb1x = bq0z[kk]*bq0y[jj]*bq1x[ii]; b1zb0yb1x = bq1z[kk]*bq0y[jj]*bq1x[ii];
9363 b0zb1yb1x = bq0z[kk]*bq1y[jj]*bq1x[ii]; b0zb0yb2x = bq0z[kk]*bq0y[jj]*bq2x[ii];
9364
9365 /* multiply by parameters and add up */
9366
9367 if( Hdox ) *xx = dxqi *
9368 ( b0zb0yb0x*Hxpar[0] + b1zb0yb0x*Hxpar[1] + b2zb0yb0x*Hxpar[2]
9369 + b0zb1yb0x*Hxpar[3] + b1zb1yb0x*Hxpar[4] + b0zb2yb0x*Hxpar[5]
9370 + b0zb0yb1x*Hxpar[6] + b1zb0yb1x*Hxpar[7] + b0zb1yb1x*Hxpar[8]
9371 + b0zb0yb2x*Hxpar[9] ) ; else *xx = 0.0f ;
9372 if( Hdoy ) *yy = dyqi *
9373 ( b0zb0yb0x*Hypar[0] + b1zb0yb0x*Hypar[1] + b2zb0yb0x*Hypar[2]
9374 + b0zb1yb0x*Hypar[3] + b1zb1yb0x*Hypar[4] + b0zb2yb0x*Hypar[5]
9375 + b0zb0yb1x*Hypar[6] + b1zb0yb1x*Hypar[7] + b0zb1yb1x*Hypar[8]
9376 + b0zb0yb2x*Hypar[9] ) ; else *yy = 0.0f ;
9377 if( Hdoz ) *zz = dzqi *
9378 ( b0zb0yb0x*Hzpar[0] + b1zb0yb0x*Hzpar[1] + b2zb0yb0x*Hzpar[2]
9379 + b0zb1yb0x*Hzpar[3] + b1zb1yb0x*Hzpar[4] + b0zb2yb0x*Hzpar[5]
9380 + b0zb0yb1x*Hzpar[6] + b1zb0yb1x*Hzpar[7] + b0zb1yb1x*Hzpar[8]
9381 + b0zb0yb2x*Hzpar[9] ) ; else *zz = 0.0f ;
9382
9383 return ;
9384 }
9385
9386 /*----------------------------------------------------------------------------*/
9387 /* evaluate lite 3D quintic warp the faster way (from 3D basis arrays) */
9388
HQwarp_eval_BMM(int qq,float * xx,float * yy,float * zz)9389 void HQwarp_eval_BMM( int qq , float *xx , float *yy , float *zz )
9390 {
9391 float b0zb0yb0x,b1zb0yb0x, b2zb0yb0x,b0zb1yb0x, b1zb1yb0x,
9392 b0zb2yb0x,b0zb0yb1x, b1zb0yb1x,b0zb1yb1x, b0zb0yb2x ;
9393
9394 /**...................................................................
9395 Note that the bbbqar[indexes] in the assignments that follow this
9396 comment are not sequential, because we are only using elements
9397 whose sum of basis funcs add up to 0, 1, or 2, and these are
9398 the definitions of bbbqar from far above:
9399 bbbqar[ 0][qq] = bq0z[kk]*bq0y[jj]*bq0x[ii]; 0 0 0
9400 bbbqar[ 1][qq] = bq1z[kk]*bq0y[jj]*bq0x[ii]; 1 0 0
9401 bbbqar[ 2][qq] = bq2z[kk]*bq0y[jj]*bq0x[ii]; 2 0 0
9402 bbbqar[ 3][qq] = bq0z[kk]*bq1y[jj]*bq0x[ii]; 0 1 0
9403 bbbqar[ 4][qq] = bq1z[kk]*bq1y[jj]*bq0x[ii]; 1 1 0
9404 bbbqar[ 6][qq] = bq0z[kk]*bq2y[jj]*bq0x[ii]; 0 2 0
9405 bbbqar[ 9][qq] = bq0z[kk]*bq0y[jj]*bq1x[ii]; 0 0 1
9406 bbbqar[10][qq] = bq1z[kk]*bq0y[jj]*bq1x[ii]; 1 0 1
9407 bbbqar[12][qq] = bq0z[kk]*bq1y[jj]*bq1x[ii]; 0 1 1
9408 bbbqar[18][qq] = bq0z[kk]*bq0y[jj]*bq2x[ii]; 0 0 2
9409 ......................................................................**/
9410
9411 b0zb0yb0x = bbbqar[ 0][qq] ; b1zb0yb0x = bbbqar[ 1][qq] ; b2zb0yb0x = bbbqar[ 2][qq] ;
9412 b0zb1yb0x = bbbqar[ 3][qq] ; b1zb1yb0x = bbbqar[ 4][qq] ; b0zb2yb0x = bbbqar[ 6][qq] ;
9413 b0zb0yb1x = bbbqar[ 9][qq] ; b1zb0yb1x = bbbqar[10][qq] ; b0zb1yb1x = bbbqar[12][qq] ;
9414 b0zb0yb2x = bbbqar[18][qq] ;
9415
9416 if( Hdox ) *xx = dxqi *
9417 ( b0zb0yb0x*Hxpar[0] + b1zb0yb0x*Hxpar[1] + b2zb0yb0x*Hxpar[2]
9418 + b0zb1yb0x*Hxpar[3] + b1zb1yb0x*Hxpar[4] + b0zb2yb0x*Hxpar[5]
9419 + b0zb0yb1x*Hxpar[6] + b1zb0yb1x*Hxpar[7] + b0zb1yb1x*Hxpar[8]
9420 + b0zb0yb2x*Hxpar[9] ) ; else *xx = 0.0f ;
9421 if( Hdoy ) *yy = dyqi *
9422 ( b0zb0yb0x*Hypar[0] + b1zb0yb0x*Hypar[1] + b2zb0yb0x*Hypar[2]
9423 + b0zb1yb0x*Hypar[3] + b1zb1yb0x*Hypar[4] + b0zb2yb0x*Hypar[5]
9424 + b0zb0yb1x*Hypar[6] + b1zb0yb1x*Hypar[7] + b0zb1yb1x*Hypar[8]
9425 + b0zb0yb2x*Hypar[9] ) ; else *yy = 0.0f ;
9426 if( Hdoz ) *zz = dzqi *
9427 ( b0zb0yb0x*Hzpar[0] + b1zb0yb0x*Hzpar[1] + b2zb0yb0x*Hzpar[2]
9428 + b0zb1yb0x*Hzpar[3] + b1zb1yb0x*Hzpar[4] + b0zb2yb0x*Hzpar[5]
9429 + b0zb0yb1x*Hzpar[6] + b1zb0yb1x*Hzpar[7] + b0zb1yb1x*Hzpar[8]
9430 + b0zb0yb2x*Hzpar[9] ) ; else *zz = 0.0f ;
9431
9432 return ;
9433 }
9434
9435 #ifdef ALLOW_BASIS5
9436 /*----------------------------------------------------------------------------*/
9437 /* The _basis3, _basis4, _basis5 functions below had their basic elements
9438 generated by the following tcsh script, followed by manual editing to
9439 make things look good. The example below is for basis5 (nb5=5) -- the way
9440 3dQwarp is set up at present, only basis5 can be executed (-5final).
9441
9442 HOWEVER: The C code was modified Dec 2018 for 'lite' purposes - RWC.
9443
9444 #!/bin/tcsh
9445
9446 set nb5 = 5
9447 @ nbb = $nb5 - 1
9448
9449 echo "float"
9450 foreach rr ( `count -dig 1 0 $nbb` )
9451 foreach qq ( `count -dig 1 0 $nbb` )
9452 foreach pp ( `count -dig 1 0 $nbb` )
9453 echo " b${pp}zb${qq}yb${rr}x,"
9454 end
9455 end
9456 end
9457
9458 echo
9459
9460 foreach rr ( `count -dig 1 0 $nbb` )
9461 foreach qq ( `count -dig 1 0 $nbb` )
9462 foreach pp ( `count -dig 1 0 $nbb` )
9463 @ ss = $pp + $qq * $nb5 + $rr * $nb5 * $nb5
9464 echo "b${pp}zb${qq}yb${rr}x = bbbcar[$ss][qq] ;"
9465 end
9466 end
9467 end
9468
9469 echo
9470
9471 foreach rr ( `count -dig 1 0 $nbb` )
9472 foreach qq ( `count -dig 1 0 $nbb` )
9473 foreach pp ( `count -dig 1 0 $nbb` )
9474 @ ss = $pp + $qq * $nb5 + $rr * $nb5 * $nb5
9475 echo " + b${pp}zb${qq}yb${rr}x*Hxpar[$ss]"
9476 end
9477 end
9478 end
9479 *//*--------------------------------------------------------------------------*/
9480
9481 /*............................................................................*/
9482 /* evaluate BASIS5 3D warps the faster way (from 3D basis arrays) */
9483
HCwarp_eval_B_basis345(int qin,float * xx,float * yy,float * zz)9484 void HCwarp_eval_B_basis345( int qin , float *xx , float *yy , float *zz )
9485 {
9486 float t1,t2,t3,t4,t5,t6,t7 ; int qq=qin , jj , np1=H5nparm-1 ;
9487
9488 if( np1 <= 0 ) return ; /* this is bad */
9489
9490 t1 = t2 = t3 = 0.0f ;
9491 for( jj=0 ; jj < np1 ; jj+=2 ){ /* unrolled by 2 for optimizer */
9492 t1 += bbbcar[jj][qq]*Hxpar[jj] + bbbcar[jj+1][qq]*Hxpar[jj+1] ;
9493 t2 += bbbcar[jj][qq]*Hypar[jj] + bbbcar[jj+1][qq]*Hypar[jj+1] ;
9494 t3 += bbbcar[jj][qq]*Hzpar[jj] + bbbcar[jj+1][qq]*Hzpar[jj+1] ;
9495 }
9496 if( jj < H5nparm ){
9497 t1 += bbbcar[jj][qq]*Hxpar[jj] ;
9498 t2 += bbbcar[jj][qq]*Hypar[jj] ;
9499 t3 += bbbcar[jj][qq]*Hzpar[jj] ;
9500 }
9501 *xx = (Hdox) ? t1 : 0.0f ;
9502 *yy = (Hdoy) ? t2 : 0.0f ;
9503 *zz = (Hdoz) ? t3 : 0.0f ;
9504
9505 return ;
9506 }
9507 #endif /* ALLOW_BASIS5 */
9508
9509 #endif /*(Q3)*/ /*############################################################*/
9510
9511 #if 1
9512 /*============================================================================*/
9513 /** (Q4) Evaluate the incrementally warped source image at the combination
9514 of the current global warp and the patch warp
9515
9516 --: This function is a key part of the 3dQwarp empire -- it is where the :--
9517 --: source image is warped so it can be compared to the base image, :--
9518 --: so it will be called ** A LOT **. :--
9519 --: It was cloned into Hwarp_apply_plusminus() for warping the base and :--
9520 --: source images together (in opposite directions). If one wanted to :--
9521 --: match vector-valued images in some way, one central necessity would :--
9522 --: be to generalize this function to deal with such beasts. :--*/
9523 /*============================================================================*/
9524
9525 /*----------------------------------------------------------------------------*/
9526 /* Evaluate Hsrcim[ Haawarp(Hwarp(x)) ] into the val[] array.
9527 Note that Haawarp is a global warp for Hsrcim, whereas Hwarp is just
9528 a local patch.
9529 On output, the val[] array contains the linearly interpolated warped image
9530 values over this patch.
9531 Also evaluate the composed warp Haawarp[Hwarp(x)] into AHwarp for future
9532 utility. AHwarp is a local patch that fits into Haawarp later, when
9533 it needs to be updated at the end of a patch optimization step.
9534 (Actually, AHwarp will not necessarily be computed if need_AH is zero.)
9535 *//*--------------------------------------------------------------------------*/
9536
Hwarp_apply(float * val)9537 void Hwarp_apply( float *val )
9538 {
9539 int nbx,nby,nbz , nbxy,nbxyz , nAx,nAy,nAz , nAx1,nAy1,nAz1 , nAxy ;
9540 float nAxh,nAyh,nAzh ;
9541 float *hxd,*hyd,*hzd , *Axd,*Ayd,*Azd , *sar , *bxd,*byd,*bzd ;
9542 void (*Heval)(int,float *,float *,float *) = NULL ; /* compute Hwarp at one index */
9543
9544 ENTRY("Hwarp_apply") ;
9545
9546 /* bad inputs? should never happen */
9547
9548 if( Hsrcim == NULL || Haawarp == NULL || val == NULL || Hwarp == NULL ) EXRETURN ;
9549
9550 /* get local pointers to the various displacement arrays in the various warps */
9551
9552 hxd = Hwarp->xd ; hyd = Hwarp->yd ; hzd = Hwarp->zd ; /* Hwarp delta (patch) */
9553 Axd = Haawarp->xd; Ayd = Haawarp->yd; Azd = Haawarp->zd; /* Haawarp (global) */
9554 bxd = AHwarp->xd ; byd = AHwarp->yd ; bzd = AHwarp->zd ; /* AHwarp delta (patch) */
9555
9556 /* decide if this is a cubic or quintic (or sincc) warp, load patch grid sizes */
9557
9558 if( WARP_IS_QUINTIC(Hbasis_code) ){ nbx = nbqx ; nby = nbqy ; nbz = nbqz ; }
9559 else if( WARP_IS_CUBIC(Hbasis_code) ){ nbx = nbcx ; nby = nbcy ; nbz = nbcz ; }
9560 else /* sincc warp case */ { nbx = nbsx ; nby = nbsy ; nbz = nbsz ; }
9561
9562 nbxy = nbx*nby ; nbxyz = nbxy*nbz ;
9563
9564 /* choose which parameter-to-patch warp function to use, given the
9565 choice of basis functions and whether 3D basis arrays (bbb[cqs]ar)
9566 exist; Heval(qq) will give us the warp displacements at location
9567 qq, given the parameters defining the warp - stored in H[xyz]par. */
9568
9569 switch( Hbasis_code ){
9570 case MRI_CUBIC: /* the oldie but goodie */
9571 Heval = (bbbcar == NULL) ? HCwarp_eval_A : HCwarp_eval_B ;
9572 break ;
9573
9574 case MRI_CUBIC_LITE: /* jetlag inspiration in Guangzhou China [Dec 2018] */
9575 Heval = (bbbcar == NULL) ? HCwarp_eval_AMM : HCwarp_eval_BMM ;
9576 break ;
9577
9578 case MRI_QUINTIC:
9579 Heval = (bbbqar == NULL) ? HQwarp_eval_A : HQwarp_eval_B ;
9580 break ;
9581
9582 case MRI_QUINTIC_LITE:
9583 Heval = (bbbqar == NULL) ? HQwarp_eval_AMM : HQwarp_eval_BMM ;
9584 break ;
9585
9586 case MRI_SINCC:
9587 Heval = (bbbsar == NULL) ? HSCwarp_eval_A : HSCwarp_eval_B ;
9588 break ;
9589
9590 #ifdef ALLOW_BASIS5
9591 case MRI_CUBIC_PLUS_1:
9592 case MRI_CUBIC_PLUS_2:
9593 case MRI_CUBIC_PLUS_3:
9594 Heval = HCwarp_eval_B_basis345 ;
9595 break ;
9596 #endif
9597
9598 default: /* should NEVER happen */
9599 ERROR_exit("Illegal Hbasis_code=%d in Hwarp_apply() function :(",Hbasis_code) ;
9600 break ;
9601 }
9602
9603 /* global warp (Haawarp) dimensions */
9604
9605 nAx = Haawarp->nx; nAy = Haawarp->ny; nAz = Haawarp->nz; nAxy = nAx*nAy;
9606 nAx1 = nAx-1 ; nAy1 = nAy-1 ; nAz1 = nAz-1 ;
9607 nAxh = nAx-0.501f ; nAyh = nAy-0.501f ; nAzh = nAz-0.501f ;
9608
9609 sar = MRI_FLOAT_PTR(SRCIM) ; /* source image array (to be warped) */
9610
9611 STATUS("start loop") ;
9612
9613 #undef IJK /* 3D index to 1D index */
9614 #define IJK(i,j,k) ((i)+(j)*nAx+(k)*nAxy)
9615
9616 #undef XINT /* linear interpolation in array aaa[] in the x-i direction */
9617 #define XINT(aaa,j,k) wt_00*aaa[IJK(ix_00,j,k)]+wt_p1*aaa[IJK(ix_p1,j,k)]
9618
9619 AFNI_OMP_START ;
9620 #pragma omp parallel
9621 { int ii,jj,kk , qq , need_val ; /* thread local variables */
9622 float xq,yq,zq ;
9623 float fx,fy,fz , ix,jy,kz ;
9624 int ix_00,ix_p1 , jy_00,jy_p1 , kz_00,kz_p1 ;
9625 float wt_00,wt_p1 ;
9626 float f_j00_k00, f_jp1_k00, f_j00_kp1, f_jp1_kp1, f_k00, f_kp1 ;
9627 float g_j00_k00, g_jp1_k00, g_j00_kp1, g_jp1_kp1, g_k00, g_kp1 ;
9628 float h_j00_k00, h_jp1_k00, h_j00_kp1, h_jp1_kp1, h_k00, h_kp1 ;
9629 #ifdef USE_OMP
9630 int ith = omp_get_thread_num() ; /* thread index (not used here) */
9631 #else
9632 int ith = 0 ;
9633 #endif
9634
9635 #pragma ivdep /* for Intel icc compiler */
9636 #pragma omp for /* parallel-ized loop over index into patch */
9637 for( qq=0 ; qq < nbxyz ; qq++ ){ /* for each voxel in the patch */
9638 ii = qq % nbx; kk = qq / nbxy; jj = (qq-kk*nbxy) / nbx; /* patch indexes */
9639
9640 /* determine if we actually need this value (is it in the mask?) */
9641
9642 need_val = ( Hbmask[IJK(ii+Hibot,jj+Hjbot,kk+Hkbot)] != 0 ) ;
9643
9644 if( !need_val && !need_AH ){ val[qq] = 0.0f; continue; } /* that was easy */
9645
9646 /* hxd (etc.) = Hwarp = incremental patch warp we are evaluating */
9647
9648 Heval(qq,hxd+qq,hyd+qq,hzd+qq) ; /* if warp not loaded, evaluate it now */
9649
9650 /* get Hwarp-ed indexes into the global warp Haawarp; e.g.,
9651 xq = Hibot + ii + hxd[qq]
9652 because the Hwarp output index warp location is computed as
9653 Hwarp_x(x,y,z) = x + hxd
9654 and we also have to add in Hibot to get a global index for use in Haawarp */
9655
9656 /* we don't need to worry about xq < 0 (etc.),
9657 since the warp patch does not run up to the edge of the volume */
9658
9659 xq = Hibot + ii + hxd[qq] ; ix = (int)(xq) ; fx = xq - ix ;
9660 yq = Hjbot + jj + hyd[qq] ; jy = (int)(yq) ; fy = yq - jy ;
9661 zq = Hkbot + kk + hzd[qq] ; kz = (int)(zq) ; fz = zq - kz ;
9662 ix_00 = ix ; ix_p1 = ix_00+1 ; QLIP(ix_00,nAx1) ; QLIP(ix_p1,nAx1) ;
9663 jy_00 = jy ; jy_p1 = jy_00+1 ; QLIP(jy_00,nAy1) ; QLIP(jy_p1,nAy1) ;
9664 kz_00 = kz ; kz_p1 = kz_00+1 ; QLIP(kz_00,nAz1) ; QLIP(kz_p1,nAz1) ;
9665
9666 /* linearly interpolate in Haawarp (global warp we are updating)
9667 to get Haawarp displacements at this Hwarp warped location; later,
9668 we then interpolate at THESE displacments to get the warped image value */
9669
9670 wt_00 = 1.0f-fx ; wt_p1 = fx ; /* x interpolations of Axd, Ayd, Azd */
9671 f_j00_k00 = XINT(Axd,jy_00,kz_00) ; f_jp1_k00 = XINT(Axd,jy_p1,kz_00) ;
9672 f_j00_kp1 = XINT(Axd,jy_00,kz_p1) ; f_jp1_kp1 = XINT(Axd,jy_p1,kz_p1) ;
9673 g_j00_k00 = XINT(Ayd,jy_00,kz_00) ; g_jp1_k00 = XINT(Ayd,jy_p1,kz_00) ;
9674 g_j00_kp1 = XINT(Ayd,jy_00,kz_p1) ; g_jp1_kp1 = XINT(Ayd,jy_p1,kz_p1) ;
9675 h_j00_k00 = XINT(Azd,jy_00,kz_00) ; h_jp1_k00 = XINT(Azd,jy_p1,kz_00) ;
9676 h_j00_kp1 = XINT(Azd,jy_00,kz_p1) ; h_jp1_kp1 = XINT(Azd,jy_p1,kz_p1) ;
9677
9678 wt_00 = 1.0f-fy ; /* y interpolations */
9679 f_k00 = wt_00 * f_j00_k00 + fy * f_jp1_k00 ;
9680 f_kp1 = wt_00 * f_j00_kp1 + fy * f_jp1_kp1 ;
9681 g_k00 = wt_00 * g_j00_k00 + fy * g_jp1_k00 ;
9682 g_kp1 = wt_00 * g_j00_kp1 + fy * g_jp1_kp1 ;
9683 h_k00 = wt_00 * h_j00_k00 + fy * h_jp1_k00 ;
9684 h_kp1 = wt_00 * h_j00_kp1 + fy * h_jp1_kp1 ;
9685
9686 /* bxd = x-displacments for AHwarp = Awarp(Hwarp())
9687 = updated warp in this patch
9688 xq = index in srcim for output interpolation to get val
9689
9690 Awarp_x(x,y,z) = x + Axd(x,y,z) , so
9691 Awarp_x( Hwarp(x,y,z) ) = Hwarp(x,y,z) + Axd(interpolated)
9692 = Hibot + ii + hxd + Axd(interpolated)
9693 Below, bxd = hxd + Axd(interpolated) = displacment for AHwarp.
9694 Later, xq = bxd + Hibot + ii = actual location of warped point */
9695
9696 wt_00 = 1.0f-fz ; /* z interpolations */
9697 bxd[qq] = wt_00 * f_k00 + fz * f_kp1 + hxd[qq] ; /* saved into AHwarp */
9698 byd[qq] = wt_00 * g_k00 + fz * g_kp1 + hyd[qq] ; /* at this point */
9699 bzd[qq] = wt_00 * h_k00 + fz * h_kp1 + hzd[qq] ;
9700
9701 /* if not in the global mask, don't bother to interpolate val */
9702
9703 if( !need_val ){ val[qq] = 0.0f; continue; }
9704
9705 /* locations at which to interpolate to source image to get val */
9706
9707 xq = bxd[qq]+ii+Hibot ; yq = byd[qq]+jj+Hjbot ; zq = bzd[qq]+kk+Hkbot ;
9708
9709 /* interpolate source image at (xq,yq,zq) indexes in sar to get val */
9710
9711 if( xq < -0.499f ) xq = -0.499f; else if( xq > nAxh ) xq = nAxh;
9712 if( yq < -0.499f ) yq = -0.499f; else if( yq > nAyh ) yq = nAyh;
9713 if( zq < -0.499f ) zq = -0.499f; else if( zq > nAzh ) zq = nAzh;
9714
9715 if( Himeth == MRI_NN ){ /* special case of NN interp of data */
9716 ix_00 = (int)(xq+0.5f) ; /* which is real easy */
9717 jy_00 = (int)(yq+0.5f) ;
9718 kz_00 = (int)(zq+0.5f) ;
9719 val[qq] = sar[IJK(ix_00,jy_00,kz_00)] ;
9720 } else { /* normal case of linear interp */
9721 ix = floorf(xq) ; fx = xq - ix ; /* which is a little more work */
9722 jy = floorf(yq) ; fy = yq - jy ;
9723 kz = floorf(zq) ; fz = zq - kz ;
9724 ix_00 = ix ; ix_p1 = ix_00+1 ; CLIP(ix_00,nAx1) ; CLIP(ix_p1,nAx1) ;
9725 jy_00 = jy ; jy_p1 = jy_00+1 ; CLIP(jy_00,nAy1) ; CLIP(jy_p1,nAy1) ;
9726 kz_00 = kz ; kz_p1 = kz_00+1 ; CLIP(kz_00,nAz1) ; CLIP(kz_p1,nAz1) ;
9727
9728 wt_00 = 1.0f-fx ; wt_p1 = fx ; /* x interpolations */
9729 f_j00_k00 = XINT(sar,jy_00,kz_00) ; f_jp1_k00 = XINT(sar,jy_p1,kz_00) ;
9730 f_j00_kp1 = XINT(sar,jy_00,kz_p1) ; f_jp1_kp1 = XINT(sar,jy_p1,kz_p1) ;
9731 wt_00 = 1.0f-fy ; /* y interpolations */
9732 f_k00 = wt_00 * f_j00_k00 + fy * f_jp1_k00 ;
9733 f_kp1 = wt_00 * f_j00_kp1 + fy * f_jp1_kp1 ;
9734 val[qq] = (1.0f-fz) * f_k00 + fz * f_kp1 ; /* z interpolation = output */
9735 }
9736
9737 } /* end of parallelized for loop over voxels */
9738 } /* end of parallel stuff */
9739 AFNI_OMP_END ;
9740
9741 AFNI_do_nothing() ; /* fprintf(stderr,"B") ; */
9742
9743 EXRETURN ;
9744 }
9745
9746 #endif /*(Q4)*/ /*############################################################*/
9747
9748 #if 1
9749 /*============================================================================*/
9750 /** (Q5) Functions to evaluate the warp penalty and cost function **/
9751 /*============================================================================*/
9752
9753 /*----------------------------------------------------------------------------*/
9754 /** Penalty parameters **/
9755
9756 #define Hpen_fbase 0.033333 /* increased by factor of 5 [23 Sep 2013] */
9757
9758 static double Hpen_fac = Hpen_fbase ;
9759 static double Hpen_fff = Hpen_fbase ; /* increases with lev [20 Sep 2013] */
9760 static double Hpen_sum = 0.0 ;
9761 static int Hpen_num = 0 ;
9762 static int Hpen_use = 1 ;
9763 static int Hpen_old = 0 ; /* don't increase with lev */
9764
9765 #define Hpen_first_lev 3 /* first lev to use penalty [06 Dec 2018] */
9766
9767 /*----------------------------------------------------------------------------*/
9768 /* Compute the penalty as the pre-computed penalty for the part of the
9769 warp outside the current patch (Hpen_sum), plus the penalty in the current
9770 patch, as computed from AHwarp. Hpen_sum is initialized in function
9771 IW3D_improve_warp().
9772 *//*-------------------------------------------------------------------------*/
9773
HPEN_penalty(void)9774 double HPEN_penalty(void)
9775 {
9776 double hsum ;
9777 hsum = Hpen_sum + (double)IW3D_load_energy(AHwarp) ;
9778 if( hsum > 0.0 ) hsum = Hpen_fff * pow( hsum , 0.25 ) ;
9779 return hsum ;
9780 }
9781
9782 /*----------------------------------------------------------------------------*/
9783 /* Utility func: if everything is the same, we usually don't like this array */
9784
is_float_array_constant(int n,float * v)9785 static INLINE int is_float_array_constant( int n , float *v )
9786 {
9787 int ii ;
9788 for( ii=1 ; ii < n && v[ii] == v[0] ; ii++ ) ; /*nada*/
9789 return (ii==n) ;
9790 }
9791
9792 /*----------------------------------------------------------------------------*/
9793 /* Clear the internal data for the -xyzmatch option */
9794
IW3D_xyzmatch_clear(void)9795 void IW3D_xyzmatch_clear(void)
9796 {
9797 FREEIFNN(Hxyzmatch_bas[0]) ; FREEIFNN(Hxyzmatch_bas[1]) ; FREEIFNN(Hxyzmatch_bas[2]) ;
9798 FREEIFNN(Hxyzmatch_src[0]) ; FREEIFNN(Hxyzmatch_src[1]) ; FREEIFNN(Hxyzmatch_src[2]) ;
9799 FREEIFNN(Hxyzmatch_b2s[0]) ; FREEIFNN(Hxyzmatch_b2s[1]) ; FREEIFNN(Hxyzmatch_b2s[2]) ;
9800 Hxyzmatch_num = 0 ; Hxyzmatch_cost = 0.0 ;
9801 return ;
9802 }
9803
9804 /*----------------------------------------------------------------------------*/
9805 /* Convert two sets of vectors to the -xyzmatch internal data.
9806 *//*--------------------------------------------------------------------------*/
9807
IW3D_xyzmatch_internalize(THD_3dim_dataset * dset,int npt,float * xb,float * yb,float * zb,float * xs,float * ys,float * zs)9808 int IW3D_xyzmatch_internalize( THD_3dim_dataset *dset , int npt ,
9809 float *xb , float *yb , float *zb ,
9810 float *xs , float *ys , float *zs )
9811 {
9812 int ii , nn ;
9813 float ib,jb,kb , is,js,ks , nx1,ny1,nz1 ;
9814 mat44 imat ;
9815
9816 ENTRY("IW3D_xyzmatch_internalize") ;
9817
9818 IW3D_xyzmatch_clear() ;
9819
9820 if( dset == NULL || npt < 1 ||
9821 xb == NULL || yb == NULL || zb == NULL ||
9822 xs == NULL || ys == NULL || zs == NULL ) RETURN(0) ;
9823
9824 imat = MAT44_INV(dset->daxes->ijk_to_dicom) ; /* takes xyz to ijk */
9825
9826 nx1 = DSET_NX(dset)-1.0f ; ny1 = DSET_NY(dset)-1.0f ; nz1 = DSET_NZ(dset)-1.0f ;
9827
9828 Hxyzmatch_bas[0] = (float *)malloc(sizeof(float)*npt) ;
9829 Hxyzmatch_bas[1] = (float *)malloc(sizeof(float)*npt) ;
9830 Hxyzmatch_bas[2] = (float *)malloc(sizeof(float)*npt) ;
9831 Hxyzmatch_src[0] = (float *)malloc(sizeof(float)*npt) ;
9832 Hxyzmatch_src[1] = (float *)malloc(sizeof(float)*npt) ;
9833 Hxyzmatch_src[2] = (float *)malloc(sizeof(float)*npt) ;
9834 Hxyzmatch_b2s[0] = (float *)malloc(sizeof(float)*npt) ;
9835 Hxyzmatch_b2s[1] = (float *)malloc(sizeof(float)*npt) ;
9836 Hxyzmatch_b2s[2] = (float *)malloc(sizeof(float)*npt) ;
9837
9838 for( nn=ii=0 ; ii < npt ; ii++ ){
9839 MAT44_VEC(imat,xb[ii],yb[ii],zb[ii],ib,jb,kb) ; /* convert to indexes */
9840 MAT44_VEC(imat,xs[ii],ys[ii],zs[ii],is,js,ks) ;
9841 if( ib < 0.0f || ib >= nx1 ||
9842 jb < 0.0f || jb >= ny1 || kb < 0.0f || kb >= nz1 ) continue ;
9843 if( is < 0.0f || is >= nx1 ||
9844 js < 0.0f || js >= ny1 || ks < 0.0f || ks >= nz1 ) continue ;
9845
9846 Hxyzmatch_bas[0][nn] = ib; Hxyzmatch_bas[1][nn] = jb; Hxyzmatch_bas[2][nn] = kb;
9847 Hxyzmatch_src[0][nn] = is; Hxyzmatch_src[1][nn] = js; Hxyzmatch_src[2][nn] = ks;
9848 nn++ ;
9849 }
9850
9851 Hxyzmatch_num = nn ; RETURN(nn) ;
9852 }
9853
9854 /*----------------------------------------------------------------------------*/
9855 /* Warp the input xyz indexes given Haawarp and AHwarp.
9856 The input indexes will have been edited to be inside the Haawarp box,
9857 so external slopes are not needed.
9858 Note that the input points are the base image locations, which will
9859 be warped to the source image locations for comparison.
9860 The only tricky part is to use AHwarp inside its box, otherwise use Haawarp.
9861 *//*--------------------------------------------------------------------------*/
9862
IW3D_nwarp_xyzmatch(int npt,float * xin,float * yin,float * zin,float * xut,float * yut,float * zut)9863 void IW3D_nwarp_xyzmatch( int npt ,
9864 float *xin , float *yin , float *zin ,
9865 float *xut , float *yut , float *zut )
9866 {
9867 int nx,ny,nz,nxy , nbx,nby ;
9868 float *xdar , *ydar , *zdar ;
9869 float *xqar , *yqar , *zqar ;
9870 float *xxar[2], *yyar[2] , *zzar[2] ;
9871
9872 nx = Hnx ; ny = Hny ; nz = Hnz ; nxy = Hnxy ;
9873 xdar = Haawarp->xd ; ydar = Haawarp->yd ; zdar = Haawarp->zd ;
9874
9875 nbx = AHwarp->nx ; nby = AHwarp->ny ;
9876 xqar = AHwarp->xd ; yqar = AHwarp->yd ; zqar = AHwarp->zd ;
9877
9878 /* pointers to displacement arrays for case 0 = outside AHwarp box */
9879
9880 xxar[0] = xdar ; yyar[0] = ydar ; zzar[0] = zdar ;
9881
9882 /* for case 1 = inside AHwarp box (cf. AHIND macro below);
9883 use pointer arithmetic fu to make them seem based at (i,j,k)=(0,0,0)
9884 [Note: could easily fail badly if on a segmented memory architecture] */
9885
9886 xxar[1] = xqar - (Hibot + Hjbot*nbx + Hkbot*nbx*nby) ;
9887 yyar[1] = yqar - (Hibot + Hjbot*nbx + Hkbot*nbx*nby) ;
9888 zzar[1] = zqar - (Hibot + Hjbot*nbx + Hkbot*nbx*nby) ;
9889
9890 /* parallel-ized linear interpolation [parallelizing probably useless] */
9891
9892 AFNI_OMP_START ;
9893 #pragma omp parallel if( npt > 111 )
9894 { int pp ;
9895 float xx,yy,zz , fx,fy,fz ; int ix,jy,kz ;
9896 int ix_00,ix_p1 , jy_00,jy_p1 , kz_00,kz_p1 ;
9897 int dc_00_00_00 , dc_p1_00_00 , dc_00_p1_00 , dc_p1_p1_00 ,
9898 dc_00_00_p1 , dc_p1_00_p1 , dc_00_p1_p1 , dc_p1_p1_p1 ;
9899 float wt_00,wt_p1 ;
9900 float f_j00_k00, f_jp1_k00, f_j00_kp1, f_jp1_kp1, f_k00, f_kp1 ;
9901 float g_j00_k00, g_jp1_k00, g_j00_kp1, g_jp1_kp1, g_k00, g_kp1 ;
9902 float h_j00_k00, h_jp1_k00, h_j00_kp1, h_jp1_kp1, h_k00, h_kp1 ;
9903
9904 #pragma omp for
9905 for( pp=0 ; pp < npt ; pp++ ){ /* loop over input points */
9906 xx = xin[pp] ; yy = yin[pp] ; zz = zin[pp] ;
9907 ix = (int)xx; fx = xx-ix; jy = (int)yy; fy = yy-jy; kz = (int)zz; fz = zz-kz;
9908 /* now linearly interpolate displacements inside the dataset grid */
9909 ix_00 = ix ; ix_p1 = ix_00+1 ; /* at this point, we are 'fx' between indexes ix_00 and ix_p1 */
9910 jy_00 = jy ; jy_p1 = jy_00+1 ; /* et cetera */
9911 kz_00 = kz ; kz_p1 = kz_00+1 ;
9912 wt_00 = (1.0f-fx) ; wt_p1 = fx ; /* weights for ix_00 and ix_p1 points for linear interp */
9913
9914 #undef AHIND /* == 0 for outside of AHwarp, == 1 for inside AHwarp */
9915 #define AHIND(i,j,k) ( (i >= Hibot) && (i <= Hitop) && \
9916 (j >= Hjbot) && (j <= Hjtop) && \
9917 (k >= Hkbot) && (k <= Hktop) )
9918
9919 dc_00_00_00 = AHIND(ix_00,jy_00,kz_00) ; /* select case 0 or 1 */
9920 dc_p1_00_00 = AHIND(ix_p1,jy_00,kz_00) ; /* for each of the 8 */
9921 dc_00_p1_00 = AHIND(ix_00,jy_p1,kz_00) ; /* points used for */
9922 dc_p1_p1_00 = AHIND(ix_p1,jy_p1,kz_00) ; /* interpolation */
9923 dc_00_00_p1 = AHIND(ix_00,jy_00,kz_p1) ; /* ['dc' == displacement case] */
9924 dc_p1_00_p1 = AHIND(ix_p1,jy_00,kz_p1) ;
9925 dc_00_p1_p1 = AHIND(ix_00,jy_p1,kz_p1) ;
9926 dc_p1_p1_p1 = AHIND(ix_p1,jy_p1,kz_p1) ;
9927
9928 #undef IJK /* convert 3D index to 1D index */
9929 #define IJK(i,j,k) ((i)+(j)*nx+(k)*nxy)
9930
9931 #undef XINT /* linear interpolation at plane jk, for cases dca,dcb */
9932 #define XINT(aaa,j,k,dca,dcb) wt_00*aaa[dca][IJK(ix_00,j,k)]+wt_p1*aaa[dcb][IJK(ix_p1,j,k)]
9933
9934 /* interpolate to location ix+fx at each jy,kz level */
9935
9936 f_j00_k00 = XINT(xxar,jy_00,kz_00,dc_00_00_00,dc_p1_00_00) ;
9937 f_jp1_k00 = XINT(xxar,jy_p1,kz_00,dc_00_p1_00,dc_p1_p1_00) ;
9938 f_j00_kp1 = XINT(xxar,jy_00,kz_p1,dc_00_00_p1,dc_p1_00_p1) ;
9939 f_jp1_kp1 = XINT(xxar,jy_p1,kz_p1,dc_00_p1_p1,dc_p1_p1_p1) ;
9940 g_j00_k00 = XINT(yyar,jy_00,kz_00,dc_00_00_00,dc_p1_00_00) ;
9941 g_jp1_k00 = XINT(yyar,jy_p1,kz_00,dc_00_p1_00,dc_p1_p1_00) ;
9942 g_j00_kp1 = XINT(yyar,jy_00,kz_p1,dc_00_00_p1,dc_p1_00_p1) ;
9943 g_jp1_kp1 = XINT(yyar,jy_p1,kz_p1,dc_00_p1_p1,dc_p1_p1_p1) ;
9944 h_j00_k00 = XINT(zzar,jy_00,kz_00,dc_00_00_00,dc_p1_00_00) ;
9945 h_jp1_k00 = XINT(zzar,jy_p1,kz_00,dc_00_p1_00,dc_p1_p1_00) ;
9946 h_j00_kp1 = XINT(zzar,jy_00,kz_p1,dc_00_00_p1,dc_p1_00_p1) ;
9947 h_jp1_kp1 = XINT(zzar,jy_p1,kz_p1,dc_00_p1_p1,dc_p1_p1_p1) ;
9948
9949 /* interpolate to jy+fy at each kz level */
9950
9951 wt_00 = 1.0f-fy ; wt_p1 = fy ;
9952 f_k00 = wt_00 * f_j00_k00 + wt_p1 * f_jp1_k00 ;
9953 f_kp1 = wt_00 * f_j00_kp1 + wt_p1 * f_jp1_kp1 ;
9954 g_k00 = wt_00 * g_j00_k00 + wt_p1 * g_jp1_k00 ;
9955 g_kp1 = wt_00 * g_j00_kp1 + wt_p1 * g_jp1_kp1 ;
9956 h_k00 = wt_00 * h_j00_k00 + wt_p1 * h_jp1_k00 ;
9957 h_kp1 = wt_00 * h_j00_kp1 + wt_p1 * h_jp1_kp1 ;
9958
9959 /* interpolate to kz+fz to get output, plus add in original coords */
9960
9961 xut[pp] = (1.0f-fz) * f_k00 + fz * f_kp1 + xin[pp] ;
9962 yut[pp] = (1.0f-fz) * g_k00 + fz * g_kp1 + yin[pp] ;
9963 zut[pp] = (1.0f-fz) * h_k00 + fz * h_kp1 + zin[pp] ;
9964
9965 } /* end of loop over input/output points */
9966 } /* end of parallel code */
9967 AFNI_OMP_END ;
9968
9969 return ;
9970 }
9971
9972 /*----------------------------------------------------------------------------*/
9973 /* Part of the penalty computed from the -xyzmatch option in 3dQwarp:
9974 a sum over distances between discrete points.
9975 *//*--------------------------------------------------------------------------*/
9976
IW3D_xyzmatch_sum(void)9977 double IW3D_xyzmatch_sum(void)
9978 {
9979 int ii ; double sum = 0.0 ;
9980
9981 IW3D_nwarp_xyzmatch( Hxyzmatch_num ,
9982 Hxyzmatch_bas[0], Hxyzmatch_bas[1], Hxyzmatch_bas[2],
9983 Hxyzmatch_b2s[0], Hxyzmatch_b2s[1], Hxyzmatch_b2s[2] ) ;
9984
9985 switch ( Hxyzmatch_pow ){
9986
9987 case 2: /* L2 sum = RMS distance */
9988 default:
9989 for( ii=0 ; ii < Hxyzmatch_num ; ii++ ){
9990 sum += SQR( Hxyzmatch_b2s[0][ii]-Hxyzmatch_src[0][ii] )
9991 + SQR( Hxyzmatch_b2s[1][ii]-Hxyzmatch_src[1][ii] )
9992 + SQR( Hxyzmatch_b2s[2][ii]-Hxyzmatch_src[2][ii] ) ;
9993 }
9994 sum = sqrt(sum/Hxyzmatch_num) ;
9995 break ;
9996
9997 case 1: /* L1 sum = average taxicab distance */
9998 for( ii=0 ; ii < Hxyzmatch_num ; ii++ ){
9999 sum += fabs( Hxyzmatch_b2s[0][ii]-Hxyzmatch_src[0][ii] )
10000 + fabs( Hxyzmatch_b2s[1][ii]-Hxyzmatch_src[1][ii] )
10001 + fabs( Hxyzmatch_b2s[2][ii]-Hxyzmatch_src[2][ii] ) ;
10002 }
10003 sum /= Hxyzmatch_num ;
10004 break ;
10005 }
10006
10007 return (sum * Hxyzmatch_fac) ;
10008 }
10009
10010 /*----------------------------------------------------------------------------*/
10011 /* This is the function which will actually be minimized
10012 (via Powell's NEWUOA); and as such is the very core of 3dQwarp!
10013 *//*--------------------------------------------------------------------------*/
10014
IW3D_scalar_costfun(int npar,double * dpar)10015 double IW3D_scalar_costfun( int npar , double *dpar )
10016 {
10017 double cost=0.0 ; int ii ;
10018
10019 /* Step 1: setup compute/load Hwarp given the param vector dpar */
10020
10021 if( Hparmap != NULL ){ /* expand via the parameter map to a full list */
10022 /* since all the warp eval functions need all */
10023 /* the parameters, used or not */
10024
10025 for( ii=0 ; ii < Hnpar ; ii++ ) Hpar[ii] = 0.0f ;
10026 for( ii=0 ; ii < npar ; ii++ ) Hpar[ Hparmap[ii] ] = (float)dpar[ii] ;
10027
10028 } else { /* we received a full list of parameters */
10029
10030 for( ii=0 ; ii < Hnpar ; ii++ ){
10031 Hpar[ii] = (float)dpar[ii] ;
10032 if( !isfinite(Hpar[ii]) ){
10033 ERROR_message("bad Hpar[%d]=%g dpar=%g",ii,Hpar[ii],dpar[ii]) ;
10034 Hpar[ii] = dpar[ii] = 0.0 ;
10035 }
10036 }
10037
10038 }
10039
10040 /* Step 2: compute warped image over the patch, into Hwval array */
10041
10042 Hwarp_apply(Hwval) ;
10043
10044 #if 0
10045 if( is_float_array_constant(Hnval,Hwval) )
10046 fprintf(stderr," costfun: Hwval is constant %g\n",Hwval[0]) ;
10047 #endif
10048
10049 /* Step 3: compute the actual cost function */
10050 /* -- the first case in the '?' expressions is when the patch
10051 is smaller than the whole volume;
10052 -- the second case is when the patch covers the entire universe */
10053
10054 cost = INCOR_evaluate( Hincor , Hnval ,
10055 (Hbval != NULL ) ? Hbval /* base image in patch */
10056 : MRI_FLOAT_PTR(BASIM),
10057 Hwval , /* warped source image in patch */
10058 (Haawt != NULL ) ? Haawt /* weight image in patch */
10059 : MRI_FLOAT_PTR(Hwtim) ) ;
10060
10061 if( Hnegate ) cost = -cost ; /* change the sign? (for minimization) */
10062
10063 if( !isfinite(cost) ){ /* bad bad Leroy Brown */
10064 ERROR_message("bad Warpomatic cost = %g -- input parameters:",cost) ;
10065 for( ii=0 ; ii < npar ; ii++ ) fprintf(stderr," %g",dpar[ii]) ;
10066 fprintf(stderr,"\n") ;
10067 }
10068
10069 Hcostt = cost ; /* store 'pure' cost globally for reporting purposes */
10070 if( save_H_zero ){ Hcostt_zero = cost ; save_H_zero = 0 ; } /* 10 Mar 2020 */
10071
10072 /* Step 4: add the penalty function into the output cost */
10073
10074 if( Hpen_use ){
10075 Hpenn = HPEN_penalty() ; cost += Hpenn ; /* penalty is saved in Hpenn */
10076 } else {
10077 Hpenn = 0.0f ;
10078 }
10079
10080 /* point matching penalty [not used yet] */
10081
10082 if( Hxyzmatch_num > 0 ){
10083 Hxyzmatch_cost = IW3D_xyzmatch_sum() ; cost += Hxyzmatch_cost ;
10084 }
10085
10086 if( Hfirsttime ){ /* just for fun fun fun in the sun sun sun */
10087 if( Hverb ) fprintf(stderr,"[first cost=%.5f]%c",cost , ((Hverb>1) ? '\n' : ' ') ) ;
10088 Hfirsttime = 0 ; Hfirstcost = (float)cost ;
10089 }
10090
10091 return cost ;
10092 }
10093
10094 #endif /*(Q5)*/ /*############################################################*/
10095
10096 #if 1
10097 /*============================================================================*/
10098 /** (Q6) Functions to setup global variables for warp optimization process **/
10099 /*============================================================================*/
10100
10101 /*----------------------------------------------------------------------------*/
10102 /* Delete various workspaces created for warp improvement [inverse of setup] */
10103
IW3D_cleanup_improvement(void)10104 void IW3D_cleanup_improvement(void)
10105 {
10106 ENTRY("IW3D_cleanup_improvement") ;
10107
10108 mri_free(Hbasim) ; Hbasim = NULL ;
10109 mri_free(Hsrcim) ; Hsrcim = NULL ;
10110 mri_free(Hwtim) ; Hwtim = NULL ; FREEIFNN(Hbmask) ;
10111 mri_free(Haasrcim) ; Haasrcim = NULL ;
10112
10113 mri_free(Hsrcim_blur) ; Hsrcim_blur = NULL ;
10114 mri_free(Hbasim_blur) ; Hbasim_blur = NULL ;
10115
10116 IW3D_destroy(Hwarp) ; Hwarp = NULL ;
10117 IW3D_destroy(AHwarp) ; AHwarp = NULL ;
10118 IW3D_destroy(Haawarp) ; Haawarp = NULL ;
10119
10120 IW3D_xyzmatch_clear() ; /* 15 Aug 2014 */
10121
10122 INCOR_set_lpc_mask(NULL) ; /* 25 Jun 2014 */
10123 INCOR_destroy(Hincor) ; Hincor = NULL ; KILL_floatvec(Hmpar) ;
10124 FREEIFNN(Hpar) ; FREEIFNN(Hwval) ; FREEIFNN(Haawt) ; FREEIFNN(Hbval) ;
10125 FREEIFNN(Hparmap) ; Hnparmap = Hnpar = 0 ; Hbasis_code = -666 ;
10126
10127 FREEIFNN(bc0x); FREEIFNN(bc1x); nbcx=0;
10128 FREEIFNN(bc0y); FREEIFNN(bc1y); nbcy=0;
10129 FREEIFNN(bc0z); FREEIFNN(bc1z); nbcz=0;
10130 FREEIFNN(bc2x); FREEIFNN(bc3x); FREEIFNN(bc4x);
10131 FREEIFNN(bc2y); FREEIFNN(bc3y); FREEIFNN(bc4y);
10132 FREEIFNN(bc2z); FREEIFNN(bc3z); FREEIFNN(bc4z);
10133
10134 FREEIFNN(bq0x); FREEIFNN(bq1x); FREEIFNN(bq2x); nbqx=0;
10135 FREEIFNN(bq0y); FREEIFNN(bq1y); FREEIFNN(bq2y); nbqy=0;
10136 FREEIFNN(bq0z); FREEIFNN(bq1z); FREEIFNN(bq2z); nbqz=0;
10137
10138 if( bbbcar != NULL ){
10139 int ii ;
10140 for( ii=0 ; ii < nbbbcar ; ii++ ) FREEIFNN(bbbcar[ii]) ;
10141 free(bbbcar) ; nbbcxyz = nbbbcar = 0 ; bbbcar = NULL ;
10142 }
10143
10144 if( bbbqar != NULL ){
10145 int ii ;
10146 for( ii=0 ; ii < 27 ; ii++ ) FREEIFNN(bbbqar[ii]) ;
10147 free(bbbqar) ; nbbqxyz = 0 ; bbbqar = NULL ;
10148 }
10149
10150 Hstopcost = -666666.6f ;
10151 Hstopped = 0 ;
10152 Hfinal = 0 ;
10153
10154 HSAVE_DESTROY ;
10155
10156 EXRETURN ;
10157 }
10158
10159 /*----------------------------------------------------------------------------*/
10160 /* Median filter specialized for 3dQwarp and for OpenMP. */
10161
IW3D_medianfilter(MRI_IMAGE * imin,float irad)10162 MRI_IMAGE * IW3D_medianfilter( MRI_IMAGE *imin, float irad )
10163 {
10164 MRI_IMAGE *imout ;
10165 float *fin, *fout , dz ;
10166 short *di , *dj , *dk ;
10167 int nd, nx,ny,nz,nxy,nxyz ;
10168 MCW_cluster *cl ;
10169
10170 ENTRY("IW3D_medianfilter") ;
10171
10172 if( imin == NULL || imin->kind != MRI_float ) RETURN(NULL) ;
10173
10174 if( irad < 1.01f ) irad = 1.01f ;
10175 dz = (imin->nz == 1) ? 6666.0f : 1.0f ;
10176 cl = MCW_build_mask( 1.0f,1.0f,dz , irad ) ;
10177
10178 if( cl == NULL || cl->num_pt < 6 ){ KILL_CLUSTER(cl); RETURN(NULL); }
10179
10180 ADDTO_CLUSTER(cl,0,0,0,0) ;
10181
10182 di = cl->i ; dj = cl->j ; dk = cl->k ; nd = cl->num_pt;
10183 nx = imin->nx; ny = imin->ny; nz = imin->nz; nxy = nx*ny ; nxyz = nxy*nz;
10184
10185 imout = mri_new_conforming( imin , MRI_float ) ;
10186 fout = MRI_FLOAT_PTR( imout ) ;
10187 fin = MRI_FLOAT_PTR( imin ) ;
10188
10189 AFNI_OMP_START ;
10190 #pragma omp parallel if( nxyz > 6666 )
10191 { int ii,jj,kk,ijk , ip,jp,kp , nt,dd ; float *tmp ;
10192 #pragma omp critical
10193 { tmp = (float *)malloc(sizeof(float)*nd) ; }
10194 #pragma omp for
10195 for( ijk=0 ; ijk < nxyz ; ijk++ ){
10196 ii = ijk % nx ; kk = ijk / nxy ; jj = (ijk-kk*nxy) / nx ;
10197 for( nt=dd=0 ; dd < nd ; dd++ ){ /* extract neighborhood values */
10198 ip = ii+di[dd] ; if( ip < 0 || ip >= nx ) continue ;
10199 jp = jj+dj[dd] ; if( jp < 0 || jp >= ny ) continue ;
10200 kp = kk+dk[dd] ; if( kp < 0 || kp >= nz ) continue ;
10201 tmp[nt++] = fin[ip+jp*nx+kp*nxy] ;
10202 }
10203 if( nt > 0 ) fout[ijk] = qmed_float( nt , tmp ) ; /* cf. cs_qmed.c */
10204 }
10205 #pragma omp critical
10206 { free(tmp) ; }
10207 }
10208 AFNI_OMP_END ;
10209
10210 KILL_CLUSTER(cl);
10211 RETURN(imout) ;
10212 }
10213
10214 /*----------------------------------------------------------------------------*/
10215 /* Function for blurring a volume in 1 of 2 ways */
10216
IW3D_blurim(float rad,MRI_IMAGE * inim,char * label)10217 MRI_IMAGE * IW3D_blurim( float rad , MRI_IMAGE *inim , char *label )
10218 {
10219 MRI_IMAGE *outim = NULL ;
10220 ENTRY("IW3D_blurim") ;
10221 if( rad >= 0.5f ){
10222 if( Hverb > 1 && label != NULL )
10223 ININFO_message(" blurring %s image %.2f voxels FWHM",label,rad) ;
10224 outim = mri_float_blur3D( FWHM_TO_SIGMA(rad) , inim ) ;
10225 } else if( rad <= -1.0f ){
10226 if( Hverb > 1 && label != NULL )
10227 ININFO_message(" median-ating %s image %.2f voxels",label,-rad) ;
10228 outim = IW3D_medianfilter( inim , -rad ) ;
10229 }
10230 RETURN(outim) ;
10231 }
10232
10233 /*----------------------------------------------------------------------------*/
10234 /* Macro for actual pblur radius, given image dimensions (in voxels) */
10235
10236 #define PBLUR(pb,qx,qy,qz) \
10237 ( ((pb) <= 0.0f) \
10238 ? 0.0f \
10239 : ( ( ((qz) >= NGMIN) ? cbrtf((qx)*(qy)*(qz)) : sqrtf((qx)*(qy)) ) * (pb) ) )
10240
10241 /* Macro to compute actual blur radius from the
10242 progressive blur (pb) and the fixed blur (fb) radii */
10243
10244 #define ACTUAL_BLUR(pb,fb) \
10245 ( sqrtf( (pb)*(pb) + (fb)*(fb) ) * ( ((fb) >= 0.0f) ? 1.0f : -1.0f ) )
10246
10247 /*----------------------------------------------------------------------------*/
10248 /* function to do the blurring */
10249
IW3D_do_blurring(float fixed_blur,float prog_blur,float scalex,float scaley,float scalez,MRI_IMAGE * inim,char * lab)10250 MRI_IMAGE * IW3D_do_blurring( float fixed_blur, float prog_blur,
10251 float scalex, float scaley, float scalez,
10252 MRI_IMAGE *inim , char *lab )
10253 {
10254 float pblur,ablur ; MRI_IMAGE *outim ;
10255
10256 ENTRY("IW3D_do_blurring") ;
10257 if( fixed_blur == 0.0f && prog_blur == 0.0f ) RETURN(NULL) ;
10258 pblur = PBLUR(prog_blur,scalex,scaley,scalez) ; /* calculate */
10259 ablur = ACTUAL_BLUR(pblur,fixed_blur) ; /* blur to use */
10260 outim = IW3D_blurim( ablur , inim , lab ) ;
10261 RETURN(outim) ;
10262 }
10263
10264 /*------------------- Macros for blurring images as needed -------------------*/
10265
10266 #define PBLUR_BASE(i1,i2,j1,j2,k1,k2) \
10267 do{ if( Hpblur_b > 0.0f ){ \
10268 mri_free(Hbasim_blur) ; \
10269 Hbasim_blur = IW3D_do_blurring( Hblur_b, Hpblur_b, \
10270 i2-i1+1, j2-j1+1, k2-k1+1, Hbasim, "base" ) ; \
10271 } } while(0)
10272
10273 #define PBLUR_SOURCE(i1,i2,j1,j2,k1,k2) \
10274 do{ if( Hpblur_s > 0.0f ){ \
10275 mri_free(Hsrcim_blur) ; \
10276 Hsrcim_blur = IW3D_do_blurring( Hblur_s, Hpblur_s, \
10277 i2-i1+1, j2-j1+1, k2-k1+1, Hsrcim, "source" ) ; \
10278 } } while(0)
10279
10280 /*----------------------------------------------------------------------------*/
10281 /* Sets a bunch of global workspace variables, prior to
10282 iteratively improving the warp with function IW3D_improve_warp() */
10283
IW3D_setup_for_improvement(MRI_IMAGE * bim,MRI_IMAGE * wbim,MRI_IMAGE * sim,IndexWarp3D * Iwarp,int meth_code,int warp_flags)10284 void IW3D_setup_for_improvement( MRI_IMAGE *bim, MRI_IMAGE *wbim, MRI_IMAGE *sim,
10285 IndexWarp3D *Iwarp,
10286 int meth_code, int warp_flags )
10287 {
10288 int iii , nmask ;
10289
10290 ENTRY("IW3D_setup_for_improvement") ;
10291
10292 /*-- check for errorosities --*/
10293
10294 if( bim == NULL )
10295 ERROR_exit("IW3D_setup_for_improvement: bad base (bim) input") ;
10296
10297 if( sim == NULL )
10298 ERROR_exit("IW3D_setup_for_improvement: bad source (sim) input") ;
10299
10300 if( sim->nx != bim->nx || sim->ny != bim->ny || sim->nz != bim->nz )
10301 ERROR_exit("IW3D_setup_for_improvement: base (bim) and source (sim) grids don't match") ;
10302
10303 /*-- eliminate old stuff (if any) --*/
10304
10305 IW3D_cleanup_improvement() ;
10306
10307 /*-- copy base and source images --*/
10308
10309 Hnx = bim->nx; Hny = bim->ny; Hnz = bim->nz; Hnxy=Hnx*Hny; Hnxyz = Hnxy*Hnz;
10310 Hbasim = mri_to_float(bim) ;
10311 Hsrcim = mri_to_float(sim);
10312
10313 #ifdef ALLOW_INEDGE /* Jul 2018 */
10314 if( Hinedge_doit ){
10315 int be,ce ;
10316 if( Hverb > 1 ) ININFO_message(" enhancing interior edges of base and source") ;
10317 be = mri_interior_edgeize( Hbasim , Hinedge_erode , Hinedge_frac ) ;
10318 ce = mri_interior_edgeize( Hsrcim , Hinedge_erode , Hinedge_frac ) ;
10319 if( Hverb > 1 ) ININFO_message(" enhancement counts: base = %d source = %d",be,ce) ;
10320 }
10321 #endif
10322
10323 /* blur the base and source images, as ordered */
10324
10325 if( Hpblur_b > 0.0f && Hblur_b == 0.0f ) Hblur_b = 0.1f ; /* if doing progressive blur, */
10326 if( Hpblur_s > 0.0f && Hblur_s == 0.0f ) Hblur_s = 0.1f ; /* constant blurs mustn't be 0 */
10327
10328 Hbasim_blur = IW3D_do_blurring( Hblur_b , Hpblur_b ,
10329 0.5f*Hnx,0.5f*Hny,0.5f*Hnz, Hbasim, "base" ) ;
10330 Hsrcim_blur = IW3D_do_blurring( Hblur_s , Hpblur_s ,
10331 0.5f*Hnx,0.5f*Hny,0.5f*Hnz, Hsrcim, "source" ) ;
10332
10333 /*-- copy or create base weight image (included Hemask for exclusion) --*/
10334
10335 if( wbim != NULL ){ /*-- user supplied weight --*/
10336
10337 int ii,nwb,nexc ; float *wbfar ;
10338 if( wbim->kind != MRI_float ||
10339 wbim->nx != Hnx || wbim->ny != Hny || wbim->nz != Hnz )
10340 ERROR_exit("IW3D_setup_for_improvement: bad wbim input") ;
10341
10342 Hwtim = mri_to_float(wbim) ; wbfar = MRI_FLOAT_PTR(Hwtim) ;
10343 Hbmask = (byte *)malloc(sizeof(byte)*Hnxyz) ;
10344 for( Hwbar=nwb=nexc=ii=0 ; ii < Hnxyz ; ii++ ){
10345 if( Hemask != NULL && Hemask[ii] && wbfar[ii] > 0.0f ){ /* 29 Oct 2012 */
10346 nexc++ ; wbfar[ii] = 0.0f ;
10347 }
10348 Hbmask[ii] = (wbfar[ii] > 0.0f) ;
10349 if( Hbmask[ii] ){ Hwbar += wbfar[ii] ; nwb++ ; }
10350 else { wbfar[ii] = 0.0f ; }
10351 }
10352 if( Hwbar == 0.0f || nwb == 0 )
10353 ERROR_exit("IW3D_setup_for_improvement: all zero wbim input") ;
10354 if( Hverb > 1 ) ININFO_message(" %d voxels in mask (out of %d = %.2f%%)",
10355 nwb,Hnxyz,(100.0*nwb)/Hnxyz ) ;
10356 Hwbar /= nwb ; /* average value of all nonzero weights */
10357 nmask = nwb ;
10358 if( nexc > 0 ) ININFO_message("-emask excluded %d voxels",nexc) ;
10359
10360 } else { /*-- make weight up from nowhere --*/
10361
10362 int ii,nwb,nexc ; float *wbfar ;
10363 Hwtim = mri_new_vol(Hnx,Hny,Hnz,MRI_float); wbfar = MRI_FLOAT_PTR(Hwtim);
10364 Hbmask = (byte *)malloc(sizeof(byte)*Hnxyz) ;
10365 for( Hwbar=nwb=nexc=ii=0 ; ii < Hnxyz ; ii++ ){
10366 if( Hemask != NULL && Hemask[ii] ){ wbfar[ii] = 0.0f; Hbmask[ii] = 0; nexc++; }
10367 else { wbfar[ii] = 1.0f; Hbmask[ii] = 1; }
10368 if( Hbmask[ii] ){ Hwbar += wbfar[ii] ; nwb++ ; }
10369 }
10370 if( Hwbar == 0.0f || nwb == 0 )
10371 ERROR_exit("IW3D_setup_for_improvement: all zero mask!?") ;
10372 Hwbar /= nwb ; /* average value of all nonzero weights */
10373 nmask = nwb ;
10374 if( nexc > 0 ) ININFO_message("-emask excluded %d voxels",nexc) ;
10375
10376 }
10377
10378 /*-- setup codes and other stuff for the 'correlation' (INCOR_) function --*/
10379
10380 Hmatch_code = meth_code ;
10381 iii = INCOR_check_meth_code(meth_code) ; /* iii tells what to setup */
10382 if( iii == 0 )
10383 ERROR_exit("IW3D_setup_for_improvement: bad meth_code input=%d",meth_code) ;
10384
10385 /* decide if the cost function needs to be negated before minimization */
10386
10387 switch( meth_code ){
10388 default: Hnegate = 0 ; break ;
10389
10390 case GA_MATCH_PEARSON_LOCALA: /* lpa (but NOT lpc) */
10391 case GA_MATCH_HELLINGER_SCALAR: /* hel */
10392 case GA_MATCH_CRAT_USYM_SCALAR: /* correlation ratio */
10393 case GA_MATCH_CRAT_SADD_SCALAR:
10394 case GA_MATCH_CORRATIO_SCALAR:
10395 case GA_MATCH_KULLBACK_SCALAR: /* mutual info */
10396 case GA_MATCH_PEARCLP_SCALAR: /* clipped Pearson */
10397 case GA_MATCH_PEARSON_SCALAR: /* pure Pearson */
10398 Hnegate = 1 ; break ;
10399 }
10400
10401 /* special case: don't try to over-minimize the Pearson costs
10402 (e.g., the base and source images are essentially identical) */
10403
10404 if( meth_code == GA_MATCH_PEARCLP_SCALAR || meth_code == GA_MATCH_PEARSON_SCALAR )
10405 Hstopcost = -3.991f ;
10406
10407 /*-- INCOR method uses 2Dhist functions (iii==2),
10408 or is the clipped Pearson method (iii==3),
10409 or is a local Pearson method (iii==4),
10410 so setup some parameters (into Hmpar) for later use --*/
10411
10412 if( iii == 2 || iii == 3 ){
10413 float *xar,*yar , *bar,*sar ; int jj,kk ;
10414 float_quad xyc , xym ;
10415 bar = MRI_FLOAT_PTR(BASIM) ; sar = MRI_FLOAT_PTR(SRCIM) ;
10416 if( nmask == Hnxyz ){ /* entire volume is in mask! */
10417 xar = bar ; yar = sar ; kk = Hnxyz ;
10418 } else { /* copy data in mask to temp arrays */
10419 xar = (float *)malloc(sizeof(float)*nmask) ;
10420 yar = (float *)malloc(sizeof(float)*nmask) ;
10421 for( jj=kk=0 ; jj < Hnxyz ; jj++ ){
10422 if( Hbmask[jj] ){ xar[kk] = bar[jj] ; yar[kk++] = sar[jj] ; }
10423 }
10424 }
10425 xym = INCOR_2Dhist_minmax( kk , xar , yar ) ;
10426 xyc = INCOR_2Dhist_xyclip( kk , xar , yar ) ;
10427 if( xar != bar ){ free(xar) ; free(yar) ; } /* toss temp arrays */
10428 MAKE_floatvec(Hmpar,9) ;
10429 if( iii == 2 ){ /* histogram parameter setup */
10430 INCOR_setup_good(Hnxyz) ;
10431 Hmpar->ar[0] = (float)INCOR_2Dhist_compute_nbin(nmask) ;
10432 Hmpar->ar[1] = xym.a ; Hmpar->ar[2] = xym.b ; /* xbot xtop */
10433 Hmpar->ar[3] = xym.c ; Hmpar->ar[4] = xym.d ; /* ybot ytop */
10434 Hmpar->ar[5] = xyc.a ; Hmpar->ar[6] = xyc.b ; /* xcbot xctop */
10435 Hmpar->ar[7] = xyc.c ; Hmpar->ar[8] = xyc.d ; /* ycbot yctop */
10436 if( Hverb > 1 ){
10437 ININFO_message(" 2Dhist: nbin=%d",(int)Hmpar->ar[0]) ;
10438 ININFO_message(" xbot=%g xcbot=%g xctop=%g xtop=%g",
10439 Hmpar->ar[1], Hmpar->ar[5], Hmpar->ar[6], Hmpar->ar[2] ) ;
10440 ININFO_message(" ybot=%g ycbot=%g yctop=%g ytop=%g",
10441 Hmpar->ar[3], Hmpar->ar[7], Hmpar->ar[8], Hmpar->ar[4] ) ;
10442 }
10443 } else if( iii == 3 ){ /* clipped Pearson setup */
10444 float d1 , d2 , dif ;
10445 d2 = 0.05f*(xyc.b-xyc.a) ; /* 5% of x clip range */
10446 d1 = 0.5f*(xyc.a-xym.a) ; /* half of x clip bot to x min */
10447 dif = MIN(d1,d2) ; Hmpar->ar[1] = xyc.a-dif ; /* xdbot */
10448 d1 = 0.5f*(xym.b-xyc.b) ; dif = MIN(d1,d2) ; Hmpar->ar[2] = xyc.b+dif ; /* xdtop */
10449 d2 = 0.05f*(xyc.d-xyc.c) ;
10450 d1 = 0.5f*(xyc.c-xym.c) ; dif = MIN(d1,d2) ; Hmpar->ar[3] = xyc.c-dif ; /* ydbot */
10451 d1 = 0.5f*(xym.d-xyc.d) ; dif = MIN(d1,d2) ; Hmpar->ar[4] = xyc.d+dif ; /* ydtop */
10452 Hmpar->ar[5] = xyc.a ; Hmpar->ar[6] = xyc.b ; /* xcbot xctop */
10453 Hmpar->ar[7] = xyc.c ; Hmpar->ar[8] = xyc.d ; /* ycbot yctop */
10454 #if 0
10455 if( Hverb ){
10456 ININFO_message(" PEARCLP: xdbot=%g xcbot=%g xctop=%g xdtop=%g",
10457 Hmpar->ar[1], Hmpar->ar[5], Hmpar->ar[6], Hmpar->ar[2] ) ;
10458 ININFO_message(" ydbot=%g ycbot=%g yctop=%g ydtop=%g",
10459 Hmpar->ar[3], Hmpar->ar[7], Hmpar->ar[8], Hmpar->ar[4] ) ;
10460 }
10461 #endif
10462 }
10463 } else if( iii == 4 ){ /*--- Local Pearson setup [25 Jun 2014] ---*/
10464 INCOR_set_lpc_mask(Hbmask) ;
10465 MAKE_floatvec(Hmpar,9) ; /* to be filled in later, for each patch */
10466 }
10467
10468 /*-- set global flags for displacements (what parameters are allowed to move) --*/
10469
10470 Hgflags = IW3D_munge_flags(Hnx,Hny,Hnz,warp_flags) ;
10471 if( Hgflags < 0 )
10472 ERROR_exit("IW3D_setup_for_improvement: bad warp_flags input") ;
10473
10474 /*-- copy/create initial warp, and warp the source image that way --*/
10475 /*** [10 Aug 2014 -- Haasrcim will be created later] ***/
10476
10477 if( Iwarp != NULL ){ /* have initial warp */
10478 if( Iwarp->nx != Hnx || Iwarp->ny != Hny || Iwarp->nz != Hnz )
10479 ERROR_exit("IW3D_setup_for_improvement: bad Iwarp input") ;
10480
10481 Haawarp = IW3D_copy(Iwarp,1.0f) ; /* copy initial warp */
10482 } else { /* have no initial warp */
10483 Haawarp = IW3D_create(Hnx,Hny,Hnz) ; /* initialized to 0 displacements */
10484 }
10485
10486 (void)IW3D_load_energy(Haawarp) ; /* initialize energy field for penalty use */
10487
10488 EXRETURN ;
10489 }
10490
10491 #endif /*(Q6)*/ /*############################################################*/
10492
10493 #if 1
10494 /*============================================================================*/
10495 /** (Q7) Function that actually optimizes ONE incremental patch warp **/
10496 /*============================================================================*/
10497
10498 /*----------------------------------------------------------------------------*/
10499 /* Given a global warp Haawarp, improve it locally over a rectangular patch.
10500 The patch is ibot..itop X jbot..jtop X kbot..ktop (inclusive).
10501 Also, keep up-to-date the copy of the warped source image Haasrcim.
10502 *//*--------------------------------------------------------------------------*/
10503
10504 #define SC_BOX 1
10505 #define SC_BALL 2
10506
10507 /* set optimization parameter range to be
10508 a ball ("sphere"), in parameter space */
10509
10510 #define BALLOPT \
10511 do{ int bc = powell_newuoa_get_con() ; \
10512 if( bc != SC_BALL ){ \
10513 powell_newuoa_set_con_ball() ; \
10514 if( Hverb > 2 ) fprintf(stderr,"[ballopt]\n") ; \
10515 } \
10516 } while(0)
10517
10518 /* set optimization parameter range to be
10519 a box in parameter space [default] */
10520
10521 #define BOXOPT \
10522 do{ int bc = powell_newuoa_get_con() ; \
10523 if( bc != SC_BOX ){ \
10524 powell_newuoa_set_con_box() ; \
10525 if( Hverb > 2 ) fprintf(stderr,"[boxopt]\n") ; \
10526 } \
10527 } while(0)
10528
10529 /*----------------------------------------------------------------------------*/
10530 /* Improve the warp, over a given patch */
10531
IW3D_improve_warp(int warp_code,int ibot,int itop,int jbot,int jtop,int kbot,int ktop)10532 int IW3D_improve_warp( int warp_code ,
10533 int ibot, int itop, int jbot, int jtop, int kbot, int ktop )
10534 {
10535 MRI_IMAGE *warpim ;
10536 int nxh,nyh,nzh , ii,jj,kk , iter,itmax,qq,pp , nwb , nball ;
10537 float *wbfar , wsum ; double prad ;
10538 double *parvec, *xbot,*xtop ;
10539 float *sar , *Axd,*Ayd,*Azd,*Aje,*Ase , *bxd,*byd,*bzd,*bje,*bse , jt,st ;
10540 int ballopt = (SC_BALL == powell_newuoa_get_con()) ; /* 30 Oct 2015 */
10541
10542 ENTRY("IW3D_improve_warp") ;
10543
10544 /*-- setup local region for Hwarp --*/
10545
10546 CLIP(ibot,Hnx-1) ; CLIP(itop,Hnx-1) ;
10547 CLIP(jbot,Hny-1) ; CLIP(jtop,Hny-1) ;
10548 CLIP(kbot,Hnz-1) ; CLIP(ktop,Hnz-1) ;
10549
10550 nxh = itop-ibot+1 ; nyh = jtop-jbot+1 ; nzh = ktop-kbot+1 ; /* sizes of patch */
10551
10552 /* can't do something if patch is TOO small */
10553
10554 if( nxh < NGMIN && nyh < NGMIN && nzh < NGMIN ){ Hskipped++ ; RETURN(0) ; }
10555
10556 Hibot = ibot ; Hitop = itop ; /* index range of the patch we're working on */
10557 Hjbot = jbot ; Hjtop = jtop ; /* (global variables) */
10558 Hkbot = kbot ; Hktop = ktop ;
10559
10560 Hnval = nxh*nyh*nzh ; /* number of points in this patch */
10561
10562 /* only use ball parameter ranges/constraints for tiny patches */
10563
10564 nball = (!WARP_IS_QUINTIC(warp_code)) ? 15 : 19 ;
10565 if( nxh < nball || nyh < nball || nzh < nball ){ /* 10 Feb 2017 */
10566 ballopt = 1 ; BALLOPT ;
10567 }
10568
10569 /* test if this region has enough "weight" to process */
10570
10571 wbfar = MRI_FLOAT_PTR(Hwtim) ; wsum = 0.0f ; /* sum of weights in the patch */
10572 for( nwb=0,kk=kbot ; kk <= ktop ; kk++ ){
10573 for( jj=jbot ; jj <= jtop ; jj++ ){
10574 for( ii=ibot ; ii <= itop ; ii++ ){
10575 qq = ii + jj*Hnx + kk*Hnxy ;
10576 if( Hbmask[qq] ){ wsum += wbfar[qq] ; nwb++ ; }
10577 }}}
10578
10579 if( !Hforce && (nwb < 0.333f*Hnval || wsum < 0.166f*Hnval*Hwbar) ){ /* too light for us */
10580 if( Hverb > 2 ) /* only print for very verbose cases, otherwise just skip silently */
10581 ININFO_message(
10582 " %s patch %03d..%03d %03d..%03d %03d..%03d : skipping (%.1f%% inmask %.1f%% weight)" ,
10583 WARP_CODE_STRING(warp_code) ,
10584 ibot,itop, jbot,jtop, kbot,ktop ,
10585 (100.0f*nwb)/Hnval , (100.0f*wsum)/(Hnval*Hwbar) ) ;
10586 Hskipped++ ; RETURN(0) ;
10587 }
10588
10589 /*-- setup the choice and scale of basis functions for Hwarp-ing --*/
10590
10591 /*-- The max displacment scales are set from the combination of parameters
10592 that gave non-singular patch warps with "OK" distortion levels. These
10593 scales were selected by running program warping_test a number of times.
10594
10595 Hfactor allows the iteration to scale these down at finer levels,
10596 but that wasn't needed after the introduction of the warp penalty.
10597 However, the Hfactor_q param in 3dQwarp allow for this capability. --*/
10598
10599 /*-- If ball optimization is on, the max displacement scales are larger!
10600 That is why BALLOPT was introduced, to allow for more displacement. --*/
10601
10602 /*-- The LITE version of quintic and cubic polynomials omit the highest
10603 order tensor products, which don't add much to the result but do
10604 add extra parameters and extra CPU time. The idea for doing this
10605 came to me one early morning in Dec 2018 in Guangzhou China, when
10606 my jet lag made me wander around in the dark rather than lie in
10607 the small hotel room I'd been given. The results: program runs
10608 nearly twice as fast, and results aren't significantly different. --*/
10609
10610 switch( warp_code ){
10611
10612 default: /* This was the original warping method, long ago and far away */
10613 case MRI_CUBIC:
10614 Hbasis_code = MRI_CUBIC ; /* 3rd order polynomials */
10615 Hbasis_parmax = 0.0280*Hfactor ; /* max displacement from 1 function */
10616 if( ballopt ) Hbasis_parmax = 0.0790*Hfactor ; /* 13 Jan 2015 */
10617 Hnpar = 24 ; /* number of params for local warp */
10618 prad = 0.333 ; /* NEWUOA initial radius */
10619 HCwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
10620 break ;
10621
10622 case MRI_CUBIC_LITE: /* Dec 2018 */
10623 Hbasis_code = MRI_CUBIC_LITE ; /* 3rd order polynomials */
10624 Hbasis_parmax = 0.0421*Hfactor ; /* max displacement from 1 function */
10625 if( ballopt ) Hbasis_parmax = 0.1141*Hfactor ;
10626 Hnpar = 12 ; /* number of params for local warp */
10627 prad = 0.333 ; /* NEWUOA initial radius */
10628 HCwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
10629 break ;
10630
10631 case MRI_QUINTIC:
10632 Hbasis_code = MRI_QUINTIC ; /* 5th order polynomials */
10633 Hbasis_parmax = 0.0099*Hfactor ;
10634 if( ballopt ) Hbasis_parmax = 0.0611*Hfactor ; /* 13 Jan 2015 */
10635 Hnpar = 81 ;
10636 prad = 0.333 ;
10637 HQwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ;
10638 break ;
10639
10640 case MRI_QUINTIC_LITE: /* Dec 2018 */
10641 Hbasis_code = MRI_QUINTIC_LITE ; /* 5th order polynomials */
10642 Hbasis_parmax = 0.0267*Hfactor ; /* max displacement from 1 function */
10643 if( ballopt ) Hbasis_parmax = 0.1111*Hfactor ;
10644 Hnpar = 30 ; /* number of params for local warp */
10645 prad = 0.333 ; /* NEWUOA initial radius */
10646 HQwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
10647 break ;
10648
10649 case MRI_SINCC: /* Nov 2018 */
10650 Hbasis_code = MRI_SINCC ;
10651 Hbasis_parmax = 0.1666*Hfactor ; /* max displacement from 1 function */
10652 if( ballopt ) Hbasis_parmax = 0.2345*Hfactor ;
10653 Hnpar = 3 ; /* number of params for local warp */
10654 prad = 0.333 ; /* NEWUOA initial radius */
10655 HSCwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
10656 break ;
10657
10658 #ifdef ALLOW_BASIS5 /* 05 Nov 2015 */
10659 case MRI_CUBIC_PLUS_1: /* basis3 */
10660 BALLOPT ; ballopt = 1 ;
10661 Hbasis_code = MRI_CUBIC_PLUS_1 ;
10662 Hbasis_parmax = 0.0444 ;
10663 prad = 0.333 ;
10664 HCwarp_setup_basis345( nxh,nyh,nzh, Hgflags , 1 ) ;
10665 Hnpar = 3*H5nparm ;
10666 break ;
10667
10668 case MRI_CUBIC_PLUS_2: /* basis4 */
10669 BALLOPT ; ballopt = 1 ;
10670 Hbasis_code = MRI_CUBIC_PLUS_2 ;
10671 Hbasis_parmax = 0.0444 ;
10672 prad = 0.333 ;
10673 HCwarp_setup_basis345( nxh,nyh,nzh, Hgflags , 2 ) ;
10674 Hnpar = 3*H5nparm ;
10675 break ;
10676
10677 case MRI_CUBIC_PLUS_3: /* basis5 */
10678 BALLOPT ; ballopt = 1 ;
10679 Hbasis_code = MRI_CUBIC_PLUS_3 ;
10680 Hbasis_parmax = 0.0432 ;
10681 prad = 0.333 ;
10682 HCwarp_setup_basis345( nxh,nyh,nzh, Hgflags , 3 ) ;
10683 Hnpar = 3*H5nparm ;
10684 break ;
10685 #endif
10686 }
10687
10688 /* skip if not enough points for number of parameters [07 Apr 2016] */
10689
10690 if( nwb < 5*Hnparmap ){ /* Hnparmap was set in a *setup_basis* function just above */
10691 if( Hverb > 2 ) /* only for the very verbose */
10692 ININFO_message(
10693 " %s patch %03d..%03d %03d..%03d %03d..%03d : skipping (%d voxels inmask vs %d parameters)" ,
10694 WARP_CODE_STRING(warp_code) ,
10695 ibot,itop, jbot,jtop, kbot,ktop ,
10696 nwb , Hnparmap ) ;
10697 Hskipped++ ; RETURN(0) ;
10698 }
10699
10700 /* mark what is allowed to be warped */
10701
10702 Hdox = !(Hflags & NWARP_NOXDIS_FLAG) ; /* do the x direction? */
10703 Hdoy = !(Hflags & NWARP_NOYDIS_FLAG) ; /* y? */
10704 Hdoz = !(Hflags & NWARP_NOZDIS_FLAG) ; /* z? */
10705
10706 /* Create parameters that define Hwarp (these are to be optimized) */
10707 /* Note the sub-arrays Hxpar etc. for the different directions. */
10708
10709 Hpar = (float *)realloc(Hpar,sizeof(float)*Hnpar) ; /* parameter array */
10710 for( ii=0 ; ii < Hnpar ; ii++ ) Hpar[ii] = 0.0f ;
10711 Hxpar = Hpar ; /* sub-array of parameters for x displacements */
10712 Hypar = Hxpar + (Hnpar/3) ; /* et cetera */
10713 Hzpar = Hypar + (Hnpar/3) ; /* et cetera */
10714
10715 /*-- create space for local warped source image values --*/
10716 /*-- these are needed for correlating with base image --*/
10717
10718 Hwval = (float *)realloc(Hwval,sizeof(float)*Hnval) ;
10719
10720 /*-- setup to do incremental 'correlation' on the local region --*/
10721
10722 if( INCOR_check_meth_code(Hmatch_code) == 4 ){ /* set LPC bounds [25 Jun 2014] */
10723 Hmpar->ar[0] = (float)Hnx; Hmpar->ar[1] = (float)Hny; Hmpar->ar[2] = (float)Hnz;
10724 Hmpar->ar[3] = (float)Hibot ; Hmpar->ar[4] = (float)Hitop ;
10725 Hmpar->ar[5] = (float)Hjbot ; Hmpar->ar[6] = (float)Hjtop ;
10726 Hmpar->ar[7] = (float)Hkbot ; Hmpar->ar[8] = (float)Hktop ;
10727 }
10728
10729 INCOR_destroy(Hincor) ;
10730 Hincor = INCOR_create( Hmatch_code , Hmpar ) ; /* struct for correlations */
10731
10732 FREEIFNN(Haawt) ; FREEIFNN(Hbval) ;
10733
10734 need_AH = Hpen_use ; /* we don't need AH = altered warp, unless */
10735 if( Hpen_use ) Hpen_sum = 0.0 ; /* need to compute warp distortion penalty */
10736
10737 #undef RESTORE_WBFAR /* macro to fix wbfar after we break it below */
10738 #define RESTORE_WBFAR \
10739 do{ for( pp=0,kk=kbot ; kk <= ktop ; kk++ ) \
10740 for( jj=jbot ; jj <= jtop ; jj++ ) \
10741 for( ii=ibot ; ii <= itop ; ii++,pp++ ) \
10742 wbfar[ii+jj*Hnx+kk*Hnxy] = Haawt[pp] ; \
10743 } while(0)
10744
10745 if( Hnval < Hnxyz ){ /* initialize correlation from */
10746 float *wbfar=MRI_FLOAT_PTR(Hwtim) ; /* non-changing part of Haasrcim */
10747 float *bar =MRI_FLOAT_PTR(BASIM) ;
10748
10749 Haawt = (float *)malloc(sizeof(float)*Hnval) ;
10750 Hbval = (float *)malloc(sizeof(float)*Hnval) ;
10751 for( pp=0,kk=kbot ; kk <= ktop ; kk++ ){ /* extract weights */
10752 for( jj=jbot ; jj <= jtop ; jj++ ){ /* and base image */
10753 for( ii=ibot ; ii <= itop ; ii++,pp++ ){ /* for patch region */
10754 qq = ii + jj*Hnx + kk*Hnxy ;
10755 Haawt[pp] = wbfar[qq] ; /* copy weight image vals */
10756 Hbval[pp] = bar[qq] ; /* copy base image vals */
10757 wbfar[qq] = 0.0f ; /* 0 out temp weight (fixed by RESTORE_WBFAR) */
10758 }}}
10759
10760 if( is_float_array_constant(Hnval,Hbval) ){ /* can't correlate with this */
10761 if( Hverb > 2 ) /* only for the very verbose */
10762 ININFO_message(
10763 " %7s patch %03d..%03d %03d..%03d %03d..%03d : skipping (base=const=%g)" ,
10764 WARP_CODE_STRING(warp_code) ,
10765 ibot,itop, jbot,jtop, kbot,ktop , Hbval[0] ) ;
10766 RESTORE_WBFAR ; Hskipped++ ; RETURN(0) ;
10767 }
10768
10769 /* initialize the 'correlation' from the data that won't
10770 be changing (i.e., data from outside the local patch)
10771 -- note wbfar was set to 0 above for voxels INSIDE the local patch */
10772
10773 if( !Hlocalstat ) /* Hlocalstat should be 0 */
10774 INCOR_addto( Hincor , Hnxyz ,
10775 MRI_FLOAT_PTR(BASIM) , MRI_FLOAT_PTR(Haasrcim) , wbfar ) ;
10776 RESTORE_WBFAR ; /* fix wbfar inside local patch */
10777
10778 /* init penalty (Hpen_sum) from non-changing part of Haawarp, if needed */
10779
10780 if( Hpen_use ){
10781 float *je , *se ;
10782 je = Haawarp->je ; se = Haawarp->se ;
10783 for( kk=kbot ; kk <= ktop ; kk++ )
10784 for( jj=jbot ; jj <= jtop ; jj++ )
10785 for( ii=ibot ; ii <= itop ; ii++ )
10786 je[ii+jj*Hnx+kk*Hnxy] = se[ii+jj*Hnx+kk*Hnxy] = 0.0f ;
10787 Hpen_sum = HPEN_addup(Hnxyz,je,se) ;
10788 }
10789
10790 } /* end of special setup over a patch smaller than the universe */
10791 /* which is 'special' in some sense, but is almost always the case */
10792
10793 /*--- OK, let's do the optimization of warp parameters (finally!) ---*/
10794
10795 parvec = (double *)malloc(sizeof(double)*Hnparmap) ; /* Hnparmap was */
10796 xbot = (double *)malloc(sizeof(double)*Hnparmap) ; /* set in func */
10797 xtop = (double *)malloc(sizeof(double)*Hnparmap) ; /* H?warp_setup_basis */
10798 for( ii=0 ; ii < Hnparmap ; ii++ ){
10799 parvec[ii] = 0.0 ; /* initialize incremental warp = identity */
10800 xbot[ii] = -Hbasis_parmax ;
10801 xtop[ii] = Hbasis_parmax ;
10802 }
10803
10804 if( 1 || Hnval > 6666 ) /* at present, this is always on, since */
10805 powell_set_mfac( 1.001f , 2.001f ) ; /* it gives slightly better results (IMHO) */
10806 else
10807 powell_set_mfac( 2.001f , 1.001f ) ; /* so this is always OFF */
10808
10809 /***** HERE is the actual optimization! (the point of all this code) *****/
10810
10811 /** set maximum number of iterations allowed in the NEWUOA code **/
10812
10813 itmax = 8*Hnparmap+31 ; /* this number was made up from nothing at all */
10814 #if 0
10815 if( WORKHARD(Hlev_now) || SUPERHARD(Hlev_now) ) itmax -= Hnparmap ;
10816 #endif
10817
10818 if( Hverb > 3 ){
10819 /* INFO_message("itmax = %d",itmax) ; */
10820 powell_set_verbose(Hverb-3) ;
10821 }
10822
10823 /******* do it babee!! ***********************************/
10824
10825 /* Note use of setjmp() here to allow the optimizer to
10826 get broken out of by the QUIT or ALRM signal, which
10827 can be necessary if OpenMP gets into some race condition
10828
10829 How setjmp/longjmp works:
10830 1) The initial call below to setjmp() will return 0,
10831 so the "normal" code will be executed after the if().
10832 2) If the ALRM or QUIT signal happen, the signal
10833 handler IW3D_signal_quit() is invoked.
10834 3) That handler will call longjmp() which will set
10835 things up so that it looks like setjmp() return
10836 a nonzero value, and at that point the "bad news"
10837 branch of the if() will be taken.
10838 4) If the signal handler is not invoked, then the
10839 program continues normally.
10840 Also see https://en.wikipedia.org/wiki/Setjmp.h for more explanation */
10841
10842 Hquitting_do_jump = 666 ; /* indication that setjmp() is invoked */
10843
10844 if( setjmp(Hquitting_jmp_buf) == 0 ){ /* optimization of Hwarp parameters */
10845
10846 #ifdef USE_OMP
10847 int asec ;
10848 asec = (int)rintf(0.0000002f*Hnval*Hnpar*itmax/nthmax) ;
10849 if( asec < 99 ) asec = 99 ; /* min num seconds to wait */
10850 else if( asec > 1888 ) asec = 1888 ; /* max num seconds to wait */
10851 (void)alarm(asec) ; /* ALRM signal if optimizer takes too long. */
10852 /* The reason for this folderol is that gcc OpenMP */
10853 /* sometimes (rarely) freezes in a race condition. */
10854
10855 /* Also, the QUIT signal might come from user */
10856
10857 /* signal handler IW3D_signal_quit() was */
10858 /* setup for ALRM and QUIT in 3dQwarp.c */
10859 #endif
10860
10861 /***** HERE IS THE OPTIMIZATION!!! *****/
10862
10863 iter = powell_newuoa_con( Hnparmap , parvec,xbot,xtop , 0 ,
10864 prad,0.009*prad , itmax , IW3D_scalar_costfun ) ;
10865
10866 if( iter == -1 ){ /* failure in newuoa() code - try one more time */
10867 iter = powell_newuoa_con( Hnparmap , parvec,xbot,xtop , 0 ,
10868 prad,0.09*prad , itmax , IW3D_scalar_costfun ) ;
10869 }
10870
10871 #ifdef USE_OMP
10872 (void)alarm(0) ; /* cancel alarm signal if we succeeded/returned ! */
10873 #endif
10874
10875 } else { /*----- if we get to here, it was from the signal handler -----*/
10876 /*----- using longjmp() to break from optimizer == failure -----*/
10877
10878 WARNING_message("longjmp out of IW3D_improve_warp due to %s signal\n"
10879 " -- warp optimization ends now" ,
10880 (Hquitting_sig==SIGQUIT) ? "QUIT (from user)"
10881 :(Hquitting_sig==SIGALRM) ? "ALRM (from internal timeout) :("
10882 : "unknown" ) ;
10883 if( Hquitting_sig == SIGALRM )
10884 WARNING_message(" ALRM signal is usually due to a multi-threaded 'race'\n"
10885 " condition in GCC OpenMP implementation\n"
10886 " -- which is to say, a compilation bug\n"
10887 " that arises quasi-randomly\n"
10888 " -- you can try running this job again and\n"
10889 " with luck, it will work out OK next time" ) ;
10890
10891 Hquitting_do_jump = 0 ; /* turn off longjmp() in signal handler */
10892 RETURN(0) ; /* failure return */
10893 }
10894 Hquitting_do_jump = 0 ;
10895 if( Hquitting ) RETURN(0) ; /* this code probably redundantly pleonastic */
10896
10897 /******* iter = number of iterations actually used in this patch *******/
10898
10899 if( iter > Hnparmap ) Hnpar_sum += Hnparmap ; /* number of parameters used so far */
10900
10901 if( Hverb > 3 ) powell_set_verbose(0) ;
10902
10903 /***** cleanup and exit phase ***/
10904
10905 free(xtop) ; free(xbot) ; /* range bounds on parameters */
10906
10907 if( iter <= 0 ){ /* something bad happened (very unlikely) */
10908 ININFO_message(
10909 " %s patch %03d..%03d %03d..%03d %03d..%03d : skipping - powell_newuoa_con() failure code=%d" ,
10910 WARP_CODE_STRING(warp_code) ,
10911 ibot,itop, jbot,jtop, kbot,ktop , iter ) ;
10912 ININFO_message(
10913 "powell_newuoa_con( ndim=%d x=%p xbot=%p xtop=%p nrand=%d rstart=%f rend=%f maxcall=%d ufunc=%p",
10914 Hnparmap , (void *)parvec , (void *)xbot , (void *)xtop , 0 , prad , 0.009*prad , itmax ,
10915 (void *)IW3D_scalar_costfun ) ;
10916 free(parvec); Hskipped++ ; RETURN(0);
10917 }
10918
10919 /* load optimized warped image and warp into their patches */
10920
10921 need_AH = 1 ; /* force the computation of AHwarp this time */
10922 Hcost = IW3D_scalar_costfun( Hnparmap , parvec ) ; /* evaluate at final params */
10923 (void)IW3D_load_energy(AHwarp) ; /* for Hverb output below */
10924
10925 /* AHwarp gets loaded into Haawarp and Hwval into Haasrcim;
10926 the patch local energy fields get loaded into Haawarp's energy fields */
10927
10928 sar = MRI_FLOAT_PTR(Haasrcim) ;
10929 Axd = Haawarp->xd; Ayd = Haawarp->yd; Azd = Haawarp->zd; Aje = Haawarp->je; Ase = Haawarp->se;
10930 bxd = AHwarp->xd ; byd = AHwarp->yd ; bzd = AHwarp->zd ; bje = AHwarp->je ; bse = AHwarp->se ;
10931
10932 jt= bje[0] ; st = bse[0] ;
10933 for( pp=0,kk=kbot ; kk <= ktop ; kk++ ){
10934 for( jj=jbot ; jj <= jtop ; jj++ ){
10935 for( ii=ibot ; ii <= itop ; ii++,pp++ ){
10936 qq = ii + jj*Hnx + kk*Hnxy ;
10937 sar[qq] = Hwval[pp]; /* load into warped source image */
10938 Axd[qq] = bxd[pp]; Ayd[qq] = byd[pp]; Azd[qq] = bzd[pp]; /* into Haawarp */
10939 Aje[qq] = bje[pp]; Ase[qq] = bse[pp]; /* into penalty 'energies' */
10940 if( Aje[qq] > jt ) jt = Aje[qq]; /* largest 'j' penalty in the patch */
10941 if( Ase[qq] > st ) st = Ase[qq]; /* largest 's' penalty */
10942 }}}
10943
10944 if( Hverb > 1 ){ /* detailed overview of what went down in this patch */
10945 char cbuf[32] ;
10946 if( Hxyzmatch_num > 0 ) sprintf(cbuf,"xyzmatch pen=%g",Hxyzmatch_cost) ;
10947 else strcpy(cbuf,"\0") ;
10948 ININFO_message(
10949 " %7s patch %03d..%03d %03d..%03d %03d..%03d : cost=%.5f iter=%d : energy=%.3f:%.3f pen=%g pure=%g %s",
10950 WARP_CODE_STRING(Hbasis_code) ,
10951 ibot,itop, jbot,jtop, kbot,ktop , Hcost , iter , jt,st , Hpenn , Hcostt , cbuf ) ;
10952
10953 } else if( Hverb == 1 && (Hlev_now<=2 || lrand48()%(Hlev_now*Hlev_now*Hlev_now/9)==0) ){
10954 fprintf(stderr,".") ; /* just a pacifier */
10955 }
10956
10957 /* ZOMG -- let's vamoose the ranchette */
10958
10959 free(parvec) ; Hdone++ ; RETURN(iter) ;
10960 }
10961
10962 #endif /*(Q7)*/ /*############################################################*/
10963
10964 #if 1
10965 /*============================================================================*/
10966 /** (Q8) Functions that drive the warp searching process **/
10967 /*============================================================================*/
10968
10969 static IndexWarp3D *WO_iwarp = NULL ;
10970 static int *WO_ilev = 0 ;
10971
Hfactor_from_patchsize_ratio(float prat)10972 static float Hfactor_from_patchsize_ratio( float prat )
10973 {
10974 static float alpha = 0.0f ;
10975 if( Hfactor_q >= 1.0f || Hfactor_q < 0.1f || prat >= 1.0f ) return 1.0f ;
10976 if( alpha == 0.0f ) alpha = logf(Hfactor_q) / logf(0.1f) ;
10977 return powf(prat,alpha) ;
10978 }
10979
10980 /*----------------------------------------------------------------------------*/
10981 /* Optimize the warp over a sequence of ever smaller patches;
10982 bim = base image
10983 wbim = weight for base image (can be NULL)
10984 sim = source image (to be warped)
10985 meth_code = matching code for INCOR
10986 warp_flags = special cases (e.g., no x-displacements)
10987 Also, a vast number of H... global variables can be set to affect the way
10988 this function works.
10989 This function is called from IW3D_warp_s2bim(), which is the function
10990 called in turn from 3dQwarp.c -- the face to the world at large.
10991 Return value is the index warp as optimized.
10992
10993 3dQwarp is basically a front end for this function.
10994 The strategy of patch shrinkage (lev) and looping was derived from
10995 (a) wild ass guesses and random emotional states
10996 (b) memories of multigrid methods I learned back in the Dark Ages
10997 (c) experiences with 3dQwarp
10998 *//*-------------------------------------------------------------------------*/
10999
IW3D_warpomatic(MRI_IMAGE * bim,MRI_IMAGE * wbim,MRI_IMAGE * sim,int meth_code,int warp_flags)11000 IndexWarp3D * IW3D_warpomatic( MRI_IMAGE *bim, MRI_IMAGE *wbim, MRI_IMAGE *sim,
11001 int meth_code, int warp_flags )
11002 {
11003 int lev,levs,leve , xwid,ywid,zwid , xdel,ydel,zdel , iter , hgzero=0 ;
11004 int ibot,itop,idon , jbot,jtop,jdon , kbot,ktop,kdon , dox,doy,doz , iii ;
11005 float flev , glev , Hcostold , Hcostmid=0.0f,Hcostend=0.0f,Hcostbeg=999.9f ;
11006 int imin,imax , jmin,jmax, kmin,kmax , ibbb,ittt , jbbb,jttt , kbbb,kttt ;
11007 int dkkk,djjj,diii , ngmin=NGMIN , levdone=0 , pcon , do_qfinal=0 ;
11008 int zmode=MRI_CUBIC , zmode2=MRI_CUBIC , zmodeX , nlevr , nsup,isup , itnum ;
11009 int cmode=MRI_CUBIC , qmode=MRI_QUINTIC , smode=MRI_SINCC ;
11010 IndexWarp3D *OutWarp ; /* the return value */
11011 char warplab[64] ; /* 02 Jan 2015 */
11012 int xwid0,ywid0,zwid0 ; /* 04 Jan 2019 */
11013 int64_t num_xyz ; /* number of voxels in a patch [06 Nov 2019] */
11014 float psize0=0.0f , psize=0.0f ; /* 24 Feb 2020 */
11015
11016 ENTRY("IW3D_warpomatic") ;
11017
11018 Hfirsttime = 1 ; /* for fun printouts on first pass */
11019
11020 /* Choose the 'modes' = basis functions to be used at various phases */
11021 /* This whole mode selection thing is a little messy, as the choice
11022 of basis functions is influenced by a bunch of 3dQwarp options,
11023 and so some of these settings might be altered later in this function. */
11024
11025 /* cmode = cubic polynomial mode - note that Huse_sincc==0 at this time */
11026 /* zmode = set here, but will likely be altered below
11027 = the mode actually used for the incremental warp */
11028 /* zmode2 = set here, but will likely be altered below
11029 = the mode used on the second pass at a given lev with '-workhard' */
11030 /* qmode = the quintic polynomial mode to use if quintics are enabled */
11031
11032 if( Huse_sincc ){ cmode = zmode = zmode2 = MRI_SINCC ; } /* Nov 2019 */
11033 if( Huse_cubic_lite ){ cmode = zmode = zmode2 = MRI_CUBIC_LITE ; } /* Dec 2018 */
11034 if( Huse_quintic_lite ){ qmode = MRI_QUINTIC_LITE ; }
11035
11036 /* set up a lot of things (mostly in global H... variables) */
11037
11038 IW3D_setup_for_improvement( bim, wbim, sim, WO_iwarp, meth_code, warp_flags ) ;
11039
11040 /* compute range of indexes over which to warp */
11041 /* imin..imax jmin..jmax kmin..kmax = autobox = contains all nonzeros */
11042
11043 MRI_autobbox( Hwtim , &imin,&imax , &jmin,&jmax , &kmin,&kmax ) ;
11044
11045 save_H_zero = 1 ; /* save 0th cost in Hcostt_zero for printout in 3dQwarp */
11046
11047 /*---- do global warping first (lev=0) ----*/
11048
11049 /* We want patch from imin..imax (etc), BUT must extend it a little
11050 on each edge to allow for the warp going to 0 at the edges; thus, we
11051 add xwid voxels on each end (if possible) -- etc for the other directions.
11052 Also, we make sure the 'global' patch is at least 1 voxel back
11053 from the edge of the volume (so the warp really IS zero at the edges). */
11054
11055 xwid = (imax-imin)/8 ; ywid = (jmax-jmin)/8 ; zwid = (kmax-kmin)/8 ;
11056 ibbb = MAX(1,imin-xwid) ; jbbb = MAX(1,jmin-ywid) ; kbbb = MAX(1,kmin-zwid) ;
11057 ittt = MIN(Hnx-2,imax+xwid); jttt = MIN(Hny-2,jmax+ywid); kttt = MIN(Hnz-2,kmax+zwid);
11058
11059 if( Hnx == 1 ){ ibbb = ittt = 0 ; } /* deal with 2D input images */
11060 if( Hny == 1 ){ jbbb = jttt = 0 ; }
11061 if( Hnz == 1 ){ kbbb = kttt = 0 ; }
11062
11063 /* initial patch width at lev=0 == largest patch */
11064
11065 xwid0 = ittt-ibbb+1 ; ywid0 = jttt-jbbb+1 ; zwid0 = kttt-kbbb+1 ;
11066
11067 /* actual warp at lev=0 is over domain ibbb..ittt X jbbb..jttt X kbbb..kttt */
11068
11069 diii = ittt-ibbb+1 ; djjj = jttt-jbbb+1 ; dkkk = kttt-kbbb+1 ; /* sizes of this patch */
11070 iter = MAX(diii,djjj) ; iter = MAX(iter,dkkk) ; /* largest size of a patch edge */
11071 psize0 = (float)iter ; /* for Hfactor modification later */
11072 if( iter < NGMIN ){
11073 ERROR_message("Can't warpomatic such a small volume: %d x %d x %d",diii,djjj,dkkk) ;
11074 RETURN(NULL) ;
11075 }
11076
11077 /*-- announce the birth of a new warping procedure --*/
11078
11079 if( Hverb )
11080 INFO_message("AFNI warpomatic: %d x %d x %d volume ; autobbox = %d..%d %d..%d %d..%d [clock=%s]",
11081 Hnx,Hny,Hnz, imin,imax,jmin,jmax,kmin,kmax,
11082 nice_time_string(NI_clock_time()) ) ;
11083 if( Hverb > 1 ) MEMORY_CHECK("starting") ;
11084
11085 /*------ do the top level (global warps) --------*/
11086
11087 pcon = powell_newuoa_get_con() ; /* 30 Oct 2015 */
11088
11089 if( Hverb > 1 && Hworkhard2 >= Hworkhard1 )
11090 INFO_message("WORKHARD from lev=%d to %d",Hworkhard1,Hworkhard2) ;
11091 if( Hverb > 1 && Hsuperhard2 >= Hsuperhard1 )
11092 INFO_message("SUPERHARD from lev=%d to %d",Hsuperhard1,Hsuperhard2) ;
11093
11094 Hfactor = 1.0f ; /* initialize to largest allowed displacement field */
11095
11096 if( Hlev_start == 0 || HGRID(0) == 0 ){ /* this is the lev=0 code */
11097
11098 /* nlevr = number of times to try the global quintic patch */
11099 /* Hznoq is set in 3dQwarp by the '-noQ' option (not in the help) */
11100 nlevr = ( Hznoq ) ? 0 : 1 ; /* used to be 2, but that didn't help much */
11101
11102 /* force the warp to happen, but don't use any penalty at this level */
11103 Hforce = 1 ; Hpen_use = 0 ; Hlev_now = 0 ;
11104
11105 PBLUR_BASE (ibbb,ittt,jbbb,jttt,kbbb,kttt) ; /* progressive blur, if ordered */
11106 PBLUR_SOURCE(ibbb,ittt,jbbb,jttt,kbbb,kttt) ;
11107
11108 mri_free(Haasrcim) ; /* At this point, create the warped */
11109 if( IW3D_is_zero(Haawarp) ) /* source image Haasrcim, which will */
11110 Haasrcim = mri_to_float(SRCIM) ; /* be updated in IW3D_improve_warp() */
11111 else
11112 Haasrcim = IW3D_warp_floatim( Haawarp, SRCIM, Himeth , 1.0f ) ;
11113
11114 if( Hverb == 1 ) fprintf(stderr,"lev=0 %d..%d %d..%d %d..%d: ",ibbb,ittt,jbbb,jttt,kbbb,kttt) ;
11115
11116 /* The goal at lev=0 is to get the large scale alignment as good
11117 as possible, as quickly as possible.
11118 To do this, successively more complicated (more parameters)
11119 incremental warps are used over this global patch.
11120 This strategy evolved over time, and seems to work reasonably well.
11121 I discovered that repeating a given type of warp to try for
11122 better optimization (e.g., 2 calls with CUBIC_LITE) seldom
11123 did much of value and consumed CPU time, so now each type
11124 of warp gets just a single optimizing call at lev=0.
11125 The sequence is
11126 SINCC(3), CUBIC_LITE(12), CUBIC(24), QUINTIC_LITE(30), QUINTIC(81). */
11127
11128 /* sincc = crudest and fastest = 3 params = mostly just shifting */
11129 BOXOPT ;
11130 (void)IW3D_improve_warp( MRI_SINCC , ibbb,ittt,jbbb,jttt,kbbb,kttt ) ;
11131
11132 /* lite cubic = next step up in refinement = 12 params = affine-ish */
11133 (void)IW3D_improve_warp( MRI_CUBIC_LITE , ibbb,ittt,jbbb,jttt,kbbb,kttt ) ;
11134 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11135
11136 /* heavy cubic = more parameters, hopefully more matching = 24 params */
11137 BALLOPT ;
11138 (void)IW3D_improve_warp( MRI_CUBIC , ibbb,ittt,jbbb,jttt,kbbb,kttt ) ;
11139 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11140
11141 if( nlevr ){ /* higher order warps at lev=0 unless turned off */
11142
11143 /* lite quintic = 30 params */
11144 BALLOPT ;
11145 (void)IW3D_improve_warp( MRI_QUINTIC_LITE , ibbb,ittt,jbbb,jttt,kbbb,kttt ) ;
11146 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11147
11148 /* heavy quintic = 81 params */
11149 BALLOPT ;
11150 (void)IW3D_improve_warp( MRI_QUINTIC, ibbb,ittt,jbbb,jttt,kbbb,kttt );
11151 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11152
11153 #if defined(ALLOW_BASIS5)
11154 if( H5zero ){ /* this is not usually of any value at all! */
11155 /* superheavy cubic = 105 params = pretty damn slow */
11156 BALLOPT ;
11157 (void)IW3D_improve_warp( MRI_CUBIC_PLUS_3, ibbb,ittt,jbbb,jttt,kbbb,kttt );
11158 HCwarp_setup_basis345(0,0,0,0,0) ; /* cleanup */
11159 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11160 }
11161
11162 #endif
11163 }
11164
11165 /* lev=0 warping is done -- save it? */
11166
11167 if( Hsave_allwarps ){ /* 02 Jan 2015 */
11168 sprintf(warplab,"Lev0.%04dx%04dx%04d",ittt-ibbb+1,jttt-jbbb+1,kttt-kbbb+1) ;
11169 HSAVE_ADDTO(Haawarp,warplab) ;
11170 }
11171 if( Hverb == 1 ) fprintf(stderr," done [cost:%.5f==>%.5f]\n",Hfirstcost,Hcost) ;
11172
11173 } else { /*--- no lev=0 optimization (3dQwarp '-inilev' option was used?) */
11174
11175 Hcost = 666.666f ; /* a beastly thing to say */
11176
11177 }
11178
11179 /*--- end of level 0 work ---*/
11180
11181 powell_newuoa_set_con(pcon) ;
11182
11183 /* for further steps, don't force things, and use the penalty [maybe] */
11184
11185 Hforce = 0 ; Hlev_final = 0 ;
11186 Hcostmid = Hcostend = Hcostbeg = Hcost ; /* set cost values for reporting */
11187
11188 /* HAVE_HGRID is set if 3dQwarp '-gridlist' option was used;
11189 normal situation (code determines patch grid sizes) is !HAVE_HGRID.
11190 Below, we set ngmin = the minimum patch size that we will shrink to. */
11191
11192 if( !HAVE_HGRID ){
11193 /* Hngmin is from 3dQwarp '-minpatch' */
11194 if( Hngmin > 0 && Hlev_end > 99 ){
11195 ngmin = Hngmin ; if( ngmin < NGMIN_Q ) ngmin = NGMIN_Q ;
11196 }
11197 if( ngmin < NGMIN ) ngmin = NGMIN ; /* can't go below this! */
11198 else if( ngmin%2 == 0 ) ngmin-- ; /* must be odd */
11199 }
11200
11201 /** this should not happen, but if it does, then we're too small **/
11202
11203 if( ngmin >= Hnx && ngmin >= Hny && ngmin >= Hnz ) goto DoneDoneDone ;
11204
11205 /* inter-level shrinkage factor
11206 -- Used to be set-able by user, but now is fixed at 0.749999
11207 -- That is, the default progress of patch dimensions
11208 shrinks by factor of 0.75 at each lev,
11209 which is a factor of 0.75^3 = 0.42 in patch volume
11210 -- No longer set-able by user, as that turned out to be useless
11211 -- Why 0.75? Seemed reasonable. 0.5 was too fast to get good results. */
11212
11213 if( Hshrink > 1.0f ) Hshrink = 1.0f / Hshrink ;
11214 if( Hshrink < 0.444f || Hshrink > 0.888f ) Hshrink = 0.749999f ;
11215
11216 /*------ Now, iterate down to finer and finer patches ------*/
11217
11218 levs = MAX(1,Hlev_start) ; /* start level */
11219 leve = Hlev_end ;
11220 /* if '-gridlist' was used, it defines the patch sizes and levels */
11221 if( HAVE_HGRID && Hgridlist_num-1 < leve ) leve = Hgridlist_num-1 ;
11222
11223 /* if '-gridlist' was used and the user inputs a '0',
11224 that means to use the default patch size at lev=1, which we save now */
11225
11226 if( HAVE_HGRID ){
11227 xwid = (Hnx+1)*0.75f ; if( xwid%2 == 0 ) xwid++ ;
11228 ywid = (Hny+1)*0.75f ; if( ywid%2 == 0 ) ywid++ ;
11229 zwid = (Hnz+1)*0.75f ; if( zwid%2 == 0 ) zwid++ ;
11230 if( xwid > ngmin ) hgzero = xwid ;
11231 if( ywid > ngmin && ywid < hgzero ) hgzero = ywid ;
11232 if( zwid > ngmin && zwid < hgzero ) hgzero = zwid ;
11233 if( hgzero == 0 ) hgzero = ngmin ; /* should not happen */
11234 }
11235
11236 /* loop over levels, finer and finer and finer */
11237
11238 for( lev=levs ; lev <= leve && !levdone ; lev++ ){
11239
11240 /* set penalty factor for this level */
11241
11242 flev = (Hpen_old) ? 1.0f : powf( (float)(lev-levs+1) , 0.333f ) ; ;
11243 Hpen_fff = Hpen_fac * MIN(3.21f,flev) ; /* 20 Sep 2013 */
11244
11245 Hpen_use = (Hpen_fff > 0.0f) && (lev >= Hpen_first_lev ) ;
11246 if( lev == Hpen_first_lev ) Hpen_fff *= 0.5f ;
11247
11248 /* compute width of rectangles at this level */
11249
11250 if( ! HAVE_HGRID ){ /* the olden way */
11251 flev = powf(Hshrink,(float)lev) ; /* shrinkage fraction */
11252
11253 /* set the patch size to use at this level */
11254
11255 /* The old way: shrink from the full dataset grid size */
11256 /* The new way: shrink from the lev=0 grid size */
11257 /* Old way causes trouble if there is a lot of zeropadding, */
11258 /* which in turn can happen (in 3dQwarp.c) if the coordinate */
11259 /* overlap between the source and base datasets is poor. */
11260 /* What happens is that the first few levs have such large patches */
11261 /* that they accomplish nothing and take a LOT of CPU time. */
11262 /* With the new way, the patch sizes match the actual extent of the */
11263 /* base dataset, so useful work is done right away and */
11264 /* lots of CPU time doesn't get burned doing useless cr*p. */
11265
11266 #if 0 /* the old way, shrinking from full grid size :( */
11267 xwid = (Hnx+1)*flev ; if( xwid%2 == 0 ) xwid++ ; /* patch sizes must be odd */
11268 ywid = (Hny+1)*flev ; if( ywid%2 == 0 ) ywid++ ;
11269 zwid = (Hnz+1)*flev ; if( zwid%2 == 0 ) zwid++ ;
11270 #else /* the new way, shrinking from lev=0 grid size :) */
11271 xwid = xwid0*flev ; if( xwid%2 == 0 ) xwid++ ; /* 04 Jan 2019 */
11272 ywid = ywid0*flev ; if( ywid%2 == 0 ) ywid++ ;
11273 zwid = zwid0*flev ; if( zwid%2 == 0 ) zwid++ ;
11274 #endif
11275 } else { /* the new-fangled way [31 Dec 2014] via '-gridlist' */
11276 xwid = ywid = zwid = HGRID(lev) ;
11277 if( xwid == 0 ) xwid = ywid = zwid = hgzero ;
11278 if( xwid < NGMIN ) goto DoneDoneDone ; /* past the end of the Hgridlist array? */
11279 }
11280 psize = MAX(xwid,ywid) ; if( psize < zwid ) psize = zwid ;
11281 Hfactor = Hfactor_from_patchsize_ratio( psize / psize0 ) ;
11282
11283 /* decide if we are doing things in x, y, and/or z */
11284
11285 dox = (xwid >= ngmin) && !(Hgflags & NWARP_NOXDEP_FLAG) ;
11286 doy = (ywid >= ngmin) && !(Hgflags & NWARP_NOYDEP_FLAG) ;
11287 doz = (zwid >= ngmin) && !(Hgflags & NWARP_NOZDEP_FLAG) ;
11288
11289 if( !dox && !doy && !doz ){ /* exit immediately if nothing to do (shrank too far) */
11290 if( Hverb > 1 ){
11291 ININFO_message(" --------- lev=%d xwid=%d ywid=%d zwid=%d -- BREAK",lev,xwid,ywid,zwid) ;
11292 ININFO_message(" ......... xwid=%d ywid=%d zwid=%d ngmin=%d",xwid,ywid,zwid,ngmin) ;
11293 }
11294 break ;
11295 }
11296
11297 /* here, we are doing something, so don't let any width go below threshold */
11298
11299 Hlev_now = Hlev_final = lev ; /* in case we leave this loop somewhere below */
11300
11301 if( xwid < ngmin ) xwid = MIN(Hnx,ngmin);
11302 if( ywid < ngmin ) ywid = MIN(Hny,ngmin);
11303 if( zwid < ngmin ) zwid = MIN(Hnz,ngmin);
11304
11305 /* if we are almost to the smallest allowed patch, jump down to that size now */
11306
11307 if( !HAVE_HGRID ){ /* only if user didn't specify the grid schedule */
11308
11309 flev = xwid / (float)ngmin ; /* flev is the */
11310 glev = ywid / (float)ngmin ; if( flev < glev ) flev = glev ; /* largest ratio */
11311 glev = zwid / (float)ngmin ; if( flev < glev ) flev = glev ; /* of ?wid to ngmin */
11312 if( flev > 1.0f && flev*Hshrink <= 1.00001f ){ /* if next time we would shrink */
11313 if( xwid > ngmin ) xwid = ngmin ; /* below ngmin, shrink to ngmin NOW */
11314 if( ywid > ngmin ) ywid = ngmin ;
11315 if( zwid > ngmin ) zwid = ngmin ;
11316 levdone = 1 ; /* signal to exit when loop finishes [last pass] */
11317
11318 } else { /* determine if this is the last pass based on patch sizes */
11319
11320 iter = MAX(xwid,ywid) ; iter = MAX(iter,zwid) ; levdone = (iter == ngmin) ;
11321
11322 }
11323
11324 Hfinal = levdone ; /* is this the final level? */
11325
11326 } else { /* with '-gridlist', last pass is when grid list is used up */
11327
11328 levdone = Hfinal = (lev == Hgridlist_num-1) ;
11329
11330 }
11331
11332 /* step sizes for shifting the patches
11333 = half widths, so neighboring patches overlap */
11334
11335 xdel = (xwid-1)/2 ; if( xdel == 0 ) xdel = 1 ;
11336 ydel = (ywid-1)/2 ; if( ydel == 0 ) ydel = 1 ;
11337 zdel = (zwid-1)/2 ; if( zdel == 0 ) zdel = 1 ;
11338
11339 diii = xdel ; djjj = ydel ; dkkk = zdel ;
11340
11341 /* bbbottom and tttop indexes to warp over */
11342
11343 ibbb = imin-xdel/4-1 ; if( ibbb <= 0 ) ibbb = 1 ;
11344 jbbb = jmin-ydel/4-1 ; if( jbbb <= 0 ) jbbb = 1 ;
11345 kbbb = kmin-zdel/4-1 ; if( kbbb <= 0 ) kbbb = 1 ;
11346 ittt = imax+xdel/4+1 ; if( ittt >= Hnx-1 ) ittt = Hnx-2 ;
11347 jttt = jmax+ydel/4+1 ; if( jttt >= Hny-1 ) jttt = Hny-2 ;
11348 kttt = kmax+zdel/4+1 ; if( kttt >= Hnz-1 ) kttt = Hnz-2 ;
11349 if( Hnx == 1 ){ ibbb = ittt = 0 ; }
11350 if( Hny == 1 ){ jbbb = jttt = 0 ; }
11351 if( Hnz == 1 ){ kbbb = kttt = 0 ; }
11352
11353 zmode = zmode2 = cmode ; /* cubic patches from here on down (maybe) */
11354 do_qfinal = (Hfinal && Hqfinal) ;
11355 if( do_qfinal || Hqonly ){ zmode = zmode2 = qmode ; } /* OK, quintic */
11356 else if( Hqhard ){ zmode2 = qmode ; } /* OK, quintic on workhard */
11357
11358 num_xyz = ((int64_t)xwid) * ((int64_t)ywid) * ((int64_t)zwid) ; /* 06 Nov 2019 */
11359
11360 if( Huse_sincc ){ /* Huse_sincc is disabled at this time */
11361 if( num_xyz >= MIN_SINCC ){ cmode = zmode = zmode2 = MRI_SINCC ; }
11362 else { cmode = zmode = zmode2 = MRI_CUBIC_LITE ; }
11363 }
11364
11365 /* if patch is too small for quintic, make sure only cubic mode is on */
11366
11367 if( xwid < NGMIN_Q || ywid < NGMIN_Q || zwid < NGMIN_Q ) /* 28 Oct 2015 */
11368 zmode = zmode2 = cmode ;
11369 #if 0 && defined(ALLOW_BASIS5)
11370 if( (Hfinal && H5final) && xwid*ywid*zwid < NVOXMAX_PLUS ){
11371 if( xwid*ywid*zwid >= NGMIN_PLUS_3*NGMIN_PLUS_3*NGMIN_PLUS_3 && H5final >= 3 ){
11372 zmode = zmode2 = MRI_CUBIC_PLUS_3 ;
11373 } else if( xwid*ywid*zwid >= NGMIN_PLUS_2*NGMIN_PLUS_2*NGMIN_PLUS_2 && H5final >= 2 ){
11374 zmode = zmode2 = MRI_CUBIC_PLUS_2 ;
11375 } else if( xwid*ywid*zwid >= NGMIN_PLUS_1*NGMIN_PLUS_1*NGMIN_PLUS_1 && H5final >= 1 ){
11376 zmode = zmode2 = MRI_CUBIC_PLUS_1 ;
11377 }
11378 }
11379 #endif
11380
11381 (void)IW3D_load_energy(Haawarp) ; /* initialize energy field for penalty use */
11382
11383 /* workhard option allows for multiple optimization passes at each level,
11384 superhard option allows for multiple passes in each direction
11385 of sweeping thru the patches (there are 2 sweeping directions) */
11386
11387 nlevr = WORKHARD(lev) ? 2 : 1 ; /* number of passes over this level */
11388 nsup = SUPERHARD(lev) ? 2 : 1 ; /* number of passes in each sweep */
11389
11390 /* get ready to go */
11391
11392 PBLUR_BASE (1,xwid,1,ywid,1,zwid) ; /* progressive blur, if ordered */
11393 PBLUR_SOURCE(1,xwid,1,ywid,1,zwid) ;
11394 if( Hpblur_b > 0.0f || Hpblur_b > 0.0f ) Hfirsttime = 1 ;
11395 mri_free(Haasrcim) ; /* re-create the warped source image Haasrcim */
11396 Haasrcim = IW3D_warp_floatim( Haawarp, SRCIM, Himeth , 1.0f ) ;
11397
11398 if( cmode == MRI_SINCC ){ nlevr = 2 ; nsup = 1 ; } /* sincc mode ==> must work hard */
11399 /* which is why I disabled the */
11400 /* '-sincc' option in 3dQwarp. */
11401
11402 /* Announce the start of a new level! */
11403
11404 if( Hverb > 1 ){
11405 ININFO_message(" ......... lev=%d xwid=%d ywid=%d zwid=%d Hfac=%g penfac=%g %s %s [clock=%s]" ,
11406 lev,xwid,ywid,zwid,Hfactor,
11407 (Hpen_use)?Hpen_fff:0.0f ,
11408 (levdone ? "FINAL" : "\0") ,
11409 (nlevr > 1 ? "WORKHARD" : "\0") ,
11410 nice_time_string(NI_clock_time()) ) ;
11411 ININFO_message(" ......... volume = %d..%d %d..%d %d..%d" , ibbb,ittt , jbbb,jttt , kbbb,kttt ) ;
11412 } else if( Hverb == 1 ) {
11413 fprintf(stderr,"lev=%d patch=%dx%dx%d [clock=%s]",lev,xwid,ywid,zwid,nice_time_string(NI_clock_time()) ) ;
11414 }
11415 if( Hverb > 1 ) MEMORY_CHECK("continuing") ;
11416 Hdone = Hskipped = 0 ;
11417
11418 /* alternate the direction of sweeping at different levels;
11419 the purpose of this is so that one side of the box doesn't get favored */
11420
11421 itnum = 0 ; /* iteration/pass number */
11422
11423 /* Sweeping over patches proceeds from bot to top, ijk order */
11424
11425 if( lev%2 == 1 || nlevr > 1 ){ /* do this if an odd numbered level, or if workhard */
11426
11427 if( do_qfinal ) BALLOPT ;
11428 else if( nlevr > 1 ) BOXOPT ;
11429 for( isup=0 ; isup < nsup ; isup++ ){ /* working superhard? do this twice! */
11430 itnum++ ;
11431
11432 /** Loops over k=z, j=y, i=x directions, plopping down patches, and */
11433 /** optimizing. Note that the last patch in each direction might **/
11434 /** be resized to make sure it doesn't go outside the bounding box. **/
11435 /** Or it might be extended a little in size if the patch is close **/
11436 /** to the edge, so a tiny patch isn't needed in the next loop. **/
11437
11438 /** kdon = flag that the k direction is done, etc. */
11439
11440 for( kdon=0,kbot=kbbb ; !kdon ; kbot += dkkk ){ /* loop over z direction of patches */
11441 ktop = kbot+zwid-1; /* top edge of patch: maybe edit it down or up */
11442 if( ktop >= kttt ) { ktop = kttt; kbot = ktop+1-zwid; kdon=1; } /* downsize?*/
11443 else if( ktop >= kttt-zwid/4 ){ ktop = kttt; kdon=1; } /* extend patch to end */
11444
11445 for( jdon=0,jbot=jbbb ; !jdon ; jbot += djjj ){ /* y direction loop */
11446 jtop = jbot+ywid-1;
11447 if( jtop >= jttt ){ jtop = jttt; jbot = jtop+1-ywid; jdon=1; }
11448 else if( jtop >= jttt-ywid/4 ){ jtop = jttt; jdon=1; }
11449
11450 for( idon=0,ibot=ibbb ; !idon ; ibot += diii ){ /* x direction loop */
11451 itop = ibot+xwid-1;
11452 if( itop >= ittt ){ itop = ittt; ibot = itop+1-xwid; idon=1; }
11453 else if( itop >= ittt-xwid/4 ){ itop = ittt; idon=1; }
11454
11455 Hcostold = Hcost ; /* the last cost we saw */
11456 /*** actually try to make things better ***/
11457 /*** (we're from the government and we're here to help) ***/
11458 iter = IW3D_improve_warp( zmode , ibot,itop , jbot,jtop , kbot,ktop ) ;
11459 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11460 if( Hcost < Hstopcost ){ /* Whoa Nellie? */
11461 if( Hverb == 1 ) fprintf(stderr,"\n") ;
11462 ININFO_message(" ######### cost has reached stopping value") ;
11463 goto DoneDoneDone ;
11464 }
11465
11466 } /* end of sweep over i-direction (x) patches */
11467 } /* end of sweep over j-direction (y) patches */
11468 } /* end of sweep over k-direction (z) patches */
11469 } /* end isup loop (1 or 2) - for superhard fanatics */
11470 Hcostmid = Hcostend = Hcost ;
11471
11472 } /* end of bot to top, ijk order sweep case */
11473
11474 /* Instead, do the patch sweeping from top to bot, kji order.
11475 Otherwise, very similar to the above, so comments are fewer */
11476
11477 if( lev%2 == 0 || nlevr > 1 ){
11478
11479 if( nlevr > 1 && Hverb == 1 ) fprintf(stderr,":[cost=%.5f]:",Hcost) ;
11480 if( do_qfinal || nlevr > 1 ) BALLOPT ;
11481 zmodeX = (nlevr > 1) ? zmode2 : zmode ; /* for workhard mode */
11482
11483 for( isup=0 ; isup < nsup ; isup++ ){ /* superhard loops? */
11484 itnum++ ;
11485
11486 for( idon=0,itop=ittt ; !idon ; itop -= diii ){
11487 ibot = itop+1-xwid;
11488 if( ibot <= ibbb ){ ibot = ibbb; itop = ibot+xwid-1; idon=1; }
11489 else if( ibot <= ibbb+xwid/4 ){ ibot = ibbb; idon=1; }
11490
11491 for( jdon=0,jtop=jttt ; !jdon ; jtop -= djjj ){
11492 jbot = jtop+1-ywid;
11493 if( jbot <= jbbb ){ jbot = jbbb; jtop = jbot+ywid-1; jdon=1; }
11494 else if( jbot <= jbbb+ywid/4 ){ jbot = jbbb; jdon=1; }
11495
11496 for( kdon=0,ktop=kttt ; !kdon ; ktop -= dkkk ){
11497 kbot = ktop+1-zwid;
11498 if( kbot <= kbbb ){ kbot = kbbb; ktop = kbot+zwid-1; kdon=1; }
11499 else if( kbot <= kbbb+zwid/4 ){ kbot = kbbb; kdon=1; }
11500
11501 Hcostold = Hcost ;
11502 iter = IW3D_improve_warp( zmodeX , ibot,itop , jbot,jtop , kbot,ktop ) ;
11503 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11504 if( Hcost < Hstopcost ){
11505 if( Hverb == 1 ) fprintf(stderr,"\n") ;
11506 ININFO_message(" ######### cost has reached stopping value") ;
11507 goto DoneDoneDone ;
11508 }
11509 }
11510 }
11511 }
11512 } /* isup loop */
11513 Hcostend = Hcost ;
11514 } /* end of top to bot, kji order sweeping */
11515
11516 /* at this point, we have just about finished with this level's patches */
11517
11518 /* However, if nothing was done at this level,
11519 try one more time, centered on the autobox, just for form's sake.
11520 This can happen at lev=1 when none of the big patches overlap the
11521 weight volume significantly -- it's not common, but does happen. */
11522
11523 if( Hdone == 0 ){
11524 ibot = (imin+imax-xwid)/2 ; if( ibot < 0 ) ibot = 0 ;
11525 jbot = (jmin+jmax-ywid)/2 ; if( jbot < 0 ) jbot = 0 ;
11526 kbot = (kmin+kmax-zwid)/2 ; if( kbot < 0 ) kbot = 0 ;
11527 itop = ibot+xwid-1 ; if( itop >= Hnx ) itop = Hnx-1 ;
11528 jtop = jbot+ywid-1 ; if( jtop >= Hny ) jtop = Hny-1 ;
11529 ktop = kbot+zwid-1 ; if( ktop >= Hnz ) ktop = Hnz-1 ;
11530 Hforce = 1 ;
11531 iter = IW3D_improve_warp( zmode , ibot,itop , jbot,jtop , kbot,ktop ) ;
11532 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
11533 Hforce = 0 ;
11534 Hcostend = Hcost ;
11535 }
11536
11537 /* If saving things as we go, save this warp now */
11538
11539 if( Hsave_allwarps ){ /* 02 Jan 2015 */
11540 sprintf(warplab,"Lev%d.%04dx%04dx%04d",lev,xwid,ywid,zwid) ;
11541 HSAVE_ADDTO(Haawarp,warplab) ;
11542 }
11543
11544 /* print some summary of what happened at this level */
11545
11546 if( Hcostbeg > 666.0f ) Hcostbeg = Hfirstcost ;
11547 if( Hverb > 0 ){
11548 if( Hdone > 0 ){
11549 int_sextet sext = IW3D_warpbox(Haawarp,1.0f,0.03333f) ;
11550 fprintf(stderr,
11551 "done [cost:%.5f==>%.5f ; %d patches optimized, %d skipped, bbox=%d:%d,%d:%d,%d:%d]\n",
11552 Hcostbeg,Hcost,Hdone,Hskipped,
11553 sext.a,sext.b , sext.c,sext.d , sext.e,sext.f ) ;
11554 } else {
11555 fprintf(stderr," done [cost:%.5f ; all patches skipped]\n",Hcost) ;
11556 }
11557 }
11558 Hcostbeg = Hcost ;
11559
11560 } /*-- end of loop over levels of refinement --*/
11561
11562 DoneDoneDone: /* breakout target (e.g., for Hquitting) == end of the world */
11563
11564 OutWarp = IW3D_copy( Haawarp , 1.0f ) ; /* the result of all this work!!! */
11565 IW3D_cleanup_improvement() ;
11566 if( Hverb > 1 ) MEMORY_CHECK("finished") ;
11567
11568 RETURN(OutWarp) ;
11569 }
11570
11571 /*----------------------------------------------------------------------------*/
11572 /* This is the function called by 3dQwarp to warp source to base (s2bim)!
11573 It returns the index warp AND the warped source image in a gift wrapping.
11574 *//*--------------------------------------------------------------------------*/
11575
11576 static void *S2BIM_iwarp = NULL ; /* initial warp? */
11577 static int S2BIM_ilev = 0 ; /* initial level */
11578 static int S2BIM_mlev = 666 ; /* final level */
11579
IW3D_warp_s2bim(MRI_IMAGE * bim,MRI_IMAGE * wbim,MRI_IMAGE * sim,int interp_code,int meth_code,int warp_flags)11580 Image_plus_Warp * IW3D_warp_s2bim( MRI_IMAGE *bim , MRI_IMAGE *wbim , MRI_IMAGE *sim,
11581 int interp_code , int meth_code , int warp_flags )
11582 {
11583 IndexWarp3D *Swarp ;
11584 MRI_IMAGE *outim ;
11585 Image_plus_Warp *imww ;
11586
11587 ENTRY("IW3D_warp_s2bim") ;
11588
11589 WO_iwarp = S2BIM_iwarp ; Hlev_start = S2BIM_ilev ; Hlev_end = S2BIM_mlev ;
11590 Hnpar_sum = 0 ;
11591
11592 /* the user can set the Hshrink factor (default=0.749999) but why bother? */
11593
11594 Hshrink = AFNI_numenv("AFNI_WARPOMATIC_SHRINK") ;
11595 if( Hshrink > 1.0f ) Hshrink = 1.0f / Hshrink ;
11596 if( Hshrink < 0.444f || Hshrink > 0.888f ) Hshrink = 0.749999f ;
11597 else ININFO_message(" -- Hshrink set to %.6f",Hshrink) ;
11598
11599 /*----- virtually all the CPU time is spent in this next puppy -----*/
11600
11601 Swarp = IW3D_warpomatic( bim , wbim , sim , meth_code , warp_flags ) ;
11602
11603 /*-- compute the warped source image --*/
11604
11605 outim = IW3D_warp_floatim( Swarp, sim , interp_code , 1.0f ) ;
11606
11607 /*-- wrap them up in a nice Hanukkah package and send them home --*/
11608
11609 imww = (Image_plus_Warp *)malloc(sizeof(Image_plus_Warp)) ;
11610 imww->im = outim ;
11611 imww->warp = Swarp ;
11612
11613 RETURN(imww) ;
11614 }
11615
11616 #if 0
11617 /*----------------------------------------------------------------------------*/
11618 /*** THIS CODE IS NOT READY YET (maybe never) ***/
11619
11620 #define WOMA_NONE 0
11621 #define WOMA_MAT44 1
11622 #define WOMA_DSET 2
11623
11624 THD_3dim_dataset * THD_warpomatic( THD_3dim_dataset *bset ,
11625 THD_3dim_dataset *sset ,
11626 int inicode , void *iniwarp ,
11627 byte *bmask , byte *emask )
11628 {
11629 int nx,ny,nz ;
11630 IndexWarp3D *iniwww=NULL , *outwww=NULL ;
11631 MRI_IMAGE *bim , *sim , *wbim ;
11632 THD_3dim_dataset *outdset=NULL ;
11633
11634 ENTRY("THD_warpomatic") ;
11635
11636 if( !ISVALID_DSET(bset) || !ISVALID_DSET(sset) || EQUIV_DSETS(bset,sset) ){
11637 ERROR_message("bad input datasets to THD_warpomatic") ;
11638 RETURN(NULL) ;
11639 }
11640
11641 nx = DSET_NX(bset) ; ny = DSET_NY(bset) ; nz = DSET_NZ(bset) ;
11642
11643 if( (nz == 1 && DSET_NZ(sset) > 1) ||
11644 (nz > 1 && DSET_NZ(sset) == 1) ){
11645 ERROR_message("exactly 1 input dataset to THD_warpomatic is 2D") ;
11646 RETURN(NULL) ;
11647 }
11648
11649 switch( inicode ){
11650 default:
11651 ERROR_message("bad inicode in THD_warpomatic: %d",inicode) ;
11652 RETURN(NULL) ;
11653 break ;
11654
11655 case WOMA_NONE:
11656 iniwww = IW3D_create(nx,ny,nz) ; /* identity warp */
11657 IW3D_adopt_dataset(iniwww,bset) ;
11658 break ;
11659
11660 case WOMA_DSET:{
11661 THD_3dim_dataset *iniset = (THD_3dim_dataset *)iniwarp ;
11662 if( !ISVALID_DSET(iniset) || DSET_NVALS(iniset) < 3 ){
11663 ERROR_message("bad iniwarp dataset in THD_warpomatic") ; RETURN(NULL) ;
11664 }
11665 if( !EQUIV_GRIDS(bset,iniset) ){
11666 ERROR_message("bad iniwarp dataset grid in THD_warpomatic") ; RETURN(NULL) ;
11667 }
11668 iniwww = IW3D_from_dataset( iniset , 0 , 0 ) ;
11669 DSET_unload(iniset) ;
11670 if( iniwww == NULL ){
11671 ERROR_message("Can't use iniwarp dataset in THD_warpomatic") ; RETURN(NULL) ;
11672 }
11673 }
11674 break ;
11675
11676 case WOMA_MAT44:{
11677 mat44 *inimat = (mat44 *)iniwarp ;
11678 iniwww = IW3D_from_mat44( *inimat , bset ) ;
11679 if( iniwww == NULL ){
11680 ERROR_message("Can't use iniwarp mat44 in THD_warpomatic") ; RETURN(NULL) ;
11681 }
11682 }
11683 break ;
11684 }
11685
11686 WO_iwarp = iniwww ;
11687
11688 DSET_load(bset) ;
11689 if( !DSET_LOADED(bset) ){
11690 IW3D_destroy(iniwww) ; WO_iwarp = NULL ;
11691 ERROR_message("Can't load base dataset in THD_warpomatic") ; RETURN(NULL) ;
11692 }
11693 bim = THD_extract_float_brick(0,bset) ; DSET_unload(bset) ;
11694
11695 DSET_load(sset) ;
11696 if( !DSET_LOADED(sset) ){
11697 IW3D_destroy(iniwww) ; WO_iwarp = NULL ; mri_free(bim) ;
11698 ERROR_message("Can't load source dataset in THD_warpomatic") ; RETURN(NULL) ;
11699 }
11700 if( EQUIV_GRIDS(bset,sset) ){
11701 sim = THD_extract_float_brick(0,sset) ;
11702 } else {
11703 THD_3dim_dataset *wset , *oset ;
11704 wset = IW3D_to_dataset( iniwww, "Qadqop" ) ;
11705 oset = THD_nwarp_dataset( wset, sset, bset, "Mercotan", MRI_LINEAR,Himeth, 0.0f, 1.0f, 1 , NULL ) ;
11706 DSET_delete(wset) ;
11707 sim = mri_copy(DSET_BRICK(oset,0)) ;
11708 DSET_delete(oset) ;
11709 }
11710
11711 RETURN(outdset) ;
11712 }
11713 #endif
11714
11715 #endif /*(Q8)*/ /*############################################################*/
11716
11717 #if 1
11718 /*============================================================================*/
11719 /** (Q9) All the above functions copied and edited for plusminus warping :) **/
11720 /*============================================================================*/
11721
11722 /*****--------------------------------------------------------------------*****/
11723 /*****||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*****/
11724 /*****--------------------------------------------------------------------*****/
11725 /***** Optimization of 'plusminus' warps: *****/
11726 /***** matching base(x-a(x)) = source(x+a(x)) *****/
11727 /***** instead of base(x) = source(x+a(x)) *****/
11728 /*****--------------------------------------------------------------------*****/
11729 /*---- There is a lot of duplicated+edited code below, mostly uncommented ----*/
11730 /*---- If you want to understand, first grok the parent functions! ----*/
11731 /*****--------------------------------------------------------------------*****/
11732
11733 static float *Hwval_plus = NULL ;
11734 static float *Hwval_minus = NULL ;
11735
11736 static MRI_IMAGE *Haasrcim_plus = NULL ; /* warped source image (global) */
11737 static MRI_IMAGE *Haabasim_minus = NULL ; /* warped base image (global) */
11738
11739 /*----------------------------------------------------------------------------*/
11740
Hwarp_apply_plusminus(float * valp,float * valm)11741 void Hwarp_apply_plusminus( float *valp , float *valm )
11742 {
11743 int nbx,nby,nbz , nbxy,nbxyz , nAx,nAy,nAz , nAx1,nAy1,nAz1 , nAxy ;
11744 float nAxh,nAyh,nAzh ;
11745 float *hxd,*hyd,*hzd , *Axd,*Ayd,*Azd , *sarp,*sarm , *bxd,*byd,*bzd ;
11746 void (*Heval)(int,float *,float *,float *) = NULL ; /* compute Hwarp at one index */
11747
11748 ENTRY("Hwarp_apply_plusminus") ;
11749
11750 hxd = Hwarp->xd ; hyd = Hwarp->yd ; hzd = Hwarp->zd ; /* Hwarp delta */
11751 Axd = Haawarp->xd; Ayd = Haawarp->yd; Azd = Haawarp->zd; /* Haawarp */
11752 bxd = AHwarp->xd ; byd = AHwarp->yd ; bzd = AHwarp->zd ; /* AHwarp delta */
11753
11754 if( WARP_IS_QUINTIC(Hbasis_code) ){ nbx = nbqx ; nby = nbqy ; nbz = nbqz ; }
11755 else { nbx = nbcx ; nby = nbcy ; nbz = nbcz ; }
11756 nbxy = nbx*nby ; nbxyz = nbxy*nbz ;
11757
11758 if( Hbasis_code == MRI_CUBIC ){
11759 Heval = (bbbcar == NULL) ? HCwarp_eval_A : HCwarp_eval_B ;
11760 } else if( Hbasis_code == MRI_CUBIC_LITE ){
11761 Heval = (bbbcar == NULL) ? HCwarp_eval_AMM : HCwarp_eval_BMM ;
11762 } else if( Hbasis_code == MRI_QUINTIC ){
11763 Heval = (bbbqar == NULL) ? HQwarp_eval_A : HQwarp_eval_B ;
11764 } else if( Hbasis_code == MRI_QUINTIC_LITE ){
11765 Heval = (bbbqar == NULL) ? HQwarp_eval_AMM : HQwarp_eval_BMM ;
11766 #ifdef ALLOW_BASIS5
11767 } else if( Hbasis_code == MRI_CUBIC_PLUS_1 ){
11768 Heval = HCwarp_eval_B_basis345 ;
11769 } else if( Hbasis_code == MRI_CUBIC_PLUS_2 ){
11770 Heval = HCwarp_eval_B_basis345 ;
11771 } else if( Hbasis_code == MRI_CUBIC_PLUS_3 ){
11772 Heval = HCwarp_eval_B_basis345 ;
11773 #endif
11774 }
11775
11776 nAx = Haawarp->nx; nAy = Haawarp->ny; nAz = Haawarp->nz; nAxy = nAx*nAy;
11777 nAx1 = nAx-1 ; nAy1 = nAy-1 ; nAz1 = nAz-1 ;
11778 nAxh = nAx-0.501f ; nAyh = nAy-0.501f ; nAzh = nAz-0.501f ;
11779
11780 sarp = MRI_FLOAT_PTR(SRCIM) ; /* source image array */
11781 sarm = MRI_FLOAT_PTR(BASIM) ; /* base image array */
11782
11783 STATUS("start loop") ;
11784
11785 #undef IJK
11786 #define IJK(i,j,k) ((i)+(j)*nAx+(k)*nAxy)
11787
11788 #undef XINT
11789 #define XINT(aaa,j,k) wt_00*aaa[IJK(ix_00,j,k)]+wt_p1*aaa[IJK(ix_p1,j,k)]
11790
11791 AFNI_OMP_START ;
11792 #pragma omp parallel
11793 { int ii,jj,kk , qq , need_val ;
11794 float xq,yq,zq ;
11795 float fx,fy,fz , ix,jy,kz ;
11796 int ix_00,ix_p1 , jy_00,jy_p1 , kz_00,kz_p1 ;
11797 float wt_00,wt_p1 ;
11798 float f_j00_k00, f_jp1_k00, f_j00_kp1, f_jp1_kp1, f_k00, f_kp1 ;
11799 float g_j00_k00, g_jp1_k00, g_j00_kp1, g_jp1_kp1, g_k00, g_kp1 ;
11800 float h_j00_k00, h_jp1_k00, h_j00_kp1, h_jp1_kp1, h_k00, h_kp1 ;
11801 #ifdef USE_OMP
11802 int ith = omp_get_thread_num() ;
11803 #else
11804 int ith = 0 ;
11805 #endif
11806
11807 #pragma ivdep /* for Intel icc compiler */
11808 #pragma omp for
11809 for( qq=0 ; qq < nbxyz ; qq++ ){ /* for each voxel in the patch */
11810 ii = qq % nbx; kk = qq / nbxy; jj = (qq-kk*nbxy) / nbx; /* patch indexes */
11811
11812 /* determine if we actually need this value (is it in the mask?) */
11813
11814 need_val = ( Hbmask[IJK(ii+Hibot,jj+Hjbot,kk+Hkbot)] != 0 ) ;
11815
11816 if( !need_val && !need_AH ){ valp[qq] = valm[qq] = 0.0f; continue; }
11817
11818 Heval(qq,hxd+qq,hyd+qq,hzd+qq) ; /* if warp not loaded, evaluate it now */
11819
11820 /* get Hwarp-ed indexes into Haawarp; e.g.,
11821 xq = Hibot + ii + hxd[qq]
11822 because the Hwarp output index warp location is computed as
11823 Hwarp_x(x,y,z) = x + hxd
11824 and we also have to add in Hibot to get a global index for use in Haawarp */
11825
11826 #if 0
11827 xq = Hibot + ii + hxd[qq] ; if( xq < -0.499f ) xq = -0.499f; else if( xq > nAxh ) xq = nAxh;
11828 yq = Hjbot + jj + hyd[qq] ; if( yq < -0.499f ) yq = -0.499f; else if( yq > nAyh ) yq = nAyh;
11829 zq = Hkbot + kk + hzd[qq] ; if( zq < -0.499f ) zq = -0.499f; else if( zq > nAzh ) zq = nAzh;
11830 ix = floorf(xq) ; fx = xq - ix ;
11831 jy = floorf(yq) ; fy = yq - jy ;
11832 kz = floorf(zq) ; fz = zq - kz ;
11833 ix_00 = ix ; ix_p1 = ix_00+1 ; CLIP(ix_00,nAx1) ; CLIP(ix_p1,nAx1) ;
11834 jy_00 = jy ; jy_p1 = jy_00+1 ; CLIP(jy_00,nAy1) ; CLIP(jy_p1,nAy1) ;
11835 kz_00 = kz ; kz_p1 = kz_00+1 ; CLIP(kz_00,nAz1) ; CLIP(kz_p1,nAz1) ;
11836 #else
11837 xq = Hibot + ii + hxd[qq] ; ix = (int)(xq) ; fx = xq - ix ;
11838 yq = Hjbot + jj + hyd[qq] ; jy = (int)(yq) ; fy = yq - jy ;
11839 zq = Hkbot + kk + hzd[qq] ; kz = (int)(zq) ; fz = zq - kz ;
11840 ix_00 = ix ; ix_p1 = ix_00+1 ; QLIP(ix_00,nAx1) ; QLIP(ix_p1,nAx1) ;
11841 jy_00 = jy ; jy_p1 = jy_00+1 ; QLIP(jy_00,nAy1) ; QLIP(jy_p1,nAy1) ;
11842 kz_00 = kz ; kz_p1 = kz_00+1 ; QLIP(kz_00,nAz1) ; QLIP(kz_p1,nAz1) ;
11843 #endif
11844
11845 /* linearly interpolate in Haawarp to get Haawarp displacements */
11846
11847 wt_00 = 1.0f-fx ; wt_p1 = fx ; /* x interpolations */
11848 f_j00_k00 = XINT(Axd,jy_00,kz_00) ; f_jp1_k00 = XINT(Axd,jy_p1,kz_00) ;
11849 f_j00_kp1 = XINT(Axd,jy_00,kz_p1) ; f_jp1_kp1 = XINT(Axd,jy_p1,kz_p1) ;
11850 g_j00_k00 = XINT(Ayd,jy_00,kz_00) ; g_jp1_k00 = XINT(Ayd,jy_p1,kz_00) ;
11851 g_j00_kp1 = XINT(Ayd,jy_00,kz_p1) ; g_jp1_kp1 = XINT(Ayd,jy_p1,kz_p1) ;
11852 h_j00_k00 = XINT(Azd,jy_00,kz_00) ; h_jp1_k00 = XINT(Azd,jy_p1,kz_00) ;
11853 h_j00_kp1 = XINT(Azd,jy_00,kz_p1) ; h_jp1_kp1 = XINT(Azd,jy_p1,kz_p1) ;
11854
11855 wt_00 = 1.0f-fy ; /* y interpolations */
11856 f_k00 = wt_00 * f_j00_k00 + fy * f_jp1_k00 ;
11857 f_kp1 = wt_00 * f_j00_kp1 + fy * f_jp1_kp1 ;
11858 g_k00 = wt_00 * g_j00_k00 + fy * g_jp1_k00 ;
11859 g_kp1 = wt_00 * g_j00_kp1 + fy * g_jp1_kp1 ;
11860 h_k00 = wt_00 * h_j00_k00 + fy * h_jp1_k00 ;
11861 h_kp1 = wt_00 * h_j00_kp1 + fy * h_jp1_kp1 ;
11862
11863 wt_00 = 1.0f-fz ; /* z interpolations */
11864
11865 /* bxd = x-displacments for AHwarp = Awarp(Hwarp())
11866 xq = index in srcim for output interpolation to get val */
11867
11868 bxd[qq] = wt_00 * f_k00 + fz * f_kp1 + hxd[qq] ;
11869 byd[qq] = wt_00 * g_k00 + fz * g_kp1 + hyd[qq] ;
11870 bzd[qq] = wt_00 * h_k00 + fz * h_kp1 + hzd[qq] ;
11871
11872 /* if not in the global mask, don't bother to compute val */
11873
11874 if( !need_val ){ valp[qq] = valm[qq] = 0.0f; continue; }
11875
11876 /** interpolate for POSITIVE displacements **/
11877
11878 xq = bxd[qq]+ii+Hibot ; yq = byd[qq]+jj+Hjbot ; zq = bzd[qq]+kk+Hkbot ;
11879
11880 if( xq < -0.499f ) xq = -0.499f; else if( xq > nAxh ) xq = nAxh;
11881 if( yq < -0.499f ) yq = -0.499f; else if( yq > nAyh ) yq = nAyh;
11882 if( zq < -0.499f ) zq = -0.499f; else if( zq > nAzh ) zq = nAzh;
11883
11884 if( Himeth == MRI_NN ){
11885 ix_00 = (int)(xq+0.5f) ;
11886 jy_00 = (int)(yq+0.5f) ;
11887 kz_00 = (int)(zq+0.5f) ;
11888 valp[qq] = sarp[IJK(ix_00,jy_00,kz_00)] ;
11889 } else {
11890 ix = floorf(xq) ; fx = xq - ix ;
11891 jy = floorf(yq) ; fy = yq - jy ;
11892 kz = floorf(zq) ; fz = zq - kz ;
11893 ix_00 = ix ; ix_p1 = ix_00+1 ; CLIP(ix_00,nAx1) ; CLIP(ix_p1,nAx1) ;
11894 jy_00 = jy ; jy_p1 = jy_00+1 ; CLIP(jy_00,nAy1) ; CLIP(jy_p1,nAy1) ;
11895 kz_00 = kz ; kz_p1 = kz_00+1 ; CLIP(kz_00,nAz1) ; CLIP(kz_p1,nAz1) ;
11896
11897 wt_00 = 1.0f-fx ; wt_p1 = fx ; /* x interpolations */
11898 f_j00_k00 = XINT(sarp,jy_00,kz_00) ; f_jp1_k00 = XINT(sarp,jy_p1,kz_00) ;
11899 f_j00_kp1 = XINT(sarp,jy_00,kz_p1) ; f_jp1_kp1 = XINT(sarp,jy_p1,kz_p1) ;
11900 wt_00 = 1.0f-fy ; /* y interpolations */
11901 f_k00 = wt_00 * f_j00_k00 + fy * f_jp1_k00 ;
11902 f_kp1 = wt_00 * f_j00_kp1 + fy * f_jp1_kp1 ;
11903 valp[qq] = (1.0f-fz) * f_k00 + fz * f_kp1 ; /* z interpolation = output */
11904 }
11905
11906 /** duplicate for NEGATIVE displacements **/
11907
11908 xq = -bxd[qq]+ii+Hibot ; yq = -byd[qq]+jj+Hjbot ; zq = -bzd[qq]+kk+Hkbot ;
11909
11910 if( xq < -0.499f ) xq = -0.499f; else if( xq > nAxh ) xq = nAxh;
11911 if( yq < -0.499f ) yq = -0.499f; else if( yq > nAyh ) yq = nAyh;
11912 if( zq < -0.499f ) zq = -0.499f; else if( zq > nAzh ) zq = nAzh;
11913
11914 if( Himeth == MRI_NN ){
11915 ix_00 = (int)(xq+0.5f) ;
11916 jy_00 = (int)(yq+0.5f) ;
11917 kz_00 = (int)(zq+0.5f) ;
11918 valm[qq] = sarm[IJK(ix_00,jy_00,kz_00)] ;
11919 } else {
11920 ix = floorf(xq) ; fx = xq - ix ;
11921 jy = floorf(yq) ; fy = yq - jy ;
11922 kz = floorf(zq) ; fz = zq - kz ;
11923 ix_00 = ix ; ix_p1 = ix_00+1 ; CLIP(ix_00,nAx1) ; CLIP(ix_p1,nAx1) ;
11924 jy_00 = jy ; jy_p1 = jy_00+1 ; CLIP(jy_00,nAy1) ; CLIP(jy_p1,nAy1) ;
11925 kz_00 = kz ; kz_p1 = kz_00+1 ; CLIP(kz_00,nAz1) ; CLIP(kz_p1,nAz1) ;
11926
11927 wt_00 = 1.0f-fx ; wt_p1 = fx ; /* x interpolations */
11928 f_j00_k00 = XINT(sarm,jy_00,kz_00) ; f_jp1_k00 = XINT(sarm,jy_p1,kz_00) ;
11929 f_j00_kp1 = XINT(sarm,jy_00,kz_p1) ; f_jp1_kp1 = XINT(sarm,jy_p1,kz_p1) ;
11930 wt_00 = 1.0f-fy ; /* y interpolations */
11931 f_k00 = wt_00 * f_j00_k00 + fy * f_jp1_k00 ;
11932 f_kp1 = wt_00 * f_j00_kp1 + fy * f_jp1_kp1 ;
11933 valm[qq] = (1.0f-fz) * f_k00 + fz * f_kp1 ; /* z interpolation = output */
11934 }
11935
11936 } /* end of for loop */
11937 } /* end of parallel stuff */
11938 AFNI_OMP_END ;
11939
11940 AFNI_do_nothing() ;
11941 EXRETURN ;
11942 }
11943
11944 /*----------------------------------------------------------------------------*/
11945
IW3D_scalar_costfun_plusminus(int npar,double * dpar)11946 double IW3D_scalar_costfun_plusminus( int npar , double *dpar )
11947 {
11948 double cost=0.0 ; int ii ;
11949
11950 /* compute Hwarp given the params */
11951
11952 if( Hparmap != NULL ){
11953 for( ii=0 ; ii < Hnpar ; ii++ ) Hpar[ii] = 0.0f ;
11954 for( ii=0 ; ii < npar ; ii++ ) Hpar[ Hparmap[ii] ] = (float)dpar[ii] ;
11955 } else {
11956 for( ii=0 ; ii < Hnpar ; ii++ ){
11957 Hpar[ii] = (float)dpar[ii] ;
11958 if( !isfinite(Hpar[ii]) ){
11959 ERROR_message("bad Hpar[%d]=%g dpar=%g",ii,Hpar[ii],dpar[ii]) ;
11960 Hpar[ii] = dpar[ii] = 0.0 ;
11961 }
11962 }
11963 }
11964
11965 /* compute warped image over the patch, into Hwval array */
11966
11967 Hwarp_apply_plusminus(Hwval_plus,Hwval_minus) ;
11968
11969 /* compute the rest of the cost function */
11970
11971 cost = INCOR_evaluate( Hincor , Hnval , Hwval_minus , Hwval_plus ,
11972 (Haawt != NULL ) ? Haawt : MRI_FLOAT_PTR(Hwtim) ) ;
11973 if( Hnegate ) cost = -cost ;
11974 Hcostt = cost ;
11975 if( save_H_zero ){ Hcostt_zero = cost ; save_H_zero = 0 ; } /* 10 Mar 2020 */
11976
11977 if( !isfinite(cost) ){
11978 ERROR_message("bad Warpomatic cost = %g -- input parameters:",cost) ;
11979 for( ii=0 ; ii < npar ; ii++ ) fprintf(stderr," %g",dpar[ii]) ;
11980 fprintf(stderr,"\n") ;
11981 }
11982
11983 if( Hpen_use ){
11984 Hpenn = HPEN_penalty() ; cost += Hpenn ; /* penalty is saved in Hpenn */
11985 } else {
11986 Hpenn = 0.0f ;
11987 }
11988
11989 if( Hfirsttime ){
11990 if( Hverb ) fprintf(stderr,"[first cost=%.5f]%c",cost , ((Hverb>1) ? '\n' : ' ') ) ;
11991 Hfirsttime = 0 ; Hfirstcost = (float)cost ;
11992 }
11993
11994 return cost ;
11995 }
11996
11997 /*----------------------------------------------------------------------------*/
11998 /* Improve one patch, the plusminus way */
11999
IW3D_improve_warp_plusminus(int warp_code,int ibot,int itop,int jbot,int jtop,int kbot,int ktop)12000 int IW3D_improve_warp_plusminus( int warp_code ,
12001 int ibot, int itop,
12002 int jbot, int jtop, int kbot, int ktop )
12003 {
12004 MRI_IMAGE *warpim ;
12005 int nxh,nyh,nzh , ii,jj,kk , iter,itmax,qq,pp , nwb , nball ;
12006 float *wbfar , wsum ; double prad ;
12007 double *parvec, *xbot,*xtop ;
12008 float *sarp,*sarm , *Axd,*Ayd,*Azd,*Aje,*Ase , *bxd,*byd,*bzd,*bje,*bse , jt,st ;
12009 int ballopt = (SC_BALL == powell_newuoa_get_con()) ; /* 30 Oct 2015 */
12010
12011 ENTRY("IW3D_improve_warp_plusminus") ;
12012
12013 /*-- setup local region for Hwarp --*/
12014
12015 CLIP(ibot,Hnx-1) ; CLIP(itop,Hnx-1) ;
12016 CLIP(jbot,Hny-1) ; CLIP(jtop,Hny-1) ;
12017 CLIP(kbot,Hnz-1) ; CLIP(ktop,Hnz-1) ;
12018
12019 nxh = itop-ibot+1 ; nyh = jtop-jbot+1 ; nzh = ktop-kbot+1 ;
12020
12021 if( nxh < NGMIN && nyh < NGMIN && nzh < NGMIN ){ Hskipped++; RETURN(0); }
12022
12023 Hibot = ibot ; Hitop = itop ; /* index range of the patch we're working on */
12024 Hjbot = jbot ; Hjtop = jtop ;
12025 Hkbot = kbot ; Hktop = ktop ;
12026
12027 /* test if this region has enough "weight" to process */
12028
12029 Hnval = nxh*nyh*nzh ;
12030
12031 nball = (!WARP_IS_QUINTIC(warp_code)) ? 15 : 19 ;
12032 if( nxh < nball || nyh < nball || nzh < nball ){ /* 10 Feb 2017 */
12033 ballopt = 1 ; BALLOPT ;
12034 }
12035
12036 wbfar = MRI_FLOAT_PTR(Hwtim) ; wsum = 0.0f ;
12037 for( nwb=0,kk=kbot ; kk <= ktop ; kk++ ){
12038 for( jj=jbot ; jj <= jtop ; jj++ ){
12039 for( ii=ibot ; ii <= itop ; ii++ ){
12040 qq = ii + jj*Hnx + kk*Hnxy ;
12041 if( Hbmask[qq] ){ wsum += wbfar[qq] ; nwb++ ; }
12042 }}}
12043 if( !Hforce && (nwb < 0.333f*Hnval || wsum < 0.222f*Hnval*Hwbar) ){ /* too light for us */
12044 if( Hverb > 2 )
12045 ININFO_message(
12046 " %7s patch %03d..%03d %03d..%03d %03d..%03d : skipping (%.1f%% inmask %.1f%% weight)" ,
12047 WARP_CODE_STRING(warp_code) ,
12048 ibot,itop, jbot,jtop, kbot,ktop ,
12049 (100.0f*nwb)/Hnval , (100.0f*wsum)/(Hnval*Hwbar) ) ;
12050 Hskipped++ ; RETURN(0) ;
12051 }
12052
12053 /*-- setup the basis functions for Hwarping --*/
12054
12055 switch( warp_code ){
12056
12057 case MRI_SINCC: /* Nov 2018 */
12058 Hbasis_code = MRI_SINCC ;
12059 Hbasis_parmax = 0.1666*Hfactor ; /* max displacement from 1 function */
12060 if( ballopt ) Hbasis_parmax = 0.2345*Hfactor ;
12061 Hnpar = 3 ; /* number of params for local warp */
12062 prad = 0.333 ; /* NEWUOA initial radius */
12063 HSCwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
12064 break ;
12065
12066 default:
12067 case MRI_CUBIC:
12068 Hbasis_code = MRI_CUBIC ; /* 3rd order polynomials */
12069 Hbasis_parmax = 0.0280*Hfactor ; /* max displacement from 1 function */
12070 if( ballopt ) Hbasis_parmax = 0.0790*Hfactor ; /* 13 Jan 2015 */
12071 Hnpar = 24 ; /* number of params for local warp */
12072 prad = 0.333 ; /* NEWUOA initial radius */
12073 HCwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
12074 break ;
12075
12076 case MRI_CUBIC_LITE: /* Dec 2018 */
12077 Hbasis_code = MRI_CUBIC_LITE ; /* 3rd order polynomials */
12078 Hbasis_parmax = 0.0421*Hfactor ; /* max displacement from 1 function */
12079 if( ballopt ) Hbasis_parmax = 0.1141*Hfactor ;
12080 Hnpar = 12 ; /* number of params for local warp */
12081 prad = 0.333 ; /* NEWUOA initial radius */
12082 HCwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
12083 break ;
12084
12085 case MRI_QUINTIC:
12086 Hbasis_code = MRI_QUINTIC ; /* 5th order polynomials */
12087 Hbasis_parmax = 0.0099*Hfactor ;
12088 if( ballopt ) Hbasis_parmax = 0.0611*Hfactor ; /* 13 Jan 2015 */
12089 Hnpar = 81 ;
12090 prad = 0.333 ;
12091 HQwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ;
12092 break ;
12093
12094 case MRI_QUINTIC_LITE: /* Dec 2018 */
12095 Hbasis_code = MRI_QUINTIC_LITE ; /* 5th order polynomials */
12096 Hbasis_parmax = 0.0267*Hfactor ; /* max displacement from 1 function */
12097 if( ballopt ) Hbasis_parmax = 0.1111*Hfactor ;
12098 Hnpar = 30 ; /* number of params for local warp */
12099 prad = 0.333 ; /* NEWUOA initial radius */
12100 HQwarp_setup_basis( nxh,nyh,nzh, Hgflags ) ; /* setup HCwarp_load */
12101 break ;
12102
12103 #ifdef ALLOW_BASIS5 /* 05 Nov 2015 */
12104 case MRI_CUBIC_PLUS_1: /* basis3 */
12105 BALLOPT ; ballopt = 1 ;
12106 Hbasis_code = MRI_CUBIC_PLUS_1 ;
12107 Hbasis_parmax = 0.0444 ;
12108 prad = 0.333 ;
12109 HCwarp_setup_basis345( nxh,nyh,nzh, Hgflags , 1 ) ;
12110 Hnpar = 3*H5nparm ;
12111 break ;
12112
12113 case MRI_CUBIC_PLUS_2: /* basis4 */
12114 BALLOPT ; ballopt = 1 ;
12115 Hbasis_code = MRI_CUBIC_PLUS_2 ;
12116 Hbasis_parmax = 0.0444 ;
12117 prad = 0.333 ;
12118 HCwarp_setup_basis345( nxh,nyh,nzh, Hgflags , 2 ) ;
12119 Hnpar = 3*H5nparm ;
12120 break ;
12121
12122 case MRI_CUBIC_PLUS_3: /* basis5 */
12123 BALLOPT ; ballopt = 1 ;
12124 Hbasis_code = MRI_CUBIC_PLUS_3 ;
12125 Hbasis_parmax = 0.0432 ;
12126 prad = 0.333 ;
12127 HCwarp_setup_basis345( nxh,nyh,nzh, Hgflags , 3 ) ;
12128 Hnpar = 3*H5nparm ;
12129 break ;
12130 #endif
12131 }
12132
12133 /* skip if not enough points for number of parameters [07 Apr 2016] */
12134
12135 if( nwb < 5*Hnparmap ){ /* Hnparmap was set in a *setup_basis* just above */
12136 if( Hverb > 2 )
12137 ININFO_message(
12138 " %s patch %03d..%03d %03d..%03d %03d..%03d : skipping (%d voxels inmask vs %d parameters)" ,
12139 WARP_CODE_STRING(warp_code) ,
12140 ibot,itop, jbot,jtop, kbot,ktop ,
12141 nwb , Hnparmap ) ;
12142 Hskipped++ ; RETURN(0) ;
12143 }
12144
12145 Hdox = !(Hflags & NWARP_NOXDIS_FLAG) ; /* do the x direction? */
12146 Hdoy = !(Hflags & NWARP_NOYDIS_FLAG) ; /* y? */
12147 Hdoz = !(Hflags & NWARP_NOZDIS_FLAG) ; /* z? */
12148
12149 Hpar = (float *)realloc(Hpar,sizeof(float)*Hnpar) ;
12150 for( ii=0 ; ii < Hnpar ; ii++ ) Hpar[ii] = 0.0f ;
12151 Hxpar = Hpar ;
12152 Hypar = Hxpar + (Hnpar/3) ;
12153 Hzpar = Hypar + (Hnpar/3) ;
12154
12155 /*-- create space for local warped image values --*/
12156
12157 Hwval_plus = (float *)realloc(Hwval_plus ,sizeof(float)*Hnval) ;
12158 Hwval_minus = (float *)realloc(Hwval_minus,sizeof(float)*Hnval) ;
12159
12160 /*-- setup to do incremental 'correlation' on the local region --*/
12161
12162 INCOR_destroy(Hincor) ;
12163 Hincor = INCOR_create( Hmatch_code , Hmpar ) ;
12164
12165 FREEIFNN(Haawt) ;
12166
12167 need_AH = Hpen_use ;
12168 if( Hpen_use ) Hpen_sum = 0.0 ;
12169
12170 #undef RESTORE_WBFAR
12171 #define RESTORE_WBFAR \
12172 do{ for( pp=0,kk=kbot ; kk <= ktop ; kk++ ) \
12173 for( jj=jbot ; jj <= jtop ; jj++ ) \
12174 for( ii=ibot ; ii <= itop ; ii++,pp++ ) \
12175 wbfar[ii+jj*Hnx+kk*Hnxy] = Haawt[pp] ; \
12176 } while(0)
12177
12178 if( Hnval < Hnxyz ){ /* initialize correlation from */
12179 float *wbfar=MRI_FLOAT_PTR(Hwtim) ; /* non-changing part of Haasrcim */
12180 float *bar =MRI_FLOAT_PTR(BASIM) ;
12181
12182 Haawt = (float *)malloc(sizeof(float)*Hnval) ;
12183 for( pp=0,kk=kbot ; kk <= ktop ; kk++ ){ /* extract weights */
12184 for( jj=jbot ; jj <= jtop ; jj++ ){ /* and base image */
12185 for( ii=ibot ; ii <= itop ; ii++,pp++ ){ /* for patch region */
12186 qq = ii + jj*Hnx + kk*Hnxy ;
12187 Haawt[pp] = wbfar[qq] ; /* copy weight image vals */
12188 wbfar[qq] = 0.0f ; /* 0 out temp weight */
12189 }}}
12190
12191 /* initialize the 'correlation' from the data that won't
12192 be changing (i.e., data from outside the local patch) */
12193
12194 if( !Hlocalstat )
12195 INCOR_addto( Hincor , Hnxyz ,
12196 MRI_FLOAT_PTR(Haabasim_minus) , MRI_FLOAT_PTR(Haasrcim_plus) , wbfar ) ;
12197 RESTORE_WBFAR ;
12198
12199 /* also init penalty from non-changing part of Haawarp, if needed */
12200
12201 if( Hpen_use ){
12202 float *je , *se ;
12203 je = Haawarp->je ; se = Haawarp->se ;
12204 for( kk=kbot ; kk <= ktop ; kk++ )
12205 for( jj=jbot ; jj <= jtop ; jj++ )
12206 for( ii=ibot ; ii <= itop ; ii++ )
12207 je[ii+jj*Hnx+kk*Hnxy] = se[ii+jj*Hnx+kk*Hnxy] = 0.0f ;
12208 Hpen_sum = HPEN_addup(Hnxyz,je,se) ;
12209 }
12210 }
12211
12212 /* optimization of warp parameters */
12213
12214 parvec = (double *)malloc(sizeof(double)*Hnparmap) ;
12215 xbot = (double *)malloc(sizeof(double)*Hnparmap) ;
12216 xtop = (double *)malloc(sizeof(double)*Hnparmap) ;
12217 for( ii=0 ; ii < Hnparmap ; ii++ ){
12218 parvec[ii] = 0.0 ;
12219 xbot[ii] = -Hbasis_parmax ;
12220 xtop[ii] = Hbasis_parmax ;
12221 }
12222
12223 powell_set_mfac( 1.001f , 2.001f ) ;
12224
12225 /***** HERE is the actual optimization! *****/
12226
12227 itmax = 8*Hnparmap+31 ;
12228 #if 0
12229 if( WORKHARD(Hlev_now) || SUPERHARD(Hlev_now) ) itmax -= Hnparmap ;
12230 #endif
12231
12232 if( Hverb > 3 ) powell_set_verbose(1) ;
12233
12234 /******* do it babee!! ***********************************/
12235
12236 /* note use of setjmp() here to allow the optimizer to
12237 get broken out of by the QUIT or ALRM signal, which
12238 can be necessary if OpenMP gets into some race condition */
12239
12240 Hquitting_do_jump = 666 ; /* indication that setjmp() is invoked */
12241
12242 if( setjmp(Hquitting_jmp_buf) == 0 ){ /* optimization of Hwarp parameters */
12243
12244 #ifdef USE_OMP
12245 int asec ;
12246 asec = (int)rintf(0.0000002f*Hnval*Hnpar*itmax/nthmax) ;
12247 if( asec < 99 ) asec = 99 ; /* min num seconds to wait */
12248 else if( asec > 1888 ) asec = 1888 ; /* max num seconds to wait */
12249 (void)alarm(asec) ; /* ALRM signal if optimizer takes too long. */
12250 /* The reason for this folderol is that gcc OpenMP */
12251 /* sometimes (rarely) freezes in a race condition. */
12252
12253 /* Also, the QUIT signal might come from user */
12254
12255 /* signal handler IW3D_signal_quit() was */
12256 /* setup for ALRM and QUIT in 3dQwarp.c */
12257 #endif
12258
12259 /***** HERE IS THE OPTIMIZATION!!! *****/
12260
12261 iter = powell_newuoa_con( Hnparmap , parvec,xbot,xtop , 0 ,
12262 prad,0.009*prad , itmax , IW3D_scalar_costfun_plusminus ) ;
12263
12264 #ifdef USE_OMP
12265 (void)alarm(0) ; /* cancel alarm signal if we succeeded/returned ! */
12266 #endif
12267
12268 } else { /*----- if we get to here, it was from the signal handler -----*/
12269 /*----- using longjmp() to break from optimizer == failure -----*/
12270
12271 WARNING_message("longjmp out of IW3D_improve_warp_plusminus due to %s signal\n"
12272 " -- warp optimization ends now" ,
12273 (Hquitting_sig==SIGQUIT) ? "QUIT (from user)"
12274 :(Hquitting_sig==SIGALRM) ? "ALRM (from internal timeout) :("
12275 : "unknown" ) ;
12276 if( Hquitting_sig == SIGALRM )
12277 WARNING_message(" ALRM signal is usually due to a multi-threaded 'race'\n"
12278 " condition in GCC OpenMP implementation\n"
12279 " -- which is to say, a compilation bug\n"
12280 " that arises quasi-randomly\n"
12281 " -- you can try running this job again and\n"
12282 " with luck, it will work out OK next time" ) ;
12283
12284 Hquitting_do_jump = 0 ; /* turn off longjmp() in signal handler */
12285 RETURN(0) ; /* failure return */
12286 }
12287 Hquitting_do_jump = 0 ;
12288 if( Hquitting ) RETURN(0) ; /* this code probably redundantly pleonastic */
12289
12290 if( iter > 0 ) Hnpar_sum += Hnparmap ;
12291
12292 if( Hverb > 3 ) powell_set_verbose(0) ;
12293
12294 /***** cleanup and exit phase ***/
12295
12296 free(xtop) ; free(xbot) ;
12297
12298 if( iter <= 0 ){ /* something bad happened */
12299 ININFO_message(
12300 " %s patch %03d..%03d %03d..%03d %03d..%03d : skipping - powell_newuoa_con() failure code=%d" ,
12301 WARP_CODE_STRING(warp_code) ,
12302 ibot,itop, jbot,jtop, kbot,ktop , iter ) ;
12303 ININFO_message(
12304 "powell_newuoa_con( ndim=%d x=%p xbot=%p xtop=%p nrand=%d rstart=%f rend=%f maxcall=%d ufunc=%p",
12305 Hnparmap , (void *)parvec , (void *)xbot , (void *)xtop , 0 , prad , 0.009*prad , itmax ,
12306 (void *)IW3D_scalar_costfun ) ;
12307 free(parvec); Hskipped++ ; RETURN(0);
12308 }
12309
12310 /* load optimized warped image and warp into their patches */
12311
12312 need_AH = 1 ;
12313 Hcost = IW3D_scalar_costfun_plusminus( Hnparmap , parvec ) ; /* evaluate at current results */
12314 (void)IW3D_load_energy(AHwarp) ;
12315
12316 /* AHwarp gets loaded into Haawarp and Hwval_plus into Haasrcim_plus
12317 and Hwval_minus into Haabasim_minus */
12318
12319 sarp = MRI_FLOAT_PTR(Haasrcim_plus) ;
12320 sarm = MRI_FLOAT_PTR(Haabasim_minus) ;
12321 Axd = Haawarp->xd; Ayd = Haawarp->yd; Azd = Haawarp->zd; Aje = Haawarp->je; Ase = Haawarp->se;
12322 bxd = AHwarp->xd ; byd = AHwarp->yd ; bzd = AHwarp->zd ; bje = AHwarp->je ; bse = AHwarp->se ;
12323
12324 jt= bje[0] ; st = bse[0] ;
12325 for( pp=0,kk=kbot ; kk <= ktop ; kk++ ){
12326 for( jj=jbot ; jj <= jtop ; jj++ ){
12327 for( ii=ibot ; ii <= itop ; ii++,pp++ ){
12328 qq = ii + jj*Hnx + kk*Hnxy ;
12329 sarp[qq] = Hwval_plus[pp] ;
12330 sarm[qq] = Hwval_minus[pp] ;
12331 Axd[qq] = bxd[pp] ; Ayd[qq] = byd[pp] ; Azd[qq] = bzd[pp] ;
12332 Aje[qq] = bje[pp] ; Ase[qq] = bse[pp] ;
12333 if( Aje[qq] > jt ) jt = Aje[qq] ;
12334 if( Ase[qq] > st ) st = Ase[qq] ;
12335 }}}
12336
12337 if( Hverb > 1 ){
12338 ININFO_message(
12339 " %7s patch %03d..%03d %03d..%03d %03d..%03d : cost=%.5f iter=%d : energy=%.3f:%.3f pen=%g",
12340 WARP_CODE_STRING(Hbasis_code) ,
12341 ibot,itop, jbot,jtop, kbot,ktop , Hcost , iter , jt,st , Hpenn ) ;
12342 } else if( Hverb == 1 && (Hlev_now<=2 || lrand48()%(Hlev_now*Hlev_now*Hlev_now/9)==0) ){
12343 fprintf(stderr,".") ;
12344 }
12345
12346 /* vamoose the ranch */
12347
12348 free(parvec) ; Hdone++ ; RETURN(iter) ;
12349 }
12350
12351 /*----------------------------------------------------------------------------*/
12352
IW3D_cleanup_improvement_plusminus(void)12353 void IW3D_cleanup_improvement_plusminus(void)
12354 {
12355 ENTRY("IW3D_cleanup_improvement_plusminus") ;
12356
12357 mri_free(Hbasim) ; Hbasim = NULL ;
12358 mri_free(Hsrcim) ; Hsrcim = NULL ;
12359 mri_free(Hwtim) ; Hwtim = NULL ; FREEIFNN(Hbmask) ;
12360 mri_free(Haasrcim) ; Haasrcim = NULL ;
12361 mri_free(Haasrcim_plus) ; Haasrcim_plus = NULL ;
12362 mri_free(Haabasim_minus); Haabasim_minus = NULL ;
12363
12364 mri_free(Hsrcim_blur) ; Hsrcim_blur = NULL ;
12365 mri_free(Hbasim_blur) ; Hbasim_blur = NULL ;
12366
12367 IW3D_destroy(Hwarp) ; Hwarp = NULL ;
12368 IW3D_destroy(AHwarp) ; AHwarp = NULL ;
12369 IW3D_destroy(Haawarp) ; Haawarp = NULL ;
12370
12371 INCOR_set_lpc_mask(NULL) ; /* 25 Jun 2014 */
12372 INCOR_destroy(Hincor) ; Hincor = NULL ; KILL_floatvec(Hmpar) ;
12373 FREEIFNN(Hpar) ; FREEIFNN(Hwval) ; FREEIFNN(Haawt) ; FREEIFNN(Hbval) ;
12374 FREEIFNN(Hparmap) ; Hnparmap = Hnpar = 0 ; Hbasis_code = -666 ;
12375 FREEIFNN(Hwval_plus) ; FREEIFNN(Hwval_minus) ;
12376
12377 FREEIFNN(bc0x); FREEIFNN(bc1x); nbcx=0;
12378 FREEIFNN(bc0y); FREEIFNN(bc1y); nbcy=0;
12379 FREEIFNN(bc0z); FREEIFNN(bc1z); nbcz=0;
12380 FREEIFNN(bc2x); FREEIFNN(bc3x); FREEIFNN(bc4x);
12381 FREEIFNN(bc2y); FREEIFNN(bc3y); FREEIFNN(bc4y);
12382 FREEIFNN(bc2z); FREEIFNN(bc3z); FREEIFNN(bc4z);
12383
12384 FREEIFNN(bq0x); FREEIFNN(bq1x); FREEIFNN(bq2x); nbqx=0;
12385 FREEIFNN(bq0y); FREEIFNN(bq1y); FREEIFNN(bq2y); nbqy=0;
12386 FREEIFNN(bq0z); FREEIFNN(bq1z); FREEIFNN(bq2z); nbqz=0;
12387
12388 if( bbbcar != NULL ){
12389 int ii ;
12390 for( ii=0 ; ii < nbbbcar ; ii++ ) FREEIFNN(bbbcar[ii]) ;
12391 free(bbbcar) ; nbbcxyz = nbbbcar = 0 ; bbbcar = NULL ;
12392 }
12393
12394 if( bbbqar != NULL ){
12395 int ii ;
12396 for( ii=0 ; ii < 27 ; ii++ ) FREEIFNN(bbbqar[ii]) ;
12397 free(bbbqar) ; nbbqxyz = 0 ; bbbqar = NULL ;
12398 }
12399
12400 Hstopcost = -666666.6f ;
12401 Hstopped = 0 ;
12402 Hfinal = 0 ;
12403
12404 HSAVE_DESTROY ;
12405
12406 EXRETURN ;
12407 }
12408
12409 /*----------------------------------------------------------------------------*/
12410
IW3D_setup_for_improvement_plusminus(MRI_IMAGE * bim,MRI_IMAGE * wbim,MRI_IMAGE * sim,IndexWarp3D * Iwarp,int meth_code,int warp_flags)12411 void IW3D_setup_for_improvement_plusminus(
12412 MRI_IMAGE *bim, MRI_IMAGE *wbim, MRI_IMAGE *sim,
12413 IndexWarp3D *Iwarp,
12414 int meth_code, int warp_flags )
12415 {
12416 int iii , nmask ;
12417
12418 ENTRY("IW3D_setup_for_improvement_plusminus") ;
12419
12420 /*-- eliminate old stuff --*/
12421
12422 IW3D_cleanup_improvement_plusminus() ;
12423
12424 /*-- copy base and source images --*/
12425
12426 Hnx = bim->nx; Hny = bim->ny; Hnz = bim->nz; Hnxy=Hnx*Hny; Hnxyz = Hnxy*Hnz;
12427 Hbasim = mri_to_float(bim) ;
12428 Hsrcim = mri_to_float(sim) ;
12429
12430 #ifdef ALLOW_INEDGE /* Jul 2018 */
12431 if( Hinedge_doit ){
12432 if( Hverb > 1 ) ININFO_message(" enhancing interior edges of base and source") ;
12433 mri_interior_edgeize( Hbasim , Hinedge_erode , Hinedge_frac ) ;
12434 mri_interior_edgeize( Hsrcim , Hinedge_erode , Hinedge_frac ) ;
12435 }
12436 #endif
12437
12438 if( Hpblur_b > 0.0f && Hblur_b == 0.0f ) Hblur_b = 0.1f ;
12439 if( Hpblur_s > 0.0f && Hblur_s == 0.0f ) Hblur_s = 0.1f ;
12440
12441 Hbasim_blur = IW3D_do_blurring( Hblur_b , Hpblur_b ,
12442 0.5f*Hnx,0.5f*Hny,0.5f*Hnz, Hbasim, "base" ) ;
12443 Hsrcim_blur = IW3D_do_blurring( Hblur_s , Hpblur_s ,
12444 0.5f*Hnx,0.5f*Hny,0.5f*Hnz, Hsrcim, "source" ) ;
12445
12446 /*-- and copy or create base weight image --*/
12447
12448 if( wbim != NULL ){ /*-- user supplied weight --*/
12449
12450 int ii,nwb,nexc ; float *wbfar ;
12451 if( wbim->kind != MRI_float ||
12452 wbim->nx != Hnx || wbim->ny != Hny || wbim->nz != Hnz )
12453 ERROR_exit("IW3D_setup_for_improvement_plusminus: bad wbim input") ;
12454
12455 Hwtim = mri_to_float(wbim) ; wbfar = MRI_FLOAT_PTR(Hwtim) ;
12456 Hbmask = (byte *)malloc(sizeof(byte)*Hnxyz) ;
12457 for( Hwbar=nwb=nexc=ii=0 ; ii < Hnxyz ; ii++ ){
12458 if( Hemask != NULL && Hemask[ii] && wbfar[ii] > 0.0f ){ /* 29 Oct 2012 */
12459 nexc++ ; wbfar[ii] = 0.0f ;
12460 }
12461 Hbmask[ii] = (wbfar[ii] > 0.0f) ;
12462 if( Hbmask[ii] ){ Hwbar += wbfar[ii] ; nwb++ ; }
12463 else { wbfar[ii] = 0.0f ; }
12464 }
12465 if( Hwbar == 0.0f || nwb == 0 )
12466 ERROR_exit("IW3D_setup_for_improvement_plusminus: all zero wbim input") ;
12467 if( Hverb > 1 ) ININFO_message(" %d voxels in mask (out of %d = %.2f%%)",
12468 nwb,Hnxyz,(100.0*nwb)/Hnxyz ) ;
12469 Hwbar /= nwb ; /* average value of all nonzero weights */
12470 nmask = nwb ;
12471 if( nexc > 0 ) ININFO_message("-emask excluded %d voxels",nexc) ;
12472
12473 } else { /*-- make weight up from nowhere --*/
12474
12475 int ii,nwb,nexc ; float *wbfar ;
12476 Hwtim = mri_new_vol(Hnx,Hny,Hnz,MRI_float); wbfar = MRI_FLOAT_PTR(Hwtim);
12477 Hbmask = (byte *)malloc(sizeof(byte)*Hnxyz) ;
12478 for( Hwbar=nwb=nexc=ii=0 ; ii < Hnxyz ; ii++ ){
12479 if( Hemask != NULL && Hemask[ii] ){ wbfar[ii] = 0.0f; Hbmask[ii] = 0; nexc++; }
12480 else { wbfar[ii] = 1.0f; Hbmask[ii] = 1; }
12481 if( Hbmask[ii] ){ Hwbar += wbfar[ii] ; nwb++ ; }
12482 }
12483 if( Hwbar == 0.0f || nwb == 0 )
12484 ERROR_exit("IW3D_setup_for_improvement_plusminus: all zero mask!?") ;
12485 Hwbar /= nwb ; /* average value of all nonzero weights */
12486 nmask = nwb ;
12487 if( nexc > 0 ) ININFO_message("-emask excluded %d voxels",nexc) ;
12488
12489 }
12490
12491 /*-- set operating codes --*/
12492
12493 Hmatch_code = meth_code ; iii = INCOR_check_meth_code(meth_code) ;
12494 if( iii == 0 )
12495 ERROR_exit("IW3D_setup_for_improvement_plusminus: bad meth_code input=%d",meth_code) ;
12496
12497 switch( meth_code ){
12498 default: Hnegate = 0 ; break ;
12499
12500 case GA_MATCH_PEARSON_LOCALA:
12501 case GA_MATCH_HELLINGER_SCALAR:
12502 case GA_MATCH_CRAT_USYM_SCALAR:
12503 case GA_MATCH_CRAT_SADD_SCALAR:
12504 case GA_MATCH_CORRATIO_SCALAR:
12505 case GA_MATCH_KULLBACK_SCALAR:
12506 case GA_MATCH_PEARCLP_SCALAR:
12507 case GA_MATCH_PEARSON_SCALAR: Hnegate = 1 ; break ;
12508 }
12509
12510 if( meth_code == GA_MATCH_PEARCLP_SCALAR || meth_code == GA_MATCH_PEARSON_SCALAR )
12511 Hstopcost = -3.995f ;
12512
12513 if( iii == 2 || iii == 3 ){ /* uses 2Dhist functions, so setup some parameters */
12514 float *xar,*yar , *bar,*sar ; int jj,kk ;
12515 float_quad xyc , xym ;
12516 bar = MRI_FLOAT_PTR(BASIM) ; sar = MRI_FLOAT_PTR(SRCIM) ;
12517 if( nmask == Hnxyz ){
12518 xar = bar ; yar = sar ; kk = Hnxyz ;
12519 } else {
12520 xar = (float *)malloc(sizeof(float)*nmask) ;
12521 yar = (float *)malloc(sizeof(float)*nmask) ;
12522 for( jj=kk=0 ; jj < Hnxyz ; jj++ ){
12523 if( Hbmask[jj] ){ xar[kk] = bar[jj] ; yar[kk++] = sar[jj] ; }
12524 }
12525 }
12526 xym = INCOR_2Dhist_minmax( kk , xar , yar ) ;
12527 xyc = INCOR_2Dhist_xyclip( kk , xar , yar ) ;
12528 if( xar != bar ){ free(xar) ; free(yar) ; }
12529 MAKE_floatvec(Hmpar,9) ;
12530 if( iii == 2 ){
12531 INCOR_setup_good(Hnxyz) ;
12532 Hmpar->ar[0] = (float)INCOR_2Dhist_compute_nbin(nmask) ;
12533 Hmpar->ar[1] = xym.a ; Hmpar->ar[2] = xym.b ; /* xbot xtop */
12534 Hmpar->ar[3] = xym.c ; Hmpar->ar[4] = xym.d ; /* ybot ytop */
12535 Hmpar->ar[5] = xyc.a ; Hmpar->ar[6] = xyc.b ; /* xcbot xctop */
12536 Hmpar->ar[7] = xyc.c ; Hmpar->ar[8] = xyc.d ; /* ycbot yctop */
12537 #if 1
12538 if( Hverb > 1 ){
12539 ININFO_message(" 2Dhist: nbin=%d",(int)Hmpar->ar[0]) ;
12540 ININFO_message(" xbot=%g xcbot=%g xctop=%g xtop=%g",
12541 Hmpar->ar[1], Hmpar->ar[5], Hmpar->ar[6], Hmpar->ar[2] ) ;
12542 ININFO_message(" ybot=%g ycbot=%g yctop=%g ytop=%g",
12543 Hmpar->ar[3], Hmpar->ar[7], Hmpar->ar[8], Hmpar->ar[4] ) ;
12544 }
12545 #endif
12546 } else if( iii == 3 ){
12547 float d1 , d2 , dif ;
12548 d2 = 0.05f*(xyc.b-xyc.a) ; /* 5% of x clip range */
12549 d1 = 0.5f*(xyc.a-xym.a) ; /* half of x clip bot to x min */
12550 dif = MIN(d1,d2) ; Hmpar->ar[1] = xyc.a-dif ; /* xdbot */
12551 d1 = 0.5f*(xym.b-xyc.b) ; dif = MIN(d1,d2) ; Hmpar->ar[2] = xyc.b+dif ; /* xdtop */
12552 d2 = 0.05f*(xyc.d-xyc.c) ;
12553 d1 = 0.5f*(xyc.c-xym.c) ; dif = MIN(d1,d2) ; Hmpar->ar[3] = xyc.c-dif ; /* ydbot */
12554 d1 = 0.5f*(xym.d-xyc.d) ; dif = MIN(d1,d2) ; Hmpar->ar[4] = xyc.d+dif ; /* ydtop */
12555 Hmpar->ar[5] = xyc.a ; Hmpar->ar[6] = xyc.b ; /* xcbot xctop */
12556 Hmpar->ar[7] = xyc.c ; Hmpar->ar[8] = xyc.d ; /* ycbot yctop */
12557 #if 0
12558 if( Hverb ){
12559 ININFO_message(" PEARCLP: xdbot=%g xcbot=%g xctop=%g xdtop=%g",
12560 Hmpar->ar[1], Hmpar->ar[5], Hmpar->ar[6], Hmpar->ar[2] ) ;
12561 ININFO_message(" ydbot=%g ycbot=%g yctop=%g ydtop=%g",
12562 Hmpar->ar[3], Hmpar->ar[7], Hmpar->ar[8], Hmpar->ar[4] ) ;
12563 }
12564 #endif
12565 }
12566 } else if( iii == 4 ){ /* Local Pearson setup [25 Jun 2014] */
12567 INCOR_set_lpc_mask(Hbmask) ;
12568 MAKE_floatvec(Hmpar,9) ; /* to be filled in later */
12569 }
12570
12571 Hgflags = IW3D_munge_flags(Hnx,Hny,Hnz,warp_flags) ;
12572 if( Hgflags < 0 )
12573 ERROR_exit("IW3D_setup_for_improvement: bad warp_flags input") ;
12574
12575 /*-- copy/create initial warp, and warp the source images --*/
12576 /*** [10 Aug 2014] Haasrcim_plus and Haabasim_minus are created later ***/
12577
12578 if( Iwarp != NULL ){
12579 if( Iwarp->nx != Hnx || Iwarp->ny != Hny || Iwarp->nz != Hnz )
12580 ERROR_exit("IW3D_setup_for_improvement: bad Iwarp input") ;
12581
12582 Haawarp = IW3D_copy(Iwarp,1.0f) ; /* copy it */
12583 } else {
12584 Haawarp = IW3D_create(Hnx,Hny,Hnz) ; /* initialize to 0 displacements */
12585 }
12586 (void)IW3D_load_energy(Haawarp) ; /* initialize energy field for penalty use */
12587
12588 EXRETURN ;
12589 }
12590
12591 /*----------------------------------------------------------------------------*/
12592
IW3D_warpomatic_plusminus(MRI_IMAGE * bim,MRI_IMAGE * wbim,MRI_IMAGE * sim,int meth_code,int warp_flags)12593 IndexWarp3D * IW3D_warpomatic_plusminus( MRI_IMAGE *bim, MRI_IMAGE *wbim, MRI_IMAGE *sim,
12594 int meth_code, int warp_flags )
12595 {
12596 int lev,levs , xwid,ywid,zwid , xdel,ydel,zdel , iter ;
12597 int ibot,itop,idon , jbot,jtop,jdon , kbot,ktop,kdon , dox,doy,doz , iii ;
12598 IndexWarp3D *OutWarp ;
12599 float flev , glev , Hcostold , Hcostmid=0.0f,Hcostend=0.0f ;
12600 int imin,imax , jmin,jmax, kmin,kmax , ibbb,ittt , jbbb,jttt , kbbb,kttt ;
12601 int dkkk,djjj,diii , ngmin=NGMIN , levdone=0 , do_qfinal=0 ;
12602 int zmode=MRI_CUBIC , nlevr , nsup,isup , leve ;
12603 int zmode2=MRI_CUBIC , zmodeX ; int cmode=MRI_CUBIC , qmode=MRI_QUINTIC ;
12604 char warplab[64] ;
12605 int xwid0,ywid0,zwid0 ; /* 04 Jan 2019 */
12606 float psize0=0.0f , psize=0.0f ; /* 24 Feb 2020 */
12607
12608 ENTRY("IW3D_warpomatic_plusminus") ;
12609
12610 Hfirsttime = 1 ;
12611
12612 if( Huse_cubic_lite ){ cmode = zmode = zmode2 = MRI_CUBIC_LITE ; } /* Dec 2018 */
12613 if( Huse_quintic_lite ){ qmode = MRI_QUINTIC_LITE ; }
12614
12615 IW3D_setup_for_improvement_plusminus( bim, wbim, sim, WO_iwarp, meth_code, warp_flags ) ;
12616
12617 /* range of indexes over which to warp */
12618
12619 MRI_autobbox( Hwtim , &imin,&imax , &jmin,&jmax , &kmin,&kmax ) ;
12620
12621 save_H_zero = 1 ;
12622
12623 /* do global warping first */
12624
12625 xwid = (imax-imin)/8 ; ywid = (jmax-jmin)/8 ; zwid = (kmax-kmin)/8 ;
12626 ibbb = MAX(0,imin-xwid) ; jbbb = MAX(0,jmin-ywid) ; kbbb = MAX(0,kmin-zwid) ;
12627 ittt = MIN(Hnx-1,imax+xwid); jttt = MIN(Hny-1,jmax+ywid); kttt = MIN(Hnz-1,kmax+zwid);
12628
12629 /* initial patch width at lev=0 == largest patch */
12630
12631 xwid0 = ittt-ibbb+1 ; ywid0 = jttt-jbbb+1 ; zwid0 = kttt-kbbb+1 ;
12632
12633 diii = ittt-ibbb+1 ; djjj = jttt-jbbb+1 ; dkkk = kttt-kbbb+1 ;
12634 iter = MAX(diii,djjj) ; iter = MAX(iter,dkkk) ;
12635 psize0 = (float)iter ; /* for modifying patch displacement magnitudes later */
12636 if( iter < NGMIN ){
12637 ERROR_message("Can't warpomatic such a small volume: %d x %d x %d",diii,djjj,dkkk) ;
12638 RETURN(NULL) ;
12639 }
12640
12641 if( Hverb )
12642 INFO_message("AFNI warpomatic start: %d x %d x %d volume ; autobbox = %d..%d %d..%d %d..%d",
12643 Hnx,Hny,Hnz, imin,imax,jmin,jmax,kmin,kmax) ;
12644
12645 Hfactor = 1.0f ;
12646 if( Hlev_start == 0 ){ /* top level = global warps */
12647 nlevr = ( Hznoq ) ? 0 : 1 ;
12648 Hforce = 1 ; Hpen_use = 0 ; Hlev_now = 0 ;
12649 PBLUR_BASE (ibbb,ittt,jbbb,jttt,kbbb,kttt) ; /* progressive blur, if ordered */
12650 PBLUR_SOURCE(ibbb,ittt,jbbb,jttt,kbbb,kttt) ;
12651 mri_free(Haasrcim_plus) ; /* at this point, create the initial */
12652 mri_free(Haabasim_minus); /* warped source and base images */
12653 if( IW3D_is_zero(Haawarp) ){
12654 Haasrcim_plus = mri_to_float(SRCIM) ; /* 'warped' source image */
12655 Haabasim_minus = mri_to_float(BASIM) ; /* 'warped' base image */
12656 } else {
12657 Haasrcim_plus = IW3D_warp_floatim( Haawarp, SRCIM, Himeth , 1.0f ) ;
12658 Haabasim_minus = IW3D_warp_floatim( Haawarp, BASIM, Himeth , -1.0f ) ;
12659 }
12660 if( Hverb == 1 ) fprintf(stderr,"lev=0 %d..%d %d..%d %d..%d: ",ibbb,ittt,jbbb,jttt,kbbb,kttt) ;
12661 /* cubic then quintic - somewhat different than 'normal' warping */
12662 BOXOPT ;
12663 #if 0 /* doesn't work at this time! */
12664 (void)IW3D_improve_warp_plusminus( MRI_SINCC , ibbb,ittt,jbbb,jttt,kbbb,kttt ) ;
12665 #endif
12666 (void)IW3D_improve_warp_plusminus( MRI_CUBIC_LITE , ibbb,ittt,jbbb,jttt,kbbb,kttt );
12667 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12668 BALLOPT ;
12669 (void)IW3D_improve_warp_plusminus( MRI_CUBIC_LITE , ibbb,ittt,jbbb,jttt,kbbb,kttt );
12670 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12671 (void)IW3D_improve_warp_plusminus( MRI_CUBIC , ibbb,ittt,jbbb,jttt,kbbb,kttt );
12672 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12673 if( nlevr ){
12674 BOXOPT ;
12675 (void)IW3D_improve_warp_plusminus( MRI_QUINTIC_LITE, ibbb,ittt,jbbb,jttt,kbbb,kttt );
12676 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12677 BALLOPT ;
12678 (void)IW3D_improve_warp_plusminus( MRI_QUINTIC_LITE, ibbb,ittt,jbbb,jttt,kbbb,kttt );
12679 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12680 (void)IW3D_improve_warp_plusminus( MRI_QUINTIC , ibbb,ittt,jbbb,jttt,kbbb,kttt );
12681 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12682 #if defined(ALLOW_BASIS5)
12683 if( H5zero ){ /* not usually useful and is very slow */
12684 BALLOPT ;
12685 (void)IW3D_improve_warp_plusminus( MRI_CUBIC_PLUS_3, ibbb,ittt,jbbb,jttt,kbbb,kttt );
12686 HCwarp_setup_basis345(0,0,0,0,0) ; /* cleanup */
12687 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12688 }
12689 #endif
12690 }
12691 if( Hverb == 1 ) fprintf(stderr," done [cost=%.5f]\n",Hcost) ;
12692 if( Hsave_allwarps ){
12693 sprintf(warplab,"Lev0.%04dx%04dx%04d",ittt-ibbb+1,jttt-jbbb+1,kttt-kbbb+1) ;
12694 HSAVE_ADDTO(Haawarp,warplab) ;
12695 }
12696 } else {
12697 Hcost = 666.666f ; /* a beastly thing to do */
12698 }
12699 Hforce = 0 ; Hlev_final = 0 ; Hpen_use = (Hpen_fac > 0.0f) ;
12700 Hcostmid = Hcostend = Hcost ;
12701
12702 if( Hngmin > 0 && Hlev_end > 99 ){
12703 ngmin = Hngmin ; if( ngmin < NGMIN_Q ) ngmin = NGMIN_Q ;
12704 }
12705
12706 if( ngmin < NGMIN ) ngmin = NGMIN ;
12707 else if( ngmin%2 == 0 ) ngmin-- ;
12708
12709 if( ngmin >= Hnx && ngmin >= Hny && ngmin >= Hnz ) goto DoneDoneDone ;
12710
12711 if( Hshrink > 1.0f ) Hshrink = 1.0f / Hshrink ;
12712 if( Hshrink < 0.444f || Hshrink > 0.888f ) Hshrink = 0.749999f ;
12713
12714 /* iterate down to finer and finer patches */
12715
12716 levs = MAX(1,Hlev_start) ;
12717 leve = Hlev_end ;
12718 for( lev=levs ; lev <= leve && !levdone ; lev++ ){
12719
12720 flev = (Hpen_old) ? 1.0f : powf( (float)(lev-levs+1) , 0.333f ) ; ;
12721 Hpen_fff = Hpen_fac * MIN(3.21f,flev) ; /* 20 Sep 2013 */
12722
12723 Hpen_use = (Hpen_fff > 0.0f) && (lev >= Hpen_first_lev ) ;
12724 if( lev == Hpen_first_lev ) Hpen_fff *= 0.5f ;
12725
12726 /* compute width of rectangles at this level */
12727
12728 flev = powf(Hshrink,(float)lev) ; /* shrinkage fraction */
12729 #if 0
12730 xwid = (Hnx+1)*flev ; if( xwid%2 == 0 ) xwid++ ;
12731 ywid = (Hny+1)*flev ; if( ywid%2 == 0 ) ywid++ ;
12732 zwid = (Hnz+1)*flev ; if( zwid%2 == 0 ) zwid++ ;
12733 #else
12734 xwid = xwid0*flev ; if( xwid%2 == 0 ) xwid++ ; /* 04 Jan 2019 */
12735 ywid = ywid0*flev ; if( ywid%2 == 0 ) ywid++ ;
12736 zwid = zwid0*flev ; if( zwid%2 == 0 ) zwid++ ;
12737 #endif
12738 psize = MAX(xwid,ywid) ; if( psize < zwid ) psize = zwid ;
12739 Hfactor = Hfactor_from_patchsize_ratio( psize / psize0 ) ;
12740
12741 /* decide if we are doing things in x, y, and/or z */
12742
12743 dox = (xwid >= ngmin) && !(Hgflags & NWARP_NOXDEP_FLAG) ;
12744 doy = (ywid >= ngmin) && !(Hgflags & NWARP_NOYDEP_FLAG) ;
12745 doz = (zwid >= ngmin) && !(Hgflags & NWARP_NOZDEP_FLAG) ;
12746
12747 if( !dox && !doy && !doz ){ /* exit immediately if nothing to do (shrank too far) */
12748 if( Hverb > 1 ){
12749 ININFO_message(" --------- lev=%d xwid=%d ywid=%d zwid=%d -- BREAK",lev,xwid,ywid,zwid) ;
12750 ININFO_message(" ......... xwid=%d ywid=%d zwid=%d ngmin=%d",xwid,ywid,zwid,ngmin) ;
12751 }
12752 break ;
12753 }
12754
12755 /* here, we are doing something, so don't let any width go below threshold */
12756
12757 Hlev_now = Hlev_final = lev ; /* in case we leave this loop somewhere below */
12758
12759 if( xwid < ngmin ) xwid = MIN(Hnx,ngmin);
12760 if( ywid < ngmin ) ywid = MIN(Hny,ngmin);
12761 if( zwid < ngmin ) zwid = MIN(Hnz,ngmin);
12762
12763 /* if we are almost to the smallest allowed patch, jump down to that size now */
12764
12765 flev = xwid / (float)ngmin ; /* flev is the */
12766 glev = ywid / (float)ngmin ; if( flev < glev ) flev = glev ; /* largest ratio */
12767 glev = zwid / (float)ngmin ; if( flev < glev ) flev = glev ; /* of ?wid to ngmin */
12768 if( flev > 1.0f && flev*Hshrink <= 1.00001f ){
12769 if( xwid > ngmin ) xwid = ngmin ;
12770 if( ywid > ngmin ) ywid = ngmin ;
12771 if( zwid > ngmin ) zwid = ngmin ;
12772 levdone = 1 ; /* signal to exit when loop finishes */
12773 } else {
12774 iter = MAX(xwid,ywid) ; iter = MAX(iter,zwid) ; levdone = (iter == ngmin) ;
12775 }
12776 Hfinal = levdone ;
12777
12778 /* step sizes for shifting the patches */
12779
12780 xdel = (xwid-1)/2 ; if( xdel == 0 ) xdel = 1 ;
12781 ydel = (ywid-1)/2 ; if( ydel == 0 ) ydel = 1 ;
12782 zdel = (zwid-1)/2 ; if( zdel == 0 ) zdel = 1 ;
12783
12784 diii = xdel ; djjj = ydel ; dkkk = zdel ;
12785
12786 /* bbbottom and tttop indexes to warp over */
12787
12788 ibbb = imin-xdel/2-1 ; if( ibbb < 0 ) ibbb = 0 ;
12789 jbbb = jmin-ydel/2-1 ; if( jbbb < 0 ) jbbb = 0 ;
12790 kbbb = kmin-zdel/2-1 ; if( kbbb < 0 ) kbbb = 0 ;
12791 ittt = imax+xdel/2+1 ; if( ittt >= Hnx ) ittt = Hnx-1 ;
12792 jttt = jmax+ydel/2+1 ; if( jttt >= Hny ) jttt = Hny-1 ;
12793 kttt = kmax+zdel/2+1 ; if( kttt >= Hnz ) kttt = Hnz-1 ;
12794
12795 zmode = zmode2 = cmode ; /* cubic patches from here down, thru all turtles */
12796 do_qfinal = (Hfinal && Hqfinal) ;
12797 if( do_qfinal || Hqonly ){ zmode = zmode2 = qmode ; }
12798 else if( Hqhard ){ zmode2 = qmode ; }
12799 if( xwid < NGMIN_Q || ywid < NGMIN_Q || zwid < NGMIN_Q ) /* 28 Oct 2015 */
12800 zmode = zmode2 = cmode ;
12801
12802 (void)IW3D_load_energy(Haawarp) ; /* initialize energy field for penalty use */
12803
12804 nlevr = WORKHARD(lev) ? 2 : 1 ;
12805 nsup = SUPERHARD(lev) ? 2 : 1 ;
12806
12807 /* announce the start of a new level! */
12808
12809 PBLUR_BASE (1,xwid,1,ywid,1,zwid) ; /* progressive blur, if ordered */
12810 PBLUR_SOURCE(1,xwid,1,ywid,1,zwid) ;
12811 if( Hpblur_b > 0.0f || Hpblur_b > 0.0f ) Hfirsttime = 1 ;
12812 mri_free(Haasrcim_plus) ; /* re-create the warped */
12813 mri_free(Haabasim_minus); /* source and base images */
12814 Haasrcim_plus = IW3D_warp_floatim( Haawarp, SRCIM, Himeth , 1.0f ) ;
12815 Haabasim_minus = IW3D_warp_floatim( Haawarp, BASIM, Himeth , -1.0f ) ;
12816
12817 if( Hverb > 1 )
12818 ININFO_message(" ........ +-lev=%d xwid=%d ywid=%d zwid=%d Hfac=%g penfac=%g %s %s" ,
12819 lev,xwid,ywid,zwid,Hfactor,
12820 (Hpen_use)?Hpen_fff:0.0f ,
12821 (levdone ? "FINAL" : "\0") ,
12822 (nlevr > 1 ? "WORKHARD" : "\0") ) ;
12823 else if( Hverb == 1 )
12824 fprintf(stderr,"lev=%d patch=%dx%dx%d: ",lev,xwid,ywid,zwid) ;
12825
12826 Hdone = Hskipped = 0 ;
12827
12828 /* alternate the direction of sweeping at different levels */
12829
12830 if( lev%2 == 1 || nlevr > 1 ){ /* bot to top, ijk */
12831 if( do_qfinal ) BALLOPT ;
12832 else if( nlevr > 1 ) BOXOPT ;
12833 for( isup=0 ; isup < nsup ; isup++ ){ /* superhard? */
12834 for( kdon=0,kbot=kbbb ; !kdon ; kbot += dkkk ){
12835 ktop = kbot+zwid-1;
12836 if( ktop >= kttt ) { ktop = kttt; kbot = ktop+1-zwid; kdon=1; }
12837 else if( ktop >= kttt-zwid/4 ){ ktop = kttt; kdon=1; }
12838 for( jdon=0,jbot=jbbb ; !jdon ; jbot += djjj ){
12839 jtop = jbot+ywid-1;
12840 if( jtop >= jttt ){ jtop = jttt; jbot = jtop+1-ywid; jdon=1; }
12841 else if( jtop >= jttt-ywid/4 ){ jtop = jttt; jdon=1; }
12842 for( idon=0,ibot=ibbb ; !idon ; ibot += diii ){
12843 itop = ibot+xwid-1;
12844 if( itop >= ittt ){ itop = ittt; ibot = itop+1-xwid; idon=1; }
12845 else if( itop >= ittt-xwid/4 ){ itop = ittt; idon=1; }
12846 Hcostold = Hcost ;
12847 iter = IW3D_improve_warp_plusminus( zmode , ibot,itop , jbot,jtop , kbot,ktop ) ;
12848 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12849 if( Hcost < Hstopcost ){
12850 if( Hverb == 1 ) fprintf(stderr,"\n") ;
12851 ININFO_message(" ######### cost has reached stopping value") ;
12852 goto DoneDoneDone ;
12853 }
12854 }
12855 }
12856 }
12857 } /* isup loop */
12858 Hcostmid = Hcostend = Hcost ;
12859 }
12860
12861 if( lev%2 == 0 || nlevr > 1 ){ /* top to bot, kji */
12862 if( nlevr > 1 && Hverb == 1 ) fprintf(stderr,":[cost=%.5f]:",Hcost) ;
12863 if( do_qfinal || nlevr > 1 ) BALLOPT ;
12864 zmodeX = (nlevr > 1) ? zmode2 : zmode ;
12865 for( isup=0 ; isup < nsup ; isup++ ){ /* superhard? */
12866 for( idon=0,itop=ittt ; !idon ; itop -= diii ){
12867 ibot = itop+1-xwid;
12868 if( ibot <= ibbb ){ ibot = ibbb; itop = ibot+xwid-1; idon=1; }
12869 else if( ibot <= ibbb+xwid/4 ){ ibot = ibbb; idon=1; }
12870 for( jdon=0,jtop=jttt ; !jdon ; jtop -= djjj ){
12871 jbot = jtop+1-ywid;
12872 if( jbot <= jbbb ){ jbot = jbbb; jtop = jbot+ywid-1; jdon=1; }
12873 else if( jbot <= jbbb+ywid/4 ){ jbot = jbbb; jdon=1; }
12874 for( kdon=0,ktop=kttt ; !kdon ; ktop -= dkkk ){
12875 kbot = ktop+1-zwid;
12876 if( kbot <= kbbb ){ kbot = kbbb; ktop = kbot+zwid-1; kdon=1; }
12877 else if( kbot <= kbbb+zwid/4 ){ kbot = kbbb; kdon=1; }
12878 Hcostold = Hcost ;
12879 iter = IW3D_improve_warp_plusminus( zmodeX , ibot,itop , jbot,jtop , kbot,ktop ) ;
12880 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12881 if( Hcost < Hstopcost ){
12882 if( Hverb == 1 ) fprintf(stderr,"\n") ;
12883 ININFO_message(" ######### cost has reached stopping value") ;
12884 goto DoneDoneDone ;
12885 }
12886 }
12887 }
12888 }
12889 } /* isup loop */
12890 Hcostend = Hcost ;
12891 }
12892
12893 if( Hdone == 0 ){ /* nuthing done at this level? */
12894 ibot = (imin+imax-xwid)/2 ; if( ibot < 0 ) ibot = 0 ; /* centered on */
12895 jbot = (jmin+jmax-ywid)/2 ; if( jbot < 0 ) jbot = 0 ; /* the autobox */
12896 kbot = (kmin+kmax-zwid)/2 ; if( kbot < 0 ) kbot = 0 ;
12897 itop = ibot+xwid-1 ; if( itop >= Hnx ) itop = Hnx-1 ;
12898 jtop = jbot+ywid-1 ; if( jtop >= Hny ) jtop = Hny-1 ;
12899 ktop = kbot+zwid-1 ; if( ktop >= Hnz ) ktop = Hnz-1 ;
12900 Hforce = 1 ;
12901 iter = IW3D_improve_warp( zmode , ibot,itop , jbot,jtop , kbot,ktop ) ;
12902 if( Hquitting ) goto DoneDoneDone ; /* signal to quit was sent */
12903 Hforce = 0 ;
12904 Hcostend = Hcost ;
12905 }
12906
12907 if( Hverb > 0 ){
12908 if( Hdone > 0 )
12909 fprintf(stderr," done [cost:%.5f ; %d patches optimized, %d skipped]\n",Hcost,Hdone,Hskipped) ;
12910 else
12911 fprintf(stderr," done [cost:%.5f ; all patches skipped]\n",Hcost) ;
12912 }
12913
12914 if( Hsave_allwarps ){
12915 sprintf(warplab,"Lev%d.%04dx%04dx%04d",lev,xwid,ywid,zwid) ;
12916 HSAVE_ADDTO(Haawarp,warplab) ;
12917 }
12918
12919 } /*-- end of loop over levels of refinement --*/
12920
12921 DoneDoneDone: /* breakout */
12922
12923 OutWarp = IW3D_copy( Haawarp , 1.0f ) ;
12924 IW3D_cleanup_improvement_plusminus() ;
12925
12926 RETURN(OutWarp) ;
12927 }
12928
12929 /*----------------------------------------------------------------------------*/
12930
IW3D_warp_s2bim_plusminus(MRI_IMAGE * bim,MRI_IMAGE * wbim,MRI_IMAGE * sim,int interp_code,int meth_code,int warp_flags)12931 Image_plus_Warp ** IW3D_warp_s2bim_plusminus(
12932 MRI_IMAGE *bim , MRI_IMAGE *wbim , MRI_IMAGE *sim,
12933 int interp_code , int meth_code , int warp_flags )
12934 {
12935 IndexWarp3D *Swarp ;
12936 Image_plus_Warp *pww , *mww , **sbww ;
12937
12938 ENTRY("IW3D_warp_s2bim_plusminus") ;
12939
12940 WO_iwarp = S2BIM_iwarp ; Hlev_start = S2BIM_ilev ; Hlev_end = S2BIM_mlev ;
12941 Hnpar_sum = 0 ;
12942
12943 Hshrink = AFNI_numenv("AFNI_WARPOMATIC_SHRINK") ;
12944 if( Hshrink > 1.0f ) Hshrink = 1.0f / Hshrink ;
12945 if( Hshrink < 0.444f || Hshrink > 0.888f ) Hshrink = 0.749999f ;
12946 else ININFO_message(" -- Hshrink set to %.6f",Hshrink) ;
12947
12948 Swarp = IW3D_warpomatic_plusminus( bim , wbim , sim , meth_code , warp_flags ) ;
12949
12950 pww = (Image_plus_Warp *)malloc(sizeof(Image_plus_Warp)) ;
12951 pww->im = IW3D_warp_floatim( Swarp, sim , interp_code , 1.0f ) ;
12952 pww->warp = Swarp ;
12953
12954 mww = (Image_plus_Warp *)malloc(sizeof(Image_plus_Warp)) ;
12955 mww->warp = IW3D_copy( Swarp , -1.0f ) ;
12956 mww->im = IW3D_warp_floatim( Swarp, bim , interp_code , -1.0f ) ;
12957
12958 sbww = (Image_plus_Warp **)malloc(sizeof(Image_plus_Warp *)*2) ;
12959 sbww[0] = pww ; sbww[1] = mww ;
12960
12961 RETURN(sbww) ;
12962 }
12963
12964 #if 0
12965 /*----------------------------------------------------------------------------*/
12966 /* Function to compute 'normal' 3dQwarp result from the plusminus warps.
12967 From the 3dQwarp help:
12968 Define Wp(x) = x+dis(x) and Wm(x) = x-dis(x). Then since
12969 base(Wm(x)) matches source(Wp(x)), by substituting INV(Wm(x))
12970 wherever we see x, we have base(x) matches source(Wp(INV(Wm(x))));
12971 that is, the warp V(x) that one would get from the 'usual' way
12972 of running 3dQwarp is V(x) = Wp(INV(Wm(x))).
12973 The code to do this is in 3dQWarp.c ('-pmBASE' option) and so this
12974 function is not actually used anywhere.
12975 *//*--------------------------------------------------------------------------*/
12976
12977 IndexWarp3D * IW3D_plusminus_to_direct( IndexWarp3D *pww )
12978 {
12979 IndexWarp3D *mww , *imww ;
12980
12981 if( pww == NULL ) return NULL ;
12982
12983 mww = IW3D_copy( pww , -1.0f ) ;
12984 imww = IW3D_invert ( mww , NULL , MRI_WSINC5 ) ;
12985 mww = IW3D_compose( imww , pww , MRI_WSINC5 ) ;
12986
12987 IW3D_destroy(imww) ;
12988 return mww ;
12989 }
12990 #endif
12991
12992 /*****--------------------------------------------------------------------*****/
12993 /*****||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*****/
12994 /*****--------------------------------------------------------------------*****/
12995
12996 #endif /*(Q9)*/ /*############################################################*/
12997
12998 #endif /* ALLOW_QWARP */
12999 /******************************************************************************/
13000 /*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*/
13001 /******************************************************************************/
13002