1 /*=========================================================================
4 Module: $RCSfile: gdcmOrientation.cxx,v $
6 Date: $Date: 2005/10/21 16:02:01 $
7 Version: $Revision: 1.18 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
19 #include "gdcmOrientation.h"
21 #include "gdcmDebug.h"
22 #include <math.h> // for sqrt
26 //--------------------------------------------------------------------
27 // THERALYS Algorithm to determine the most similar basic orientation
29 // Transliterated from original Python code.
30 // Kept as close as possible to the original code
31 // in order to speed up any further modif of Python code :-(
32 //-----------------------------------------------------------------------
35 * \brief THERALYS' Algorithm to determine the most similar basic orientation
36 * (Axial, Coronal, Sagital) of the image
37 * \note Should be run on the first gdcm::File of a 'coherent' Serie
38 * @return orientation code
39 * # 0 : Not Applicable (neither 0020,0037 Image Orientation Patient
40 * # nor 0020,0032 Image Position found)
44 * # -2 : Coronal invert
46 * # -3 : Sagital invert
48 * # -4 : Heart Axial invert
50 * # -5 : Heart Coronal invert
52 * # -6 : Heart Sagital invert
55 static const char *OrientationTypeStrings[] = {
67 "Heart Coronal invert",
68 "Heart Sagital invert",
72 const char* Orientation::GetOrientationTypeString(OrientationType const o)
78 return OrientationTypeStrings[k];
81 OrientationType Orientation::GetOrientationType( File *f )
84 bool succ = f->GetImageOrientationPatient( iop );
87 gdcmErrorMacro( "No Image Orientation (0020,0037)/(0020,0032) found in the file, cannot proceed." )
93 ori1.x = iop[0]; ori1.y = iop[1]; ori1.z = iop[2];
94 ori2.x = iop[3]; ori2.y = iop[4]; ori2.z = iop[5];
96 // two perpendicular vectors describe one plane
97 double dicPlane[6][2][3] =
98 { { { 1, 0, 0 },{ 0, 1, 0 } }, // Axial
99 { { 1, 0, 0 },{ 0, 0, -1 } }, // Coronal
100 { { 0, 1, 0 },{ 0, 0, -1 } }, // Sagittal
101 { { 0.8, 0.5, 0.0 },{-0.1, 0.1 , -0.95 } }, // Axial - HEART
102 { { 0.8, 0.5, 0.0 },{-0.6674, 0.687, 0.1794} }, // Coronal - HEART
103 { {-0.1, 0.1, -0.95},{-0.6674, 0.687, 0.1794} } // Sagittal - HEART
109 Res res; // [ <result> , <memory of the last succes calcule> ]
113 for (int numDicPlane=0; numDicPlane<6; numDicPlane++)
117 refA.x = dicPlane[numDicPlane][0][0];
118 refA.y = dicPlane[numDicPlane][0][1];
119 refA.z = dicPlane[numDicPlane][0][2];
121 refB.x = dicPlane[numDicPlane][1][0];
122 refB.y = dicPlane[numDicPlane][1][1];
123 refB.z = dicPlane[numDicPlane][1][2];
124 res=VerfCriterion( i, CalculLikelyhood2Vec(refA,refB,ori1,ori2), res );
125 res=VerfCriterion( -i, CalculLikelyhood2Vec(refB,refA,ori1,ori2), res );
127 // res thought looks like is a float value, but is indeed an int
128 // try casting it to int first then enum value to please VS7:
129 int int_res = (int)res.first;
130 gdcmAssertMacro( int_res <= 6 && int_res >= -6);
131 return (OrientationType)int_res;
135 Orientation::VerfCriterion(int typeCriterion, double criterionNew, Res const &in)
138 double type = in.first;
139 double criterion = in.second;
140 if (/*criterionNew < 0.1 && */criterionNew < criterion)
142 type = typeCriterion;
143 criterion = criterionNew;
146 res.second = criterion;
150 inline double square_dist(vector3D const &v1, vector3D const &v2)
153 res = (v1.x - v2.x)*(v1.x - v2.x) +
154 (v1.y - v2.y)*(v1.y - v2.y) +
155 (v1.z - v2.z)*(v1.z - v2.z);
159 //------------------------- Purpose : -----------------------------------
160 //- This function determines the orientation similarity of two planes.
161 // Each plane is described by two vectors.
162 //------------------------- Parameters : --------------------------------
163 //- <refA> : - type : vector 3D (double)
164 //- <refB> : - type : vector 3D (double)
165 // - Description of the first plane
166 //- <ori1> : - type : vector 3D (double)
167 //- <ori2> : - type : vector 3D (double)
168 // - Description of the second plane
169 //------------------------- Return : ------------------------------------
170 // double : 0 if the planes are perpendicular. While the difference of
171 // the orientation between the planes are big more enlarge is
173 //------------------------- Other : -------------------------------------
174 // The calculus is based with vectors normalice
176 Orientation::CalculLikelyhood2Vec(vector3D const &refA, vector3D const &refB,
177 vector3D const &ori1, vector3D const &ori2 )
180 vector3D ori3 = ProductVectorial(ori1,ori2);
181 vector3D refC = ProductVectorial(refA,refB);
182 double res = square_dist(refC, ori3);
187 //------------------------- Purpose : -----------------------------------
188 //- Calculus of the poduct vectorial between two vectors 3D
189 //------------------------- Parameters : --------------------------------
190 //- <vec1> : - type : vector 3D (double)
191 //- <vec2> : - type : vector 3D (double)
192 //------------------------- Return : ------------------------------------
193 // (vec) : - Vector 3D
194 //------------------------- Other : -------------------------------------
196 Orientation::ProductVectorial(vector3D const &vec1, vector3D const &vec2)
199 vec3.x = vec1.y*vec2.z - vec1.z*vec2.y;
200 vec3.y = -( vec1.x*vec2.z - vec1.z*vec2.x);
201 vec3.z = vec1.x*vec2.y - vec1.y*vec2.x;
207 // ------------------------------------------------------------------------
209 2.2.2 Orientation of DICOM images
212 http://www.dclunie.com/medical-image-faq/html/part2.html#DICOMOrientation
215 A question that is frequently asked in comp.protocols.dicom is how to determine
216 which side of an image is which (e.g. left, right) and so on.
217 The short answer is that for projection radiographs this is specified
218 explicitly using the "Patient Orientation" attribute, and for cross-sectional
219 images it needs to be derived from the "Image Orientation (Patient)" direction
220 cosines. In the standard these are explained as follows:
222 * "C.7.6.1.1.1 Patient Orientation.
223 The Patient Orientation (0020,0020) relative to the image
224 plane shall be specified by two values that designate the
225 anatomical direction of the positive row axis (left to right)
226 and the positive column axis (top to bottom).
227 The first entry is the direction of the rows, given by the
228 direction of the last pixel in the first row from the first
230 The second entry is the direction of the columns, given by
231 the direction of the last pixel in the first column from the
232 first pixel in that column.
233 Anatomical direction shall be designated by the capital
234 letters: A (anterior), P (posterior), R (right),L (left),
236 Each value of the orientation attribute shall contain at
237 least one of these characters.
238 If refinements in the orientation descriptions are to be
239 specified, then they shall be designated by one or two
240 additional letters in each value.
241 Within each value, the letters shall be ordered with the
242 principal orientation designated in the first character."
244 * "C.7.6.2.1.1 Image Position And Image Orientation.
245 The "Image Position (Patient)" (0020,0032) specifies the x, y,
246 and z coordinates of the upper left hand corner of the image;
247 it is the center of the first voxel transmitted.
248 The "Image Orientation (Patient)" (0020,0037) specifies the
249 direction cosines of the first row and the first column
250 with respect to the patient.
251 These Attributes shall be provided as a pair.
252 Row value for the x, y, and z axes respectively followed by
253 the Column value for the x, y, and z axes respectively.
254 The direction of the axes is defined fully by the patient's
256 The x-axis is increasing to the left hand side of the patient.
257 The y-axis is increasing to the posterior side of the patient.
258 The z-axis is increasing toward the head of the patient.
259 The patient based coordinate system is a right handed system,
260 i.e. the vector cross product of a unit vector along the
261 positive x-axis and a unit vector along the positive y-axis
262 is equal to a unit vector along the positive z-axis."
264 Some simple code to take one of the direction cosines (vectors) from the
265 Image Orientation (Patient) attribute and generate strings equivalent to one
266 of the values of Patient Orientation looks like this (noting that if the vector
267 is not aligned exactly with one of the major axes, the resulting string will
268 have multiple letters in as described under "refinements" in C.7.6.1.1.1):
273 * \brief computes the Patient Orientation relative to the image plane
274 * from the 'Image Orientation (Patient)'
275 * The first entry is the direction of the rows, given by the
276 * direction of the last pixel in the first row from the first
278 * The second entry is the direction of the columns, given by
279 * the direction of the last pixel in the first column from the
280 * first pixel in that column.
281 * Anatomical direction is designated by the capital
282 * letters: A (anterior), P (posterior), R (right),L (left),
283 * H (head), F (foot).
284 * Refinements in the orientation descriptions are designated
285 * by one or two additional letters in each value.
286 * Use it when "Patient Orientation" (0020,0020) is not found
287 * @return orientation string as "rawOrientation\columnsOrientation"
289 std::string Orientation::GetOrientation ( File *f )
292 if ( !f->GetImageOrientationPatient( iop ) )
295 std::string orientation;
296 orientation = GetSingleOrientation ( iop )
298 + GetSingleOrientation ( iop + 3 );
303 std::string Orientation::GetSingleOrientation ( float *iop)
305 std::string orientation;
307 char orientationX = iop[0] < 0 ? 'R' : 'L';
308 char orientationY = iop[1] < 0 ? 'A' : 'P';
309 char orientationZ = iop[2] < 0 ? 'F' : 'H';
311 double absX = iop[0];
312 if (absX < 0) absX = -absX;
313 double absY = iop[1];
314 if (absY < 0) absY = -absY;
315 double absZ = iop[2];
316 if (absZ < 0) absZ = -absZ;
318 for (int i=0; i<3; ++i)
320 if (absX>.0001 && absX>absY && absX>absZ)
322 orientation = orientation + orientationX;
325 else if (absY>.0001 && absY>absX && absY>absZ)
327 orientation = orientation + orientationY;
330 else if (absZ>.0001 && absZ>absX && absZ>absY)
332 orientation = orientation + orientationZ;
342 /*-------------------------------------------------------------------
344 Some more stuff, from XMedcon
346 ---> Personal remark from JPRx :
347 --> patient_position (0x0018,0x5100) can be "HFS ", "FFS ", "HFP ", "FFP "
348 --> or, not so common,
349 // HFDR = Head First-Decubitus Right
350 // HFDL = Head First-Decubitus Left
351 // FFDR = Feet First-Decubitus Right
352 // FFDL = Feet First-Decubitus Left
353 --> the cosines may have any value -1.< <+1., for MR images !
355 enum patient_slice_orientation_type
357 patient_slice_orientation_unknown = 0,
358 supine_headfirst_transaxial,
359 supine_headfirst_sagittal,
360 supine_headfirst_coronal,
361 supine_feetfirst_transaxial,
362 supine_feetfirst_sagittal,
363 supine_feetfirst_coronal,
364 prone_headfirst_transaxial,
365 prone_headfirst_sagittal,
366 prone_headfirst_coronal,
367 prone_feetfirst_transaxial,
368 prone_feetfirst_sagittal,
369 prone_feetfirst_coronal
372 void GetImageOrientationPatient(gdcm::File &h,F32 image_orientation_patient[6])
374 h.GetImageOrientationPatient(image_orientation_patient);
379 // this is all completely cribbed from the xmedcon library, since
380 // we're trying to do what it does, mostly.
381 patient_slice_orientation_type
382 GetPatSliceOrient(gdcm::File &h)
384 F32 image_orientation_patient[6];
386 // protected, do it the hard way
387 // h.GetImageOrientationPatient(image_orientation_patient);
388 GetImageOrientationPatient(h,image_orientation_patient);
390 enum { headfirst, feetfirst } patient_orientation;
391 enum { supine, prone } patient_rotation;
392 enum { transaxial, sagittal, coronal } slice_orientation;
394 std::string patient_position = h.GetEntryByNumber(0x0018,0x5100);
395 if(patient_position == "gdcm::Unfound")
397 patient_position = "HF";
399 if(patient_position.find("HF") != std::string::npos)
401 patient_orientation = headfirst;
403 else if(patient_position.find("FF") != std::string::npos)
405 patient_orientation = feetfirst;
408 if(patient_position.find("S") != std::string::npos)
410 patient_rotation = supine;
412 else if(patient_position.find("P") != std::string::npos)
414 patient_rotation = prone;
417 if((image_orientation_patient[0] == 1 || image_orientation_patient[0] == -1) &&
418 (image_orientation_patient[4] == +1 || image_orientation_patient[4] == -1))
420 slice_orientation = transaxial;
422 else if((image_orientation_patient[1] == 1 || image_orientation_patient[1] == -1) &&
423 (image_orientation_patient[5] == +1 || image_orientation_patient[5] == -1))
425 slice_orientation = sagittal;
427 else if((image_orientation_patient[0] == 1 || image_orientation_patient[0] == -1) &&
428 (image_orientation_patient[5] == +1 || image_orientation_patient[5] == -1))
430 slice_orientation = coronal;
434 patient_slice_orientation_type patient_slice_orientation =
435 patient_slice_orientation_unknown;
436 switch (patient_rotation)
439 switch (patient_orientation)
442 switch (slice_orientation)
445 patient_slice_orientation = supine_headfirst_transaxial;
448 patient_slice_orientation = supine_headfirst_sagittal;
451 patient_slice_orientation = supine_headfirst_coronal;
456 switch (slice_orientation)
459 patient_slice_orientation = supine_feetfirst_transaxial;
462 patient_slice_orientation = supine_feetfirst_sagittal;
465 patient_slice_orientation = supine_feetfirst_coronal;
472 switch (patient_orientation)
475 switch (slice_orientation)
478 patient_slice_orientation = prone_headfirst_transaxial;
481 patient_slice_orientation = prone_headfirst_sagittal;
484 patient_slice_orientation = prone_headfirst_coronal;
489 switch (slice_orientation)
492 patient_slice_orientation = prone_feetfirst_transaxial;
495 patient_slice_orientation = prone_feetfirst_sagittal;
498 patient_slice_orientation = prone_feetfirst_coronal;
505 if(patient_slice_orientation != patient_slice_orientation_unknown)
506 return patient_slice_orientation;
508 // this is what xmedcon does
509 if ((image_orientation_patient[0] == +1) &&
510 (image_orientation_patient[4] == +1))
511 patient_slice_orientation = supine_headfirst_transaxial;
512 else if ((image_orientation_patient[0] == -1) &&
513 (image_orientation_patient[4] == +1))
514 patient_slice_orientation = supine_feetfirst_transaxial;
515 else if ((image_orientation_patient[0] == -1) &&
516 (image_orientation_patient[4] == -1))
517 patient_slice_orientation = prone_headfirst_transaxial;
518 else if ((image_orientation_patient[0] == +1) &&
519 (image_orientation_patient[4] == -1))
520 patient_slice_orientation = prone_feetfirst_transaxial;
522 else if ((image_orientation_patient[1] == +1) &&
523 (image_orientation_patient[5] == -1))
524 patient_slice_orientation = supine_headfirst_sagittal;
525 else if ((image_orientation_patient[1] == +1) &&
526 (image_orientation_patient[5] == +1))
527 patient_slice_orientation = supine_feetfirst_sagittal;
528 else if ((image_orientation_patient[1] == -1) &&
529 (image_orientation_patient[5] == -1))
530 patient_slice_orientation = prone_headfirst_sagittal;
531 else if ((image_orientation_patient[1] == -1) &&
532 (image_orientation_patient[5] == +1))
533 patient_slice_orientation = prone_feetfirst_sagittal;
535 else if ((image_orientation_patient[0] == +1) &&
536 (image_orientation_patient[5] == -1))
537 patient_slice_orientation = supine_headfirst_coronal;
538 else if ((image_orientation_patient[0] == -1) &&
539 (image_orientation_patient[5] == +1))
540 patient_slice_orientation = supine_feetfirst_coronal;
541 else if ((image_orientation_patient[0] == -1) &&
542 (image_orientation_patient[5] == -1))
543 patient_slice_orientation = prone_headfirst_coronal;
544 else if ((image_orientation_patient[0] == +1) &&
545 (image_orientation_patient[5] == +1))
546 patient_slice_orientation = prone_feetfirst_coronal;
547 return patient_slice_orientation;
551 -------------------------------------------------------------------------*/
553 } // end namespace gdcm