1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2008-2017 Imperial College London
5  * Copyright 2008-2013 Daniel Rueckert, Julia Schnabel
6  * Copyright 2013-2017 Andreas Schuh
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #include "mirtk/FreeFormTransformation.h"
22 
23 #include "mirtk/Math.h"
24 #include "mirtk/Memory.h"
25 #include "mirtk/Parallel.h"
26 #include "mirtk/Profiling.h"
27 
28 
29 namespace mirtk {
30 
31 
32 // =============================================================================
33 // Construction/Destruction
34 // =============================================================================
35 
36 // Auxiliary macro used to initialize references to often needed
37 // control point image attributes in initialization list of constructor
38 #define _References()                                                          \
39   _attr  (_CPImage.Attributes()),                                              \
40   _x     (_CPImage.Attributes()._x),                                           \
41   _y     (_CPImage.Attributes()._y),                                           \
42   _z     (_CPImage.Attributes()._z),                                           \
43   _t     (_CPImage.Attributes()._t),                                           \
44   _dx    (_CPImage.Attributes()._dx),                                          \
45   _dy    (_CPImage.Attributes()._dy),                                          \
46   _dz    (_CPImage.Attributes()._dz),                                          \
47   _dt    (_CPImage.Attributes()._dt),                                          \
48   _matW2L(_CPImage.GetWorldToImageMatrix()),                                   \
49   _matL2W(_CPImage.GetImageToWorldMatrix())
50 
51 // -----------------------------------------------------------------------------
52 FreeFormTransformation
FreeFormTransformation(CPInterpolator & func,CPInterpolator * func2D)53 ::FreeFormTransformation(CPInterpolator &func, CPInterpolator *func2D)
54 :
55   _ExtrapolationMode(Extrapolation_Const),
56   _SpeedupFactor(1.0),
57   _CPValue (NULL),
58   _CPFunc  (&func),
59   _CPFunc2D(func2D),
60   _CPStatus(NULL),
61   _References()
62 {
63 }
64 
65 // -----------------------------------------------------------------------------
66 FreeFormTransformation
FreeFormTransformation(const FreeFormTransformation & ffd,CPInterpolator & func,CPInterpolator * func2D)67 ::FreeFormTransformation(const FreeFormTransformation &ffd,
68                          CPInterpolator &func, CPInterpolator *func2D)
69 :
70   Transformation(ffd),
71   _ExtrapolationMode(ffd._ExtrapolationMode),
72   _SpeedupFactor(ffd._SpeedupFactor),
73   _CPValue (NULL),
74   _CPFunc  (&func),
75   _CPFunc2D(func2D),
76   _CPStatus(NULL),
77   _References()
78 {
79   if (_NumberOfDOFs > 0) {
80     _CPImage.Initialize(ffd.Attributes(), reinterpret_cast<CPValue  *>(_Param));
81     Allocate(_CPStatus, _x, _y, _z, _t,   reinterpret_cast<CPStatus *>(_Status));
82   }
83 }
84 
85 // -----------------------------------------------------------------------------
~FreeFormTransformation()86 FreeFormTransformation::~FreeFormTransformation()
87 {
88   Deallocate(_CPStatus, _Status);
89   Delete(_CPValue);
90 }
91 
92 
93 #undef _References // only used/needed for constructors
94 
95 // -----------------------------------------------------------------------------
96 ImageAttributes
97 FreeFormTransformation
DefaultAttributes(const ImageAttributes & attr,double dx,double dy,double dz,double dt)98 ::DefaultAttributes(const ImageAttributes &attr,
99                     double dx, double dy, double dz, double dt)
100 {
101   // Set spacing equal to voxel size if not specified (i.e., negative)
102   if (dx < .0) dx = attr._dx;
103   if (dy < .0) dy = attr._dy;
104   if (dz < .0) dz = attr._dz;
105   if (dt < .0) dt = attr._dt;
106 
107   // Orthogonalize image grid if necessary
108   const ImageAttributes grid = OrthogonalFieldOfView(attr);
109 
110   // Set lattice attributes to image attributes
111   ImageAttributes lattice = grid;
112 
113   // Adjust lattice dimensions
114   lattice._x = ((dx > .0 && grid._x > 1) ? iround((grid._x - 1) * grid._dx / dx) + 1 : 1);
115   lattice._y = ((dy > .0 && grid._y > 1) ? iround((grid._y - 1) * grid._dy / dy) + 1 : 1);
116   lattice._z = ((dz > .0 && grid._z > 1) ? iround((grid._z - 1) * grid._dz / dz) + 1 : 1);
117   lattice._t = ((dt > .0 && grid._t > 1) ? iround((grid._t - 1) * grid._dt / dt) + 1 : 1);
118 
119   // Update lattice spacing
120   lattice._dx = ((lattice._x > 1) ? dx : .0);
121   lattice._dy = ((lattice._y > 1) ? dy : .0);
122   lattice._dz = ((lattice._z > 1) ? dz : .0);
123   lattice._dt = ((lattice._t > 1) ? dt : .0);
124 
125   return lattice;
126 }
127 
128 // -----------------------------------------------------------------------------
129 ImageAttributes
130 FreeFormTransformation
DefaultAttributes(double x1,double y1,double z1,double t1,double x2,double y2,double z2,double t2,double dx,double dy,double dz,double dt,const double * xaxis,const double * yaxis,const double * zaxis)131 ::DefaultAttributes(double x1, double y1, double z1, double t1,
132                     double x2, double y2, double z2, double t2,
133                     double dx, double dy, double dz, double dt,
134                     const double *xaxis,
135                     const double *yaxis,
136                     const double *zaxis)
137 {
138   ImageAttributes attr;
139 
140   // Lattice origin
141   attr._xorigin = (x2 + x1) / 2.0;
142   attr._yorigin = (y2 + y1) / 2.0;
143   attr._zorigin = (z2 + z1) / 2.0;
144   attr._torigin = t1;
145 
146   // Lattice orientation
147   memcpy(attr._xaxis, xaxis, 3 * sizeof(double));
148   memcpy(attr._yaxis, yaxis, 3 * sizeof(double));
149   memcpy(attr._zaxis, zaxis, 3 * sizeof(double));
150 
151   // FOV in lattice orientation
152   double a, b, c;
153   a  = x1 * xaxis[0] + y1 * xaxis[1] + z1 * xaxis[2];
154   b  = x1 * yaxis[0] + y1 * yaxis[1] + z1 * yaxis[2];
155   c  = x1 * zaxis[0] + y1 * zaxis[1] + z1 * zaxis[2];
156   x1 = a, y1 = b, z1 = c;
157   a  = x2 * xaxis[0] + y2 * xaxis[1] + z2 * xaxis[2];
158   b  = x2 * yaxis[0] + y2 * yaxis[1] + z2 * yaxis[2];
159   c  = x2 * zaxis[0] + y2 * zaxis[1] + z2 * zaxis[2];
160   x2 = a, y2 = b, z2 = c;
161 
162   // Lattice dimensions
163   attr._x = ((dx > .0) ? iround((x2 - x1) / dx) + 1 : 1);
164   attr._y = ((dy > .0) ? iround((y2 - y1) / dy) + 1 : 1);
165   attr._z = ((dz > .0) ? iround((z2 - z1) / dz) + 1 : 1);
166   attr._t = ((dt > .0) ? iround((t2 - t1) / dt) + 1 : 1);
167 
168   // Lattice spacing
169   attr._dx = ((attr._x > 1) ? dx : .0);
170   attr._dy = ((attr._y > 1) ? dy : .0);
171   attr._dz = ((attr._z > 1) ? dz : .0);
172   attr._dt = ((attr._t > 1) ? dt : .0);
173 
174   return attr;
175 }
176 
177 // -----------------------------------------------------------------------------
InitializeInterpolator()178 void FreeFormTransformation::InitializeInterpolator()
179 {
180   // Attention: Returns NULL for _ExtrapolationMode == Extrapolation_None!
181   Delete(_CPValue);
182   _CPValue = CPExtrapolator::New(_ExtrapolationMode, &_CPImage);
183   _CPFunc->Input(&_CPImage);
184   _CPFunc->Extrapolator(_CPValue);
185   _CPFunc->Initialize(true); // also initializes _CPValue
186   if (_CPFunc2D) {
187     _CPFunc2D->Input(&_CPImage);
188     _CPFunc2D->Extrapolator(_CPValue);
189     _CPFunc2D->Initialize(true);
190   }
191 }
192 
193 // -----------------------------------------------------------------------------
InitializeCPs(const ImageAttributes & attr,bool dofs)194 void FreeFormTransformation::InitializeCPs(const ImageAttributes &attr, bool dofs)
195 {
196   if (attr._x > 1000 || attr._y > 1000 || attr._z > 1000) {
197     // Otherwise we may not only run out of memory, but also encounter an
198     // integer overflow when computing the number of control points...
199     cerr << "FreeFormTransformation::InitializeCPs: Number of control points may not exceed 1000 in each dimension!" << endl;
200     exit(1);
201   }
202   Deallocate(_CPStatus, _Status);
203   const int ncps = attr.NumberOfPoints();
204   if (ncps > 0) {
205     // If NULL, allocate memory for control points which differs from
206     // the memory referenced by _Param and _Status of the base class
207     CPValue  *param  = NULL;
208     CPStatus *status = NULL;
209     // If control points are directly the parameters of the transformation...
210     if (dofs) {
211       // Assert size of types
212       if (sizeof(CPValue) != 3 * sizeof(DOFValue)) {
213         cerr << "FreeFormTransformation::InitializeCPs: Assertion \"sizeof(CPValue) == 3 * sizeof(DOFValue)\" failed!" << endl;
214         cerr << "    Make sure that the vector type used fulfills this assertion such that FFDs can wrap the linear" << endl;
215         cerr << "    memory used to store the transformation parameters in an image instance with 3D vector voxel type." << endl;
216         exit(1);
217       }
218       // ...allocate memory for parameters and their respective status
219       InitializeDOFs(3 * ncps);
220       // ...and reinterpret this memory as 3D(+t) images
221       param  = reinterpret_cast<CPValue  *>(_Param);
222       status = reinterpret_cast<CPStatus *>(_Status);
223     }
224     // Allocate 4D array for convenient access to parameters of each control point
225     _CPImage.Initialize(attr, param);
226     Allocate(_CPStatus, attr._x, attr._y, attr._z, attr._t, status);
227     InitializeInterpolator();
228     InitializeStatus();
229   } else {
230     Delete(_CPValue);
231     _CPImage.Clear();
232     InitializeDOFs(0);
233   }
234 }
235 
236 // -----------------------------------------------------------------------------
InitializeCPs(const FreeFormTransformation & ffd,bool dofs)237 void FreeFormTransformation::InitializeCPs(const FreeFormTransformation &ffd, bool dofs)
238 {
239   if (ffd.NumberOfCPs() > 0) {
240     // If NULL, allocate memory for control points which differs from
241     // the memory referenced by _Param and _Status of the base class
242     CPValue  *param  = NULL;
243     CPStatus *status = NULL;
244     // If control points are directly the parameters of the transformation...
245     if (dofs) {
246       // ...copy transformation parameters and their respective status
247       InitializeDOFs(ffd);
248       // ...and reinterpret this memory as 3D(+t) images
249       param  = reinterpret_cast<CPValue  *>(_Param);
250       status = reinterpret_cast<CPStatus *>(_Status);
251     }
252     // Allocate 4D array for convenient access to parameters of each control point
253     _CPImage.Initialize(ffd.Attributes(), param);
254     Allocate(_CPStatus, _x, _y, _z, _t,   status);
255     InitializeInterpolator();
256     InitializeStatus();
257   } else {
258     Delete(_CPValue);
259     _CPImage.Clear();
260     Deallocate(_CPStatus, _Status);
261     InitializeDOFs(0);
262   }
263 }
264 
265 // -----------------------------------------------------------------------------
InitializeStatus()266 void FreeFormTransformation::InitializeStatus()
267 {
268   // Set status of unused control point dimensions to Passive
269   // TODO: Consider removing this commented code or uncomment it again once
270   //       the control point coefficients are in lattice units/orientation.
271   //       A 2D FFD in a plane not parallel to the xy plane has non-zero
272   //       (i.e., active) z coefficients in world space!
273 //  const CPStatus status(((_x > 1) ? Active : Passive),
274 //                        ((_y > 1) ? Active : Passive),
275 //                        ((_z > 1) ? Active : Passive));
276   const CPStatus status = Active;
277   for (int cp = 0; cp < this->NumberOfCPs(); ++cp) PutStatus(cp, status);
278 }
279 
280 // -----------------------------------------------------------------------------
Initialize(const ImageAttributes & attr)281 void FreeFormTransformation::Initialize(const ImageAttributes &attr)
282 {
283   InitializeCPs(attr);
284   this->Changed(true);
285 }
286 
287 // -----------------------------------------------------------------------------
Initialize(const ImageAttributes & attr,double dx,double dy,double dz,double dt)288 void FreeFormTransformation::Initialize(const ImageAttributes &attr,
289                                         double dx, double dy, double dz, double dt)
290 {
291   this->Initialize(DefaultAttributes(attr, dx, dy, dz, dt));
292 }
293 
294 // -----------------------------------------------------------------------------
Initialize(const CPImage & image,bool displacement)295 void FreeFormTransformation::Initialize(const CPImage &image, bool displacement)
296 {
297   // Initialize free-form transformation
298   this->Initialize(image.Attributes());
299   // Copy control point coefficients
300   _CPImage.CopyFrom(image);
301   // Convert control point deformation to displacement
302   if (!displacement) {
303     double x, y, z;
304     CPValue *data = _CPImage.Data();
305     for (int l = 0; l < _t; ++l)
306     for (int k = 0; k < _z; ++k)
307     for (int j = 0; j < _y; ++j)
308     for (int i = 0; i < _x; ++i) {
309       x = i, y = j, z = k;
310       this->LatticeToWorld(x, y, z);
311       data->_x -= x;
312       data->_y -= y;
313       data->_z -= z;
314       ++data;
315     }
316   }
317 }
318 
319 // -----------------------------------------------------------------------------
320 void FreeFormTransformation
Initialize(const GenericImage<double> & image,bool displacement)321 ::Initialize(const GenericImage<double> &image, bool displacement)
322 {
323   if (image.T() < 2 || image.T() > 3) {
324     cerr << "FreeFormTransformation::Initialize:"
325             " Input image must be 2D/3D vector field with _t == 2 or 3)" << endl;
326     exit(1);
327   }
328   // Initialize control points
329   ImageAttributes attr = image.Attributes();
330   attr._t  = 1;
331   attr._dt = .0;
332   this->Initialize(attr);
333   // Copy vector field
334   CPValue *data = _CPImage.Data();
335   for (int k = 0; k < image.GetZ(); ++k)
336   for (int j = 0; j < image.GetY(); ++j)
337   for (int i = 0; i < image.GetX(); ++i) {
338     data->_x = image(i, j, k, 0);
339     data->_y = image(i, j, k, 1);
340     if (image.GetT() >= 3) data->_z = image(i, j, k, 2);
341     else                   data->_z = .0;
342     ++data;
343   }
344   // Convert control point deformation to displacement
345   if (!displacement) {
346     double x, y, z;
347     CPValue *data = _CPImage.Data();
348     for (int l = 0; l < _t; ++l)
349     for (int k = 0; k < _z; ++k)
350     for (int j = 0; j < _y; ++j)
351     for (int i = 0; i < _x; ++i) {
352       x = i, y = j, z = k;
353       this->LatticeToWorld(x, y, z);
354       data->_x -= x;
355       data->_y -= y;
356       data->_z -= z;
357       ++data;
358     }
359   }
360 }
361 
362 // =============================================================================
363 // Parameters (non-DoFs)
364 // =============================================================================
365 
366 // -----------------------------------------------------------------------------
ExtrapolationMode(enum ExtrapolationMode mode)367 void FreeFormTransformation::ExtrapolationMode(enum ExtrapolationMode mode)
368 {
369   // Set extrapolation mode
370   _ExtrapolationMode = mode;
371   // Update control point function, if _CPValue == NULL the FFD is uninitialized yet
372   if (_CPValue && _CPValue->ExtrapolationMode() != _ExtrapolationMode) {
373     Delete(_CPValue);
374     _CPValue = CPExtrapolator::New(_ExtrapolationMode, &_CPImage);
375     if (_CPValue) {
376       _CPValue->Input(&_CPImage);
377       _CPValue->Initialize();
378     }
379     _CPFunc->Extrapolator(_CPValue);
380     if (_CPFunc2D) _CPFunc2D->Extrapolator(_CPValue);
381     // Note: No need to call _CPFunc(2D)->Initialize() again
382   }
383 }
384 
385 // -----------------------------------------------------------------------------
Set(const char * name,const char * value)386 bool FreeFormTransformation::Set(const char *name, const char *value)
387 {
388   // Extrapolation mode
389   if (strcmp(name, "Transformation extrapolation") == 0) {
390     enum ExtrapolationMode mode;
391     if (!FromString(value, mode)) return false;
392     this->ExtrapolationMode(mode);
393     this->Changed(true);
394     return true;
395   }
396   // Speedup factor for gradient computation
397   if (strcmp(name, "Speedup factor") == 0) {
398     double d;
399     if (!FromString(value, d) || d < 1.0) return false;
400     _SpeedupFactor = d;
401     this->Changed(true);
402     return true;
403   }
404   // Control point spacing
405   if (strncmp(name, "Control point spacing", 21) == 0) {
406     double dx, dy, dz, dt, ds;
407     if (!FromString(value, ds) || ds <= .0) return false;
408     if      (strstr(name, "[m]")  != NULL) ds *= 1.0e+3;
409     else if (strstr(name, "[mu]") != NULL) ds *= 1.0e-3;
410     _CPImage.GetPixelSize(dx, dy, dz, dt);
411     if (strncmp(name + 21, " in ", 4) == 0) {
412       if      (name[25] == 'X') dx = ds;
413       else if (name[25] == 'Y') dy = ds;
414       else if (name[25] == 'Z') dz = ds;
415       else if (name[25] == 'T') dt = ds;
416     } else {
417       dx = dy = dz = dt = ds;
418     }
419     if (_x == 1) dx = .0;
420     if (_y == 1) dy = .0;
421     if (_z == 1) dz = .0;
422     if (_t == 1) dt = .0;
423     _CPImage.PutPixelSize(dx, dy, dz, dt);
424     this->Changed(true);
425     return true;
426   }
427   if (strcmp(name, "Control point xyz units") == 0) {
428     double s;
429     if      (strcmp(value, "Meter")      == 0 || strcmp(value, "m")  == 0) s = 1.0e+3;
430     else if (strcmp(value, "Millimeter") == 0 || strcmp(value, "mm") == 0) s = 1.0;
431     else if (strcmp(value, "Micrometer") == 0 || strcmp(value, "mu") == 0) s = 1.0e-3;
432     else return false;
433     if (s != 1.0) {
434       double ox, oy, oz, dx, dy, dz;
435       _CPImage.GetOrigin   (ox, oy, oz);
436       _CPImage.PutOrigin   (ox * s, oy * s, oz * s);
437       _CPImage.GetPixelSize(dx, dy, dz);
438       _CPImage.PutPixelSize(dx * s, dy * s, dz * s);
439       _CPImage *= s;
440     }
441     this->Changed(true);
442     return true;
443   }
444   return Transformation::Set(name, value);
445 }
446 
447 // -----------------------------------------------------------------------------
Parameter() const448 ParameterList FreeFormTransformation::Parameter() const
449 {
450   ParameterList params = Transformation::Parameter();
451   Insert(params, "Transformation extrapolation", ToString(_ExtrapolationMode));
452   Insert(params, "Speedup factor",               ToString(_SpeedupFactor));
453   return params;
454 }
455 
456 // =============================================================================
457 // Approximation/Interpolation
458 // =============================================================================
459 
460 // -----------------------------------------------------------------------------
461 ImageAttributes FreeFormTransformation
ApproximationDomain(const ImageAttributes & domain,const Transformation *)462 ::ApproximationDomain(const ImageAttributes &domain, const Transformation *)
463 {
464   return domain;
465 }
466 
467 // -----------------------------------------------------------------------------
EvaluateRMSError(const Transformation * dof) const468 double FreeFormTransformation::EvaluateRMSError(const Transformation *dof) const
469 {
470   return EvaluateRMSError(_attr, dof);
471 }
472 
473 // -----------------------------------------------------------------------------
Approximate(const Transformation * t,int niter,double max_error)474 double FreeFormTransformation::Approximate(const Transformation *t, int niter, double max_error)
475 {
476   return this->Approximate(_attr, t, niter, max_error);
477 }
478 
479 // -----------------------------------------------------------------------------
480 double FreeFormTransformation
Approximate(const ImageAttributes & domain,double * dx,double * dy,double * dz,int niter,double max_error)481 ::Approximate(const ImageAttributes &domain, double *dx, double *dy, double *dz,
482               int niter, double max_error)
483 {
484   const int no = domain.NumberOfPoints();
485   if (no <= 0) return .0;
486   double error = numeric_limits<double>::infinity();
487   if (niter < 1) return error;
488 
489   // Compute world coordinates of lattice points
490   double *x = Allocate<double>(no);
491   double *y = Allocate<double>(no);
492   double *z = Allocate<double>(no);
493   double *t = Allocate<double>(no);
494   domain.LatticeToWorld(x, y, z, t);
495 
496   // Copy original displacements
497   double *tx = CAllocate<double>(no);
498   double *ty = CAllocate<double>(no);
499   double *tz = CAllocate<double>(no);
500   memcpy(tx, dx, no * sizeof(double));
501   memcpy(ty, dy, no * sizeof(double));
502   memcpy(tz, dz, no * sizeof(double));
503 
504   // Evaluate error of approximation and residual displacements
505   if (this->RequiresCachingOfDisplacements()) {
506     error = EvaluateRMSError(domain, dx, dy, dz);
507   } else {
508     error = EvaluateRMSError(x, y, z, t, dx, dy, dz, no);
509   }
510 
511   // Repeat approximation n times or until error drops below a threshold
512   DOFValue *param = Allocate<DOFValue>(_NumberOfDOFs);
513   for (int iter = 0; iter < niter && error > max_error; ++iter) {
514 
515     // Copy current parameters
516     this->Get(param);
517 
518     // Approximate residual displacements by new parameters
519     this->ApproximateDOFs(x, y, z, t, dx, dy, dz, no);
520 
521     // Add previous parameters
522     this->Add(param);
523 
524     // Evaluate error of approximation and residual displacements
525     memcpy(dx, tx, no * sizeof(double));
526     memcpy(dy, ty, no * sizeof(double));
527     memcpy(dz, tz, no * sizeof(double));
528     if (this->RequiresCachingOfDisplacements()) {
529       error = EvaluateRMSError(domain, dx, dy, dz);
530     } else {
531       error = EvaluateRMSError(x, y, z, t, dx, dy, dz, no);
532     }
533   }
534 
535   // Free memory
536   Deallocate(param);
537   Deallocate(tx);
538   Deallocate(ty);
539   Deallocate(tz);
540   Deallocate(x);
541   Deallocate(y);
542   Deallocate(z);
543   Deallocate(t);
544 
545   return error;
546 }
547 
548 // -----------------------------------------------------------------------------
549 double FreeFormTransformation
Approximate(const double * x,const double * y,const double * z,double * dx,double * dy,double * dz,int no,int niter,double max_error)550 ::Approximate(const double *x,  const double *y,  const double *z,
551               double       *dx, double       *dy, double       *dz, int no,
552               int niter, double max_error)
553 {
554   if (no <= 0) return .0;
555   double error = numeric_limits<double>::infinity();
556   if (niter < 1) return error;
557 
558   // Compute (fixed) time coordinates
559   const double t0 = .0;
560   double *t = CAllocate<double>(no, &t0);
561 
562   // Copy original displacements
563   double *tx = Allocate<double>(no);
564   double *ty = Allocate<double>(no);
565   double *tz = Allocate<double>(no);
566   memcpy(tx, dx, no * sizeof(double));
567   memcpy(ty, dy, no * sizeof(double));
568   memcpy(tz, dz, no * sizeof(double));
569 
570   // Evaluate error of approximation and residual displacements
571   error = EvaluateRMSError(x, y, z, t0, dx, dy, dz, no);
572 
573   // Repeat approximation n times or until error drops below a threshold
574   DOFValue *param = Allocate<DOFValue>(_NumberOfDOFs);
575   for (int iter = 0; iter < niter && error > max_error; ++iter) {
576 
577     // Copy current parameters
578     this->Get(param);
579 
580     // Approximate residual displacements by new parameters
581     this->ApproximateDOFs(x, y, z, t, dx, dy, dz, no);
582 
583     // Add previous parameters
584     this->Add(param);
585 
586     // Evaluate error of approximation and residual displacements
587     memcpy(dx, tx, no * sizeof(double));
588     memcpy(dy, ty, no * sizeof(double));
589     memcpy(dz, tz, no * sizeof(double));
590     error = EvaluateRMSError(x, y, z, t0, dx, dy, dz, no);
591   }
592 
593   // Free memory
594   Deallocate(param);
595   Deallocate(tx);
596   Deallocate(ty);
597   Deallocate(tz);
598   Deallocate(t);
599 
600   return error;
601 }
602 
603 // -----------------------------------------------------------------------------
604 double FreeFormTransformation
Approximate(const double * x,const double * y,const double * z,const double * t,double * dx,double * dy,double * dz,int no,int niter,double max_error)605 ::Approximate(const double *x,  const double *y,  const double *z,  const double *t,
606               double       *dx, double       *dy, double       *dz, int no,
607               int niter, double max_error)
608 {
609   if (no <= 0) return .0;
610   double error = numeric_limits<double>::infinity();
611   if (niter < 1) return error;
612 
613   // Copy original displacements
614   double *tx = Allocate<double>(no);
615   double *ty = Allocate<double>(no);
616   double *tz = Allocate<double>(no);
617   memcpy(tx, dx, no * sizeof(double));
618   memcpy(ty, dy, no * sizeof(double));
619   memcpy(tz, dz, no * sizeof(double));
620 
621   // Evaluate error of approximation and residual displacements
622   error = EvaluateRMSError(x, y, z, t, dx, dy, dz, no);
623 
624   // Repeat approximation n times or until error drops below a threshold
625   DOFValue *param = Allocate<DOFValue>(_NumberOfDOFs);
626   for (int iter = 0; iter < niter && error > max_error; ++iter) {
627 
628     // Copy current parameters
629     this->Get(param);
630 
631     // Approximate residual displacements by new parameters
632     this->ApproximateDOFs(x, y, z, t, dx, dy, dz, no);
633 
634     // Add previous parameters
635     this->Add(param);
636 
637     // Evaluate error of approximation and residual displacements
638     memcpy(dx, tx, no * sizeof(double));
639     memcpy(dy, ty, no * sizeof(double));
640     memcpy(dz, tz, no * sizeof(double));
641     error = EvaluateRMSError(x, y, z, t, dx, dy, dz, no);
642   }
643 
644   // Free memory
645   Deallocate(param);
646   Deallocate(tx);
647   Deallocate(ty);
648   Deallocate(tz);
649 
650   return error;
651 }
652 
653 // -----------------------------------------------------------------------------
ApproximateAsNew(const Transformation * t,int niter,double max_error)654 double FreeFormTransformation::ApproximateAsNew(const Transformation *t, int niter, double max_error)
655 {
656   return this->ApproximateAsNew(_attr, t, niter, max_error);
657 }
658 
659 // -----------------------------------------------------------------------------
660 double FreeFormTransformation
ApproximateAsNew(const ImageAttributes & domain,double * dx,double * dy,double * dz,int niter,double max_error)661 ::ApproximateAsNew(const ImageAttributes &domain, double *dx, double *dy, double *dz, int niter, double max_error)
662 {
663   bool ignore_time = (AreEqual(domain._dt, 0.) || domain._t == 1) && (AreEqual(_dt, 0.) || _t == 1);
664   if ((!ignore_time && domain == _attr) || (ignore_time && domain.EqualInSpace(_attr))) {
665     this->Interpolate(dx, dy, dz);
666     return .0; // No approximation error at lattice/control points
667   }
668   return Transformation::ApproximateAsNew(domain, dx, dy, dz, niter, max_error);
669 }
670 
671 // =============================================================================
672 // Lattice
673 // =============================================================================
674 
675 // -----------------------------------------------------------------------------
CropPadPassiveCPs(int margin,bool reset)676 bool FreeFormTransformation::CropPadPassiveCPs(int margin, bool reset)
677 {
678   return this->CropPadPassiveCPs(margin, margin, margin, margin, reset);
679 }
680 
681 // -----------------------------------------------------------------------------
CropPadPassiveCPs(int mx,int my,int mz,int mt,bool reset)682 bool FreeFormTransformation::CropPadPassiveCPs(int mx, int my, int mz, int mt, bool reset)
683 {
684   ImageAttributes attr = _attr;
685   // Determine lower bound along x axis: i1
686   int i1 = _x;
687   for (int l = 0; l < _t; ++l)
688   for (int k = 0; k < _z; ++k)
689   for (int j = 0; j < _y; ++j)
690   for (int i = 0; i < _x; ++i) {
691     if (IsActive(i, j, k, l)) {
692       if (i < i1) i1 = i;
693       break;
694     }
695   }
696   // Determine upper bound along x axis: i2
697   int i2 = -1;
698   for (int l = 0;      l <  _t; ++l)
699   for (int k = 0;      k <  _z; ++k)
700   for (int j = 0;      j <  _y; ++j)
701   for (int i = _x - 1; i >= i1; --i) {
702     if (IsActive(i, j, k, l)) {
703       if (i > i2) i2 = i;
704       break;
705     }
706   }
707   // Determine lower bound along y axis: j1
708   int j1 = _y;
709   for (int l = 0;  l <  _t; ++l)
710   for (int k = 0;  k <  _z; ++k)
711   for (int i = i1; i <= i2; ++i)
712   for (int j = 0;  j <  _y; ++j) {
713     if (IsActive(i, j, k, l)) {
714       if (j < j1) j1 = j;
715       break;
716     }
717   }
718   // Determine upper bound along y axis: j2
719   int j2 = -1;
720   for (int l = 0;      l <  _t; ++l)
721   for (int k = 0;      k <  _z; ++k)
722   for (int i = i1;     i <= i2; ++i)
723   for (int j = _y - 1; j >= j1; --j) {
724     if (IsActive(i, j, k, l)) {
725       if (j > j2) j2 = j;
726       break;
727     }
728   }
729   // Determine lower bound along z axis: k1
730   int k1 = _z;
731   for (int l = 0;  l <  _t; ++l)
732   for (int j = j1; j <= j2; ++j)
733   for (int i = i1; i <= i2; ++i)
734   for (int k = 0;  k <  _z; ++k) {
735     if (IsActive(i, j, k, l)) {
736       if (k < k1) k1 = k;
737       break;
738     }
739   }
740   // Determine upper bound along z axis: k2
741   int k2 = -1;
742   for (int l = 0;      l <  _t; ++l)
743   for (int j = j1;     j <= j2; ++j)
744   for (int i = i1;     i <= i2; ++i)
745   for (int k = _z - 1; k >= k1; --k) {
746     if (IsActive(i, j, k, l)) {
747       if (k > k2) k2 = k;
748        break;
749     }
750   }
751   // Determine lower bound along t axis: l1
752   int l1 = _t;
753   for (int k = k1; k <= k2; ++k)
754   for (int j = j1; j <= j2; ++j)
755   for (int i = i1; i <= i2; ++i)
756   for (int l = 0;  l <  _t; ++l) {
757     if (IsActive(i, j, k, l)) {
758       if (l < l1) l1 = l;
759       break;
760     }
761   }
762   // Determine upper bound along t axis: l2
763   int l2 = -1;
764   for (int k = k1;     k <= k2; ++k)
765   for (int j = j1;     j <= j2; ++j)
766   for (int i = i1;     i <= i2; ++i)
767   for (int l = _t - 1; l >= l1; --l) {
768     if (IsActive(i, j, k, l)) {
769       if (l > l2) l2 = l;
770       break;
771     }
772   }
773   // Do nothing if all control points are passive, but report it
774   if (i1 > i2 || j1 > j2 || k1 > k2 || l1 > l2) return false;
775   // Convert upper index bounds to margin widths
776   i2 = (_x - 1) - i2;
777   j2 = (_y - 1) - j2;
778   k2 = (_z - 1) - k2;
779   l2 = (_t - 1) - l2;
780   // Leave a margin of passive control points with specified width
781   // Note: Negative value gives the number of control points to add.
782   if (_x > 1) i1 -= mx, i2 -= mx;
783   if (_y > 1) j1 -= my, j2 -= my;
784   if (_z > 1) k1 -= mz, k2 -= mz;
785   if (_t > 1) l1 -= mt, l2 -= mt;
786   // Do nothing, if nothing to be done
787   if (i1 == 0 && i2 == 0 && j1 == 0 && j2 == 0 &&
788       k1 == 0 && k2 == 0 && l1 == 0 && l2 == 0) return true;
789   // Adjust control point lattice
790   attr._x -= i1 + i2;
791   attr._y -= j1 + j2;
792   attr._z -= k1 + k2;
793   attr._t -= l1 + l2;
794   attr._xorigin = 0.5 * ((_x - 1) + (i1 - i2));
795   attr._yorigin = 0.5 * ((_y - 1) + (j1 - j2));
796   attr._zorigin = 0.5 * ((_z - 1) + (k1 - k2));
797   this->LatticeToWorld(attr._xorigin, attr._yorigin, attr._zorigin);
798   attr._torigin = this->LatticeToTime(l1);
799   // Convert upper margin widths to index bounds
800   i2 = (_x - 1) - i2;
801   j2 = (_y - 1) - j2;
802   k2 = (_z - 1) - k2;
803   l2 = (_t - 1) - l2;
804   // Copy remaining control points and pad lattice where needed
805   const int ncps = attr.NumberOfPoints();
806   CPValue  *param       = Allocate<CPValue >(ncps);
807   CPStatus *status      = Allocate<CPStatus>(ncps);
808   CPValue  *param_iter  = param;
809   CPStatus *status_iter = status;
810   for (int l = l1; l <= l2; ++l)
811   for (int k = k1; k <= k2; ++k)
812   for (int j = j1; j <= j2; ++j)
813   for (int i = i1; i <= i2; ++i, ++param_iter, ++status_iter) {
814     if (0 <= i && i < _x &&
815         0 <= j && j < _y &&
816         0 <= k && k < _z &&
817         0 <= l && l < _t) {
818       (*status_iter) = _CPStatus[l][k][j][i];
819       (*param_iter)  = _CPImage(i, j, k, l);
820     } else {
821       // Padded control point to extend margin
822       (*status_iter) = CPStatus(Passive);
823       (*param_iter)  = CPValue (.0);
824     }
825   }
826   // Initialize new control point lattice
827   this->Initialize(attr);
828   param_iter  = param;
829   status_iter = status;
830   for (int l = 0; l < _t; ++l)
831   for (int k = 0; k < _z; ++k)
832   for (int j = 0; j < _y; ++j)
833   for (int i = 0; i < _x; ++i, ++param_iter, ++status_iter) {
834     _CPStatus[l][k][j][i] = (*status_iter);
835     if (// Copy all control point values by default
836         !reset ||
837         // Always if control point status is not passive...
838         (status_iter->_x != Passive) ||
839         (status_iter->_y != Passive) ||
840         (status_iter->_z != Passive) ||
841         // ...or control point is not within the boundary margin
842         i >= mx || i < _x - mx ||
843         j >= my || j < _y - my ||
844         k >= mz || k < _z - mz ||
845         l >= mt || l < _t - mt) {
846       _CPImage(i, j, k, l) = (*param_iter);
847     }
848   }
849   // Free temporary allocated memory
850   Deallocate(param);
851   Deallocate(status);
852   return true;
853 }
854 
855 // =============================================================================
856 // Bounding box
857 // =============================================================================
858 
859 // -----------------------------------------------------------------------------
PutBoundingBox(double x1,double y1,double z1,double x2,double y2,double z2)860 void FreeFormTransformation::PutBoundingBox(double x1, double y1, double z1,
861                                             double x2, double y2, double z2)
862 {
863   const ImageAttributes &attr = Attributes();
864   double a, b, c;
865 
866   // Update lattice origin
867   _CPImage.PutOrigin((x2 + x1) / 2.0, (y2 + y1) / 2.0, (z2 + z1) / 2.0);
868 
869   // FOV in lattice orientation
870   a = x1 * attr._xaxis[0] + y1 * attr._xaxis[1] + z1 * attr._xaxis[2];
871   b = x1 * attr._yaxis[0] + y1 * attr._yaxis[1] + z1 * attr._yaxis[2];
872   c = x1 * attr._zaxis[0] + y1 * attr._zaxis[1] + z1 * attr._zaxis[2];
873   x1 = a, y1 = b, z1 = c;
874 
875   a = x2 * attr._xaxis[0] + y2 * attr._xaxis[1] + z2 * attr._xaxis[2];
876   b = x2 * attr._yaxis[0] + y2 * attr._yaxis[1] + z2 * attr._yaxis[2];
877   c = x2 * attr._zaxis[0] + y2 * attr._zaxis[1] + z2 * attr._zaxis[2];
878   x2 = a, y2 = b, z2 = c;
879 
880   // Update lattice spacing
881   _CPImage.PutPixelSize(((x2 > x1) ? ((x2 - x1) / (attr._x - 1)) : 1),
882                         ((y2 > y1) ? ((y2 - y1) / (attr._y - 1)) : 1),
883                         ((z2 > z1) ? ((z2 - z1) / (attr._z - 1)) : 1));
884 
885   this->Changed(true);
886 }
887 
888 // -----------------------------------------------------------------------------
PutBoundingBox(const Point & p1,const Point & p2)889 void FreeFormTransformation::PutBoundingBox(const Point &p1, const Point &p2)
890 {
891   PutBoundingBox(p1._x, p1._y, p1._z, p2._x, p2._y, p2._z);
892 }
893 
894 // ---------------------------------------------------------------------------
PutBoundingBox(double t1,double t2)895 void FreeFormTransformation::PutBoundingBox(double t1, double t2)
896 {
897   _CPImage.PutTOrigin((t2 + t1) / 2.0);
898   _CPImage.PutTSize  ((t2 > t2) ? ((t2 - t1) / (_t - 1)) : 1);
899   this->Changed(true);
900 }
901 
902 // ---------------------------------------------------------------------------
PutBoundingBox(double x1,double y1,double z1,double t1,double x2,double y2,double z2,double t2)903 void FreeFormTransformation::PutBoundingBox(double x1, double y1, double z1, double t1,
904                                             double x2, double y2, double z2, double t2)
905 {
906   PutBoundingBox(x1, y1, z1, x2, y2, z2);
907   PutBoundingBox(t1, t2);
908 }
909 
910 // =============================================================================
911 // Derivatives
912 // =============================================================================
913 
914 // -----------------------------------------------------------------------------
915 /// (Multi-threaded) body of FreeFormTransformation::ParametricGradient
916 ///
917 /// This default implementation uses the FreeFormTransformation::JacobianDOFs
918 /// overload which computes the derivatives of the transformation w.r.t. all
919 /// transformation parameters at once for a given spatial location (target voxel).
920 /// It provides better performance in particular for transformations which are
921 /// parameterized by non-stationary velocity fields (3D+t) in which case the
922 /// derivatives along each temporal trajectory are computed at once and therefore
923 /// this trajectory does not have to be re-computed for each and every control
924 /// point. For classic FFDs, which are parameterized by displacements, less
925 /// general but more efficient implementations of the ParametricGradient function
926 /// are provided by the FreeFormTransformation3D and
927 /// FreeFormTransformation4D subclasses, which therefore override this
928 /// FreeFormTransformation::ParametricGradient implementation. If a subclass
929 /// of either of these subclasses is parameterized by velocities, it must therefore
930 /// override the ParametericGradient function again, for example as follows,
931 /// unless a more specialized gradient computation is provided by this subclass.
932 ///
933 /// \code
934 /// void BSplineFreeFormTransformationTD::ParametricGradient(...)
935 /// {
936 ///   FreeFormTransformation::ParametricGradient(...);
937 /// }
938 /// \endcode
939 class FreeFormTransformationParametricGradientBody
940 {
941 public:
942   const FreeFormTransformation *_FFD;
943   const GenericImage<double>   *_Input;
944   const WorldCoordsImage       *_WorldCoords;
945   double                       *_Output;
946   double                        _Weight;
947 
948   double _t;  ///< Time corrresponding to input gradient image (in ms)
949   double _t0; ///< Second time argument for velocity-based transformations
950 
951 private:
952 
953   int    _X;  ///< Number of voxels along x axis
954   int    _Y;  ///< Number of voxels along y axis
955 
956 public:
957 
958   // ---------------------------------------------------------------------------
959   /// Default constructor
FreeFormTransformationParametricGradientBody()960   FreeFormTransformationParametricGradientBody()
961   :
962     _FFD        (NULL),
963     _Input      (NULL),
964     _WorldCoords(NULL),
965     _Output     (NULL),
966     _Weight     ( 1.0),
967     _t          ( 0.0),
968     _t0         (-1.0),
969     _X          (0),
970     _Y          (0)
971   {}
972 
973   // ---------------------------------------------------------------------------
974   /// Split constructor
FreeFormTransformationParametricGradientBody(const FreeFormTransformationParametricGradientBody & other,split)975   FreeFormTransformationParametricGradientBody(const FreeFormTransformationParametricGradientBody &other, split)
976   :
977     _FFD        (other._FFD),
978     _Input      (other._Input),
979     _WorldCoords(other._WorldCoords),
980     _Output     (NULL),
981     _Weight     (other._Weight),
982     _t          (other._t),
983     _t0         (other._t0),
984     _X          (other._X),
985     _Y          (other._Y)
986   {
987     const int ndofs = _FFD->NumberOfDOFs();
988     _Output         = new double[ndofs];
989     memset(_Output, 0, ndofs * sizeof(double));
990   }
991 
992   // ---------------------------------------------------------------------------
993   /// Destructor
~FreeFormTransformationParametricGradientBody()994   ~FreeFormTransformationParametricGradientBody() {}
995 
996   // ---------------------------------------------------------------------------
997   /// Join results of right-hand body with this body
join(FreeFormTransformationParametricGradientBody & rhs)998   void join(FreeFormTransformationParametricGradientBody &rhs)
999   {
1000     const int ndofs = _FFD->NumberOfDOFs();
1001     for (int dof = 0; dof < ndofs; dof++) _Output[dof] += rhs._Output[dof];
1002     delete[] rhs._Output;
1003     rhs._Output = NULL;
1004   }
1005 
1006   // ---------------------------------------------------------------------------
1007   /// Calculates the gradient of the similarity term w.r.t. the transformation
1008   /// parameters for each voxel in the specified image region
operator ()(const blocked_range3d<int> & re)1009   void operator ()(const blocked_range3d<int> &re)
1010   {
1011     const int i1 = re.cols ().begin();
1012     const int j1 = re.rows ().begin();
1013     const int k1 = re.pages().begin();
1014     const int i2 = re.cols ().end();
1015     const int j2 = re.rows ().end();
1016     const int k2 = re.pages().end();
1017 
1018     TransformationJacobian                      jac;
1019     TransformationJacobian::ConstColumnIterator it;
1020 
1021     //s1=1
1022     const int s2 =  _X - (i2 - i1);
1023     const int s3 = (_Y - (j2 - j1)) * _X;
1024 
1025     // Non-parametric/voxelwise gradient
1026     const double *gx = _Input->Data(i1, j1, k1, 0);
1027     const double *gy = _Input->Data(i1, j1, k1, 1);
1028     const double *gz = _Input->Data(i1, j1, k1, 2);
1029 
1030     // With (transformed) pre-computed world coordinates
1031     if (_WorldCoords) {
1032       const double *wx = _WorldCoords->Data(i1, j1, k1, 0);
1033       const double *wy = _WorldCoords->Data(i1, j1, k1, 1);
1034       const double *wz = _WorldCoords->Data(i1, j1, k1, 2);
1035       for (int k = k1; k < k2; ++k, wx += s3, wy += s3, wz += s3, gx += s3, gy += s3, gz += s3)
1036       for (int j = j1; j < j2; ++j, wx += s2, wy += s2, wz += s2, gx += s2, gy += s2, gz += s2)
1037       for (int i = i1; i < i2; ++i, wx +=  1, wy +=  1, wz +=  1, gx +=  1, gy +=  1, gz +=  1) {
1038         if (*gx || *gy || *gz) {
1039           // Calculate derivatives of transformation w.r.t. the parameters
1040           _FFD->JacobianDOFs(jac, *wx, *wy, *wz, _t, _t0);
1041           // Apply chain rule to obtain similarity gradient w.r.t. the transformation parameters
1042           for (it = jac.Begin(); it != jac.End(); ++it) {
1043             _Output[it->first] += _Weight * (it->second._x * (*gx) +
1044                                              it->second._y * (*gy) +
1045                                              it->second._z * (*gz));
1046           }
1047         }
1048       }
1049     // Without pre-computed world coordinates
1050     } else {
1051       double x, y, z;
1052       for (int k = k1; k < k2; ++k, gx += s3, gy += s3, gz += s3)
1053       for (int j = j1; j < j2; ++j, gx += s2, gy += s2, gz += s2)
1054       for (int i = i1; i < i2; ++i, gx +=  1, gy +=  1, gz +=  1) {
1055         if (*gx || *gy || *gz) {
1056           // Convert voxel to world coordinates
1057           x = i, y = j, z = k;
1058           _Input->ImageToWorld(x, y, z);
1059           // Calculate derivatives of transformation w.r.t. the parameters
1060           _FFD->JacobianDOFs(jac, x, y, z, _t, _t0);
1061           // Apply chain rule to obtain similarity gradient w.r.t. the transformation parameters
1062           for (it = jac.Begin(); it != jac.End(); ++it) {
1063             _Output[it->first] += _Weight * (it->second._x * (*gx) +
1064                                              it->second._y * (*gy) +
1065                                              it->second._z * (*gz));
1066           }
1067         }
1068       }
1069     }
1070   }
1071 
1072   // ---------------------------------------------------------------------------
operator ()()1073   void operator ()()
1074   {
1075     // Initialize members to often accessed data
1076     _X           = _Input->GetX();
1077     _Y           = _Input->GetY();
1078     const int _Z = _Input->GetZ();
1079 
1080     // Check input
1081     if (_WorldCoords && (_WorldCoords->X() != _X ||
1082                          _WorldCoords->Y() != _Y ||
1083                          _WorldCoords->Z() != _Z ||
1084                          _WorldCoords->T() !=  3)) {
1085       cerr << "FreeFormTransformation::ParametricGradient: Invalid world coordinates map" << endl;
1086       exit(1);
1087     }
1088 
1089     if (_Input->GetXSize() > _FFD->GetXSpacing() ||
1090         _Input->GetYSize() > _FFD->GetYSpacing() ||
1091         (_FFD->Z() > 1 && _Input->GetZSize() > _FFD->GetZSpacing())) {
1092       // FIXME: In this case, the non-parametric input gradient should be
1093       //        resampled using linear interpolation to obtain a value at
1094       //        each control point.
1095       cerr << "Warning: FFD spacing smaller than image resolution!" << endl;
1096       cerr << "         This may lead to artifacts in the transformation because" << endl;
1097       cerr << "         not all control points are within the vicinity of a voxel center." << endl;
1098     }
1099 
1100     // Calculate parametric gradient
1101     blocked_range3d<int> voxels(0, _Z, 0, _Y, 0, _X);
1102     parallel_reduce(voxels, *this);
1103   }
1104 
1105 }; // FreeFormTransformationParametricGradientBody
1106 
1107 // -----------------------------------------------------------------------------
1108 class FreeFormTransformationPointWiseParametricGradientBody
1109 {
1110 public:
1111 
1112   const FreeFormTransformation *_FFD;
1113   const PointSet               *_PointSet;
1114   const Vector3D<double>       *_Input;
1115   double                       *_Output;
1116   double                        _Weight;
1117 
1118   double _t;  ///< Time corrresponding to input gradient image (in ms)
1119   double _t0; ///< Second time argument for velocity-based transformations
1120 
1121   // ---------------------------------------------------------------------------
1122   /// Default constructor
FreeFormTransformationPointWiseParametricGradientBody()1123   FreeFormTransformationPointWiseParametricGradientBody()
1124   :
1125     _FFD     (NULL),
1126     _PointSet(NULL),
1127     _Input   (NULL),
1128     _Output  (NULL),
1129     _Weight  ( 1.0),
1130     _t       ( 0.0),
1131     _t0      (-1.0)
1132   {}
1133 
1134   // ---------------------------------------------------------------------------
1135   /// Split constructor
FreeFormTransformationPointWiseParametricGradientBody(const FreeFormTransformationPointWiseParametricGradientBody & other,split)1136   FreeFormTransformationPointWiseParametricGradientBody(const FreeFormTransformationPointWiseParametricGradientBody &other, split)
1137   :
1138     _FFD     (other._FFD),
1139     _PointSet(other._PointSet),
1140     _Input   (other._Input),
1141     _Output  (NULL),
1142     _Weight  (other._Weight),
1143     _t       (other._t),
1144     _t0      (other._t0)
1145   {
1146     const int ndofs = _FFD->NumberOfDOFs();
1147     _Output         = new double[ndofs];
1148     memset(_Output, 0, ndofs * sizeof(double));
1149   }
1150 
1151   // ---------------------------------------------------------------------------
1152   /// Destructor
~FreeFormTransformationPointWiseParametricGradientBody()1153   ~FreeFormTransformationPointWiseParametricGradientBody() {}
1154 
1155   // ---------------------------------------------------------------------------
1156   /// Join results of right-hand body with this body
join(FreeFormTransformationPointWiseParametricGradientBody & rhs)1157   void join(FreeFormTransformationPointWiseParametricGradientBody &rhs)
1158   {
1159     const int ndofs = _FFD->NumberOfDOFs();
1160     for (int dof = 0; dof < ndofs; ++dof) _Output[dof] += rhs._Output[dof];
1161     delete[] rhs._Output;
1162     rhs._Output = NULL;
1163   }
1164 
1165   // ---------------------------------------------------------------------------
1166   /// Calculates the gradient of the similarity term w.r.t. the transformation
1167   /// parameters for the specified points in 3D.
operator ()(const blocked_range<int> & re)1168   void operator ()(const blocked_range<int> &re)
1169   {
1170     TransformationJacobian                      jac;
1171     TransformationJacobian::ConstColumnIterator it;
1172     Point                                       p;
1173 
1174     for (int i = re.begin(); i != re.end(); ++i) {
1175       const Vector3D<double> &g = _Input[i];
1176       // Check whether reference point is valid
1177       if (g._x != .0 || g._y != .0 || g._z != .0) {
1178         // Calculate derivatives of transformation w.r.t. the parameters
1179         _PointSet->GetPoint(i, p);
1180         _FFD->JacobianDOFs(jac, p._x, p._y, p._z, _t, _t0);
1181         // Apply chain rule to obtain gradient w.r.t. the transformation parameters
1182         for (it = jac.Begin(); it != jac.End(); ++it) {
1183           _Output[it->first] += _Weight * (it->second._x * g._x +
1184                                            it->second._y * g._y +
1185                                            it->second._z * g._z);
1186         }
1187       }
1188     }
1189   }
1190 
1191   // ---------------------------------------------------------------------------
operator ()()1192   void operator ()()
1193   {
1194     blocked_range<int> pts(0, _PointSet->Size());
1195     parallel_reduce(pts, *this);
1196   }
1197 
1198 }; // FreeFormTransformationPointWiseParametricGradientBody
1199 
1200 // -----------------------------------------------------------------------------
1201 void FreeFormTransformation
ParametricGradient(const GenericImage<double> * in,double * out,const WorldCoordsImage * i2w,const WorldCoordsImage * wc,double t0,double w) const1202 ::ParametricGradient(const GenericImage<double> *in, double *out,
1203                      const WorldCoordsImage *i2w, const WorldCoordsImage *wc,
1204                      double t0, double w) const
1205 {
1206   MIRTK_START_TIMING();
1207   FreeFormTransformationParametricGradientBody body;
1208   body._FFD         = this;
1209   body._Input       = in;
1210   body._Output      = out;
1211   body._Weight      = w;
1212   body._WorldCoords = (wc ? wc : i2w);
1213   body._t           = in->GetTOrigin();
1214   body._t0          = t0;
1215   body();
1216   MIRTK_DEBUG_TIMING(2, "parametric gradient computation (FFD)");
1217 }
1218 
1219 // -----------------------------------------------------------------------------
1220 void FreeFormTransformation
ParametricGradient(const PointSet & pos,const Vector3D<double> * in,double * out,double t,double t0,double w) const1221 ::ParametricGradient(const PointSet &pos, const Vector3D<double> *in,
1222                      double *out, double t, double t0, double w) const
1223 {
1224   MIRTK_START_TIMING();
1225   FreeFormTransformationPointWiseParametricGradientBody body;
1226   body._FFD      = this;
1227   body._PointSet = &pos;
1228   body._Input    = in;
1229   body._Output   = out;
1230   body._Weight   = w;
1231   body._t        = t;
1232   body._t0       = t0;
1233   body();
1234   MIRTK_DEBUG_TIMING(2, "point-wise parametric gradient computation (FFD)");
1235 }
1236 
1237 // -----------------------------------------------------------------------------
1238 void FreeFormTransformation
FFDJacobianDetDerivative(double[3],const Matrix &,int,double,double,double,double,double,bool,bool) const1239 ::FFDJacobianDetDerivative(double [3], const Matrix &, int, double, double, double, double, double, bool, bool) const
1240 {
1241   Throw(ERR_NotImplemented, __FUNCTION__, "Not implemented");
1242 }
1243 
1244 // =============================================================================
1245 // Properties
1246 // =============================================================================
1247 
1248 // -----------------------------------------------------------------------------
1249 double FreeFormTransformation
BendingEnergy(double x,double y,double z,double t,double t0,bool wrt_world) const1250 ::BendingEnergy(double x, double y, double z, double t, double t0, bool wrt_world) const
1251 {
1252   if (!wrt_world) {
1253     cerr << "FreeFormTransformation::BendingEnergy: Always uses derivatives w.r.t. world coordinates" << endl;
1254     exit(1);
1255   }
1256   // Calculate 2nd order derivatives
1257   Matrix hessian[3];
1258   this->LocalHessian(hessian, x, y, z, t, t0);
1259   // Calculate bending energy
1260   return Bending3D(hessian);
1261 }
1262 
1263 // -----------------------------------------------------------------------------
1264 double FreeFormTransformation
BendingEnergy(bool incl_passive,bool wrt_world) const1265 ::BendingEnergy(bool incl_passive, bool wrt_world) const
1266 {
1267   int    nactive = 0;
1268   double bending = .0;
1269   double x, y, z, t;
1270 
1271   for (int l = 0; l < _t; ++l)
1272   for (int k = 0; k < _z; ++k)
1273   for (int j = 0; j < _y; ++j)
1274   for (int i = 0; i < _x; ++i) {
1275     if (incl_passive || this->IsActive(i, j, k, l)) {
1276       x = i, y = j, z = k;
1277       this->LatticeToWorld(x, y, z);
1278       t = this->LatticeToTime(l);
1279       bending += this->BendingEnergy(x, y, z, t, 1.0, wrt_world);
1280       ++nactive;
1281     }
1282   }
1283 
1284   if (nactive > 0) bending /= nactive;
1285   return bending;
1286 }
1287 
1288 // -----------------------------------------------------------------------------
1289 double FreeFormTransformation
BendingEnergy(const ImageAttributes & attr,double t0,bool wrt_world) const1290 ::BendingEnergy(const ImageAttributes &attr, double t0, bool wrt_world) const
1291 {
1292   const int N = attr.NumberOfPoints();
1293   const int L = attr._dt ? attr._t : 1;
1294   if (N == 0) return .0;
1295 
1296   double x, y, z, t, bending = .0;
1297   for (int l = 0; l < L; ++l) {
1298     t = attr.LatticeToTime(l);
1299     for (int k = 0; k < attr._z; ++k)
1300     for (int j = 0; j < attr._y; ++j)
1301     for (int i = 0; i < attr._x; ++i) {
1302       x = i, y = j, z = k;
1303       attr.LatticeToWorld(x, y, z);
1304       bending += this->BendingEnergy(x, y, z, t, t0, wrt_world);
1305     }
1306   }
1307 
1308   return bending / N;
1309 }
1310 
1311 // -----------------------------------------------------------------------------
BendingEnergyGradient(double *,double,bool,bool,bool) const1312 void FreeFormTransformation::BendingEnergyGradient(double *, double, bool, bool, bool) const
1313 {
1314   cerr << this->NameOfClass() << "::BendingEnergyGradient: Not implemented" << endl;
1315   exit(1);
1316 }
1317 
1318 // =============================================================================
1319 // I/O
1320 // =============================================================================
1321 
1322 // -----------------------------------------------------------------------------
Print(ostream & os,Indent indent) const1323 void FreeFormTransformation::Print(ostream &os, Indent indent) const
1324 {
1325   // Print no. of transformation parameters
1326   os << indent << "Number of DOFs:          " << this->NumberOfDOFs() << endl;
1327   os << indent << "Number of CPs (active):  " << this->NumberOfActiveCPs()<< endl;
1328   os << indent << "Number of CPs (passive): " << this->NumberOfPassiveCPs()<< endl;
1329   os << indent << "Extrapolation mode:      " << ToString(_ExtrapolationMode) << endl;
1330   // Print lattice attributes
1331   os << indent << "Control point lattice:" << endl;
1332   _attr.Print(os, indent + 1);
1333 }
1334 
1335 // -----------------------------------------------------------------------------
WriteCPs(Cofstream & to) const1336 Cofstream &FreeFormTransformation::WriteCPs(Cofstream &to) const
1337 {
1338   // Note: this->NumberOfDOFs() may differ for specialized subclasses!
1339   const int num = 3 * this->NumberOfCPs();
1340   to.WriteAsDouble(reinterpret_cast<const double *>(_CPImage.Data()),    num);
1341   to.WriteAsInt   (reinterpret_cast<const int    *>(_CPStatus[0][0][0]), num);
1342   return to;
1343 }
1344 
1345 
1346 } // namespace mirtk
1347