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