1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2008-2015 Imperial College London
5  * Copyright 2008-2013 Daniel Rueckert, Julia Schnabel
6  * Copyright 2013-2015 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/Common.h"
22 #include "mirtk/Options.h"
23 
24 #include "mirtk/BaseImage.h"
25 #include "mirtk/IOConfig.h"
26 
27 #ifdef HAVE_MIRTK_Transformation
28   #include "mirtk/Matrix.h"
29   #include "mirtk/Transformation.h"
30   #include "mirtk/HomogeneousTransformation.h"
31   #include "mirtk/MultiLevelTransformation.h"
32 #endif
33 
34 using namespace mirtk;
35 
36 
37 // =============================================================================
38 // Help
39 // =============================================================================
40 
41 // -----------------------------------------------------------------------------
PrintHelp(const char * name)42 void PrintHelp(const char *name)
43 {
44   cout << "\n";
45   cout << "Usage: " << name << " <input> <output> [options]\n";
46   cout << "\n";
47   cout << "Description:\n";
48   cout << "  Modifies the attributes of an image stored in the header.\n";
49   cout << "\n";
50   cout << "Arguments:\n";
51   cout << "  input    Input image.\n";
52   cout << "  output   Output image.\n";
53   cout << "\n";
54   cout << "Optional arguments:\n";
55   cout << "  -spacing, -voxel-size, -size <dx> [<dy> [<dz> [<dt>]]]\n";
56   cout << "      Voxel size (dx, dy, dz in mm, dt in ms)\n";
57   cout << "  -dx <dx>\n";
58   cout << "      Spatial voxel size in x dimension.\n";
59   cout << "  -dy <dy>\n";
60   cout << "      Spatial voxel size in y dimension.\n";
61   cout << "  -dz <dz>\n";
62   cout << "      Spatial voxel size in z dimension.\n";
63   cout << "  -dt, tsize <dt>\n";
64   cout << "      Temporal voxel size (in ms)\n";
65   cout << "  -origin <x> <y> [<z> [<t>]]\n";
66   cout << "      Image spatial origin (in mm)\n";
67   cout << "  -torigin <t>\n";
68   cout << "      Temporal image origin (in ms)\n";
69   cout << "  -orientation <x1> <x2> <x3>  <y1> <y2> <y3>  <z1> <z2> <z3>\n";
70   cout << "      Image orientation. The axes direction vectors are normalized to unit length.\n";
71   cout << "\n";
72   cout << "  -copy-spacing, -copy-voxel-size, -copy-size <image>\n";
73   cout << "      Copy voxel size.\n";
74   cout << "  -copy-origin <image>\n";
75   cout << "      Copy origin.\n";
76   cout << "  -copy-orientation <image>\n";
77   cout << "      Copy orientation.\n";
78   cout << "  -copy-origin-orientation <image>\n";
79   cout << "      Alias for :option:`-copy-origin` :option:`-copy-orientation`.\n";
80   cout << "  -copy-origin-orientation-spacing, -copy-origin-orientation-voxel-size, -copy-origin-orientation-size <image>\n";
81   cout << "      Alias for :option:`-copy-origin` :option:`-copy-orientation` :option:`-copy-spacing`.\n";
82   cout << "  -reset\n";
83   cout << "      Set orientation, origin, and affine transformation matrix to default.\n";
84   cout << "  -reset-dof\n";
85   cout << "      Set affine transformation matrix to default.\n";
86 #ifdef HAVE_MIRTK_Transformation
87   cout << "  -dofin <file>\n";
88   cout << "      Apply transformation to axis, spacing and origin information\n";
89   cout << "      in the header. Note that any shearing that is present is\n";
90   cout << "      stored as additional affine transformation (c.f. :option:`-putdof`).\n";
91   cout << "  -dofin_i <fil>\n";
92   cout << "      Same as :option:`-dofin` but using the inverse transformation.\n";
93   cout << "  -putdof <file>\n";
94   cout << "      Store affine transformation in image header (NIfTI only).\n";
95   cout << "  -putdof_i <fil>\n";
96   cout << "      Same as :option:`-putdof` but using the inverse transformation.\n";
97   cout << "  -dofout <file>\n";
98   cout << "      Save transformation which maps the world coordinates of the\n";
99   cout << "      output image to the world coordinates of the input image.\n";
100   cout << "      Applying this output transformation to the output image\n";
101   cout << "      using the :option:`-dofin` option restores the previous\n";
102   cout << "      image origin, orientation, and voxel size.\n";
103 #endif // HAVE_MIRTK_Transformation
104   cout << "\n";
105   cout << "  -swapxy\n";
106   cout << "      Swap the x and y axis vectors.\n";
107   cout << "  -swapxz\n";
108   cout << "      Swap the x and z axis vectors.\n";
109   cout << "  -swapyz\n";
110   cout << "      Swap the y and z axis vectors.\n";
111   PrintStandardOptions(cout);
112   cout << endl;
113 }
114 
115 // -----------------------------------------------------------------------------
Normalize(double v[3])116 double Normalize(double v[3])
117 {
118   double l = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
119   v[0] /= l, v[1] /= l, v[2] /= l;
120   return l;
121 }
122 
123 // =============================================================================
124 // Main
125 // =============================================================================
126 
127 // -----------------------------------------------------------------------------
main(int argc,char * argv[])128 int main(int argc, char *argv[])
129 {
130   EXPECTS_POSARGS(2);
131 
132   const char *input_name  = POSARG(1);
133   const char *output_name = POSARG(2);
134 
135   InitializeIOLibrary();
136   UniquePtr<BaseImage> image(BaseImage::New(input_name));
137 
138   double origin[4];
139   double xsize, ysize, zsize, tsize;
140   double xaxis[3], yaxis[3], zaxis[3];
141 
142   #ifdef HAVE_MIRTK_Transformation
143     ImageAttributes input_attr = image->Attributes();
144     input_attr._smat.Ident();
145     const Matrix input_mat = input_attr.GetImageToWorldMatrix();
146     const char *dofout_name = nullptr;
147   #endif
148 
149   for (ALL_OPTIONS) {
150     if (OPTION("-spacing") || OPTION("-voxel-size") || OPTION("-size")) {
151       image->GetPixelSize(xsize, ysize, zsize, tsize);
152       PARSE_ARGUMENT(xsize);
153       if (HAS_ARGUMENT) {
154         PARSE_ARGUMENT(ysize);
155         if (HAS_ARGUMENT) PARSE_ARGUMENT(zsize);
156       } else {
157         ysize = zsize = xsize;
158       }
159       if (HAS_ARGUMENT) PARSE_ARGUMENT(tsize);
160       image->PutPixelSize(xsize, ysize, zsize, tsize);
161     }
162     else if (OPTION("-dx")) {
163       image->GetPixelSize(xsize, ysize, zsize);
164       PARSE_ARGUMENT(xsize);
165       image->PutPixelSize(xsize, ysize, zsize);
166     }
167     else if (OPTION("-dy")) {
168       image->GetPixelSize(xsize, ysize, zsize);
169       PARSE_ARGUMENT(ysize);
170       image->PutPixelSize(xsize, ysize, zsize);
171     }
172     else if (OPTION("-dz")) {
173       image->GetPixelSize(xsize, ysize, zsize);
174       PARSE_ARGUMENT(zsize);
175       image->PutPixelSize(xsize, ysize, zsize);
176     }
177     else if (OPTION("-dt") || OPTION("-tsize")) {
178       PARSE_ARGUMENT(tsize);
179       image->PutTSize(tsize);
180     }
181     else if (OPTION("-origin")) {
182       image->GetOrigin(origin[0], origin[1], origin[2], origin[3]);
183       PARSE_ARGUMENT(origin[0]);
184       PARSE_ARGUMENT(origin[1]);
185       if (HAS_ARGUMENT) PARSE_ARGUMENT(origin[2]);
186       if (HAS_ARGUMENT) PARSE_ARGUMENT(origin[3]);
187       image->PutOrigin(origin[0], origin[1], origin[2], origin[3]);
188     }
189     else if (OPTION("-torigin")) {
190       PARSE_ARGUMENT(origin[3]);
191       image->PutTOrigin(origin[3]);
192     }
193     else if (OPTION("-orientation")) {
194       PARSE_ARGUMENT(xaxis[0]);
195       PARSE_ARGUMENT(xaxis[1]);
196       PARSE_ARGUMENT(xaxis[2]);
197       Normalize(xaxis);
198       PARSE_ARGUMENT(yaxis[0]);
199       PARSE_ARGUMENT(yaxis[1]);
200       PARSE_ARGUMENT(yaxis[2]);
201       Normalize(yaxis);
202       PARSE_ARGUMENT(zaxis[0]);
203       PARSE_ARGUMENT(zaxis[1]);
204       PARSE_ARGUMENT(zaxis[2]);
205       Normalize(zaxis);
206       image->PutOrientation(xaxis, yaxis, zaxis);
207     }
208     else if (OPTION("-copy-spacing") || OPTION("-copy-voxel-size") || OPTION("-copy-size")) {
209       GreyImage target(ARGUMENT);
210       target.GetPixelSize(&xsize, &ysize, &zsize, &tsize);
211       image->PutPixelSize(xsize, ysize, zsize, tsize);
212     }
213     else if (OPTION("-copy-origin")) {
214       GreyImage target(ARGUMENT);
215       target.GetOrigin(origin[0], origin[1], origin[2], origin[3]);
216       image->PutOrigin(origin[0], origin[1], origin[2], origin[3]);
217     }
218     else if (OPTION("-copy-orientation")) {
219       GreyImage target(ARGUMENT);
220       target.GetOrientation(xaxis, yaxis, zaxis);
221       image->PutOrientation(xaxis, yaxis, zaxis);
222     }
223     else if (OPTION("-copy-origin-orientation")) {
224       GreyImage target(ARGUMENT);
225       target.GetOrigin(origin[0], origin[1], origin[2], origin[3]);
226       image->PutOrigin(origin[0], origin[1], origin[2], origin[3]);
227       target.GetOrientation(xaxis, yaxis, zaxis);
228       image->PutOrientation(xaxis, yaxis, zaxis);
229     }
230     else if (OPTION("-copy-origin-orientation-spacing") ||
231              OPTION("-copy-origin-orientation-voxel-size") ||
232              OPTION("-copy-origin-orientation-size")) {
233       GreyImage target(ARGUMENT);
234       target.GetPixelSize(&xsize, &ysize, &zsize);
235       image->PutPixelSize(xsize, ysize, zsize);
236       target.GetOrientation(xaxis, yaxis, zaxis);
237       image->PutOrientation(xaxis, yaxis, zaxis);
238       target.GetOrigin(origin[0], origin[1], origin[2], origin[3]);
239       image->PutOrigin(origin[0], origin[1], origin[2], origin[3]);
240     }
241     #ifdef HAVE_MIRTK_Transformation
242     else if (OPTION("-dofin") || OPTION("-dofin_i") || OPTION("-putdof") || OPTION("-putdof_i")) {
243       Matrix m;
244       UniquePtr<Transformation> dof(Transformation::New(ARGUMENT));
245       HomogeneousTransformation *lin  = dynamic_cast<HomogeneousTransformation *>(dof.get());
246       MultiLevelTransformation  *mffd = dynamic_cast<MultiLevelTransformation  *>(dof.get());
247       if      (lin)  m = lin->GetMatrix();
248       else if (mffd) m = mffd->GetGlobalTransformation()->GetMatrix();
249       else {
250         FatalError("Option -dofin requires a rigid, similarity, affine or MFFD transformation as argument");
251       }
252       if      (strcmp(OPTNAME, "-dofin"    ) == 0) image->PutAffineMatrix(m,          true);
253       else if (strcmp(OPTNAME, "-dofin_i"  ) == 0) image->PutAffineMatrix(m.Invert(), true);
254       else if (strcmp(OPTNAME, "-putdof"   ) == 0) image->PutAffineMatrix(m,          false);
255       else if (strcmp(OPTNAME, "-putdof_i" ) == 0) image->PutAffineMatrix(m.Invert(), false);
256     }
257     else if (OPTION("-dofout")) {
258       dofout_name = ARGUMENT;
259     }
260     #endif // HAVE_MIRTK_Transformation
261     else if (OPTION("-reset")) {
262       ImageAttributes defaultAttr;
263       image->PutOrientation(defaultAttr._xaxis,defaultAttr._yaxis,defaultAttr._zaxis);
264       image->PutOrigin(defaultAttr._xorigin,defaultAttr._yorigin,defaultAttr._zorigin);
265       image->ResetAffineMatrix();
266     }
267     else if (OPTION("-reset-dof")) {
268       image->ResetAffineMatrix();
269     }
270     else if (OPTION("-swapxy") || OPTION("-swapyx")) {
271       image->GetOrientation(xaxis, yaxis, zaxis);
272       image->PutOrientation(yaxis, xaxis, zaxis);
273     }
274     else if (OPTION("-swapxz") || OPTION("-swapzx")) {
275       image->GetOrientation(xaxis, yaxis, zaxis);
276       image->PutOrientation(zaxis, yaxis, xaxis);
277     }
278     else if (OPTION("-swapyz") || OPTION("-swapzy")) {
279       image->GetOrientation(xaxis, yaxis, zaxis);
280       image->PutOrientation(xaxis, zaxis, yaxis);
281     }
282     else HANDLE_COMMON_OR_UNKNOWN_OPTION();
283   }
284 
285   #ifdef HAVE_MIRTK_Transformation
286     if (dofout_name) {
287       ImageAttributes output_attr = image->Attributes();
288       output_attr._smat.Ident();
289       const Matrix output_mat = output_attr.GetImageToWorldMatrix();
290       AffineTransformation dofout;
291       dofout.PutMatrix(input_mat * output_mat.Inverse());
292       dofout.Write(dofout_name);
293     }
294   #endif
295 
296   if (verbose) image->Print();
297   image->Write(output_name);
298 
299   return 0;
300 }
301