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