1 /*=========================================================================
4 Module: $RCSfile: gdcmOrientation.cxx,v $
6 Date: $Date: 2005/10/01 19:39:16 $
7 Version: $Revision: 1.15 $
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 gdcmAssertMacro( res.first <= 6 && res.first >= -6);
128 return (OrientationType)res.first;
132 Orientation::VerfCriterion(int typeCriterion, double criterionNew, Res const &in)
135 double type = in.first;
136 double criterion = in.second;
137 if (/*criterionNew < 0.1 && */criterionNew < criterion)
139 type = typeCriterion;
140 criterion = criterionNew;
143 res.second = criterion;
147 inline double square_dist(vector3D const &v1, vector3D const &v2)
150 res = (v1.x - v2.x)*(v1.x - v2.x) +
151 (v1.y - v2.y)*(v1.y - v2.y) +
152 (v1.z - v2.z)*(v1.z - v2.z);
156 //------------------------- Purpose : -----------------------------------
157 //- This function determines the orientation similarity of two planes.
158 // Each plane is described by two vectors.
159 //------------------------- Parameters : --------------------------------
160 //- <refA> : - type : vector 3D (double)
161 //- <refB> : - type : vector 3D (double)
162 // - Description of the first plane
163 //- <ori1> : - type : vector 3D (double)
164 //- <ori2> : - type : vector 3D (double)
165 // - Description of the second plane
166 //------------------------- Return : ------------------------------------
167 // double : 0 if the planes are perpendicular. While the difference of
168 // the orientation between the planes are big more enlarge is
170 //------------------------- Other : -------------------------------------
171 // The calculus is based with vectors normalice
173 Orientation::CalculLikelyhood2Vec(vector3D const &refA, vector3D const &refB,
174 vector3D const &ori1, vector3D const &ori2 )
177 vector3D ori3 = ProductVectorial(ori1,ori2);
178 vector3D refC = ProductVectorial(refA,refB);
179 double res = square_dist(refC, ori3);
184 //------------------------- Purpose : -----------------------------------
185 //- Calculus of the poduct vectorial between two vectors 3D
186 //------------------------- Parameters : --------------------------------
187 //- <vec1> : - type : vector 3D (double)
188 //- <vec2> : - type : vector 3D (double)
189 //------------------------- Return : ------------------------------------
190 // (vec) : - Vector 3D
191 //------------------------- Other : -------------------------------------
193 Orientation::ProductVectorial(vector3D const &vec1, vector3D const &vec2)
196 vec3.x = vec1.y*vec2.z - vec1.z*vec2.y;
197 vec3.y = -( vec1.x*vec2.z - vec1.z*vec2.x);
198 vec3.z = vec1.x*vec2.y - vec1.y*vec2.x;
205 // ---------------------------------------------------------------------------
206 // Here is the original Python code, kindly supplied by THERALYS
208 // C++ code doesn't give good results
213 def TypeOrientation(self,file0):
215 # ------------------------- Purpose : -----------------------------------
216 # - This function compare the orientation of the given image and the
217 # basics orientations (Axial, Cornal, Sagital)
218 # ------------------------- Parameters : --------------------------------
219 # - <file0> : - type : string
220 # - The name of the first image file of the serie
221 # ------------------------- Return : ------------------------------------
225 # -2 : Coronal invert
227 # -3 : Sagital invert
229 # -4 : Heart Axial invert
231 # -5 : Heart Coronal invert
233 # -6 : Heart Sagital invert
235 # ------------------------- Other : -------------------------------------
236 # This method finds the most similar basic orientation.
239 toRead = gdcm.File(file0)
240 ValDict = GetValuesDict(toRead)
242 imageOrientation=ValDict["Image Orientation (Patient)"]
244 imageOrientation=ValDict["Image Orientation"]
246 ori1=[float(split(imageOrientation,"\\")[0]),\
247 float(split(imageOrientation,"\\")[1]),\
248 float(split(imageOrientation,"\\")[2])]
249 ori2=[float(split(imageOrientation,"\\")[3]),\
250 float(split(imageOrientation,"\\")[4]),\
251 float(split(imageOrientation,"\\")[5])]
253 ## two vectors perpendicular describe one plane
254 dicPlane=[ [ [1,0,0],[0,1,0] ], ## Axial
255 [ [1,0,0],[0,0,-1] ], ## Coronal
256 [ [0,1,0],[0,0,-1] ], ## Sagittal
257 [ [ 0.8 , 0.5 , 0.0 ],[-0.1 , 0.1 , -0.95] ],## Axial - HEART
258 [ [ 0.8 , 0.5 , 0.0 ],[-0.6674 , 0.687 , 0.1794] ],## Coronal - HEART
259 [ [-0.1 , 0.1 , -0.95],[-0.6674 , 0.687 , 0.1794] ] ] ## Sagittal - HEART
262 res=[0,99999] ## [ <result> , <memory of the last succes calcule> ]
263 for plane in dicPlane:
267 res=self.VerfCriterion( i , self.CalculLikelyhood2Vec(refA,refB,ori1,ori2) , res )
268 res=self.VerfCriterion( -i , self.CalculLikelyhood2Vec(refB,refA,ori1,ori2) , res )
275 def VerfCriterion(self,typeCriterion,criterionNew,res):
278 # if criterionNew<0.1 and criterionNew<criterion:
279 if criterionNew<criterion:
280 criterion=criterionNew
282 return [ type , criterion ]
285 def CalculLikelyhood2Vec(self,refA,refB,ori1,ori2):
287 # ------------------------- Purpose : -----------------------------------
288 # - This function determine the orientation similarity of two planes.
289 # Each plane is described by two vector.
290 # ------------------------- Parameters : --------------------------------
291 # - <refA> : - type : vector 3D (float)
292 # - <refB> : - type : vector 3D (float)
293 # - Description of the first plane
294 # - <ori1> : - type : vector 3D (float)
295 # - <ori2> : - type : vector 3D (float)
296 # - Description of the second plane
297 # ------------------------- Return : ------------------------------------
298 # float : 0 if the planes are perpendicular.
299 # While the difference of the orientation between the planes
300 # are big more enlarge is
302 # ------------------------- Other : -------------------------------------
303 # The calculus is based with vectors normalice
306 ori3=self.ProductVectorial(ori1,ori2)
307 refC=self.ProductVectorial(refA,refB)
308 res=math.pow(refC[0]-ori3[0],2) + math.pow(refC[1]-ori3[1],2) + math.pow(refC[2]-ori3[2],2)
309 return math.sqrt(res)
311 def ProductVectorial(self,vec1,vec2):
313 # ------------------------- Purpose : -----------------------------------
314 # - Calculus of the poduct vectorial between two vectors 3D
315 # ------------------------- Parameters : --------------------------------
316 # - <vec1> : - type : vector 3D (float)
317 # - <vec2> : - type : vector 3D (float)
318 # ------------------------- Return : ------------------------------------
319 # (vec) : - Vector 3D
320 # ------------------------- Other : -------------------------------------
323 vec3[0]=vec1[1]*vec2[2] - vec1[2]*vec2[1]
324 vec3[1]=-( vec1[0]*vec2[2] - vec1[2]*vec2[0])
325 vec3[2]=vec1[0]*vec2[1] - vec1[1]*vec2[0]
328 def GetValuesDict(image):
330 Returns a dictionnary containing values associated with Field Names
331 dict["Dicom Field Name"]="Dicom field value"
333 val=image.GetFirstEntry()
336 if isinstance(val,gdcm.ValEntryPtr):
337 dic[val.GetName()]=val.GetValue()
338 val=image.GetNextEntry()
344 // ------------------------------------------------------------------------
346 2.2.2 Orientation of DICOM images
349 http://www.dclunie.com/medical-image-faq/html/part2.html#DICOMOrientation
352 A question that is frequently asked in comp.protocols.dicom is how to determine
353 which side of an image is which (e.g. left, right) and so on.
354 The short answer is that for projection radiographs this is specified
355 explicitly using the "Patient Orientation" attribute, and for cross-sectional
356 images it needs to be derived from the "Image Orientation (Patient)" direction
357 cosines. In the standard these are explained as follows:
359 * "C.7.6.1.1.1 Patient Orientation.
360 The Patient Orientation (0020,0020) relative to the image
361 plane shall be specified by two values that designate the
362 anatomical direction of the positive row axis (left to right)
363 and the positive column axis (top to bottom).
364 The first entry is the direction of the rows, given by the
365 direction of the last pixel in the first row from the first
367 The second entry is the direction of the columns, given by
368 the direction of the last pixel in the first column from the
369 first pixel in that column.
370 Anatomical direction shall be designated by the capital
371 letters: A (anterior), P (posterior), R (right),L (left),
373 Each value of the orientation attribute shall contain at
374 least one of these characters.
375 If refinements in the orientation descriptions are to be
376 specified, then they shall be designated by one or two
377 additional letters in each value.
378 Within each value, the letters shall be ordered with the
379 principal orientation designated in the first character."
381 * "C.7.6.2.1.1 Image Position And Image Orientation.
382 The "Image Position (Patient)" (0020,0032) specifies the x, y,
383 and z coordinates of the upper left hand corner of the image;
384 it is the center of the first voxel transmitted.
385 The "Image Orientation (Patient)" (0020,0037) specifies the
386 direction cosines of the first row and the first column
387 with respect to the patient.
388 These Attributes shall be provided as a pair.
389 Row value for the x, y, and z axes respectively followed by
390 the Column value for the x, y, and z axes respectively.
391 The direction of the axes is defined fully by the patient's
393 The x-axis is increasing to the left hand side of the patient.
394 The y-axis is increasing to the posterior side of the patient.
395 The z-axis is increasing toward the head of the patient.
396 The patient based coordinate system is a right handed system,
397 i.e. the vector cross product of a unit vector along the
398 positive x-axis and a unit vector along the positive y-axis
399 is equal to a unit vector along the positive z-axis."
401 Some simple code to take one of the direction cosines (vectors) from the
402 Image Orientation (Patient) attribute and generate strings equivalent to one
403 of the values of Patient Orientation looks like this (noting that if the vector
404 is not aligned exactly with one of the major axes, the resulting string will
405 have multiple letters in as described under "refinements" in C.7.6.1.1.1):
410 * \brief computes the Patient Orientation relative to the image plane
411 * from the 'Image Orientation (Patient)'
412 * The first entry is the direction of the rows, given by the
413 * direction of the last pixel in the first row from the first
415 * The second entry is the direction of the columns, given by
416 * the direction of the last pixel in the first column from the
417 * first pixel in that column.
418 * Anatomical direction is designated by the capital
419 * letters: A (anterior), P (posterior), R (right),L (left),
420 * H (head), F (foot).
421 * Refinements in the orientation descriptions are designated
422 * by one or two additional letters in each value.
423 * Use it when "Patient Orientation" (0020,0020) is not found
424 * @return orientation string as "rawOrientation\columnsOrientation"
426 std::string Orientation::GetOrientation ( File *f )
429 if ( !f->GetImageOrientationPatient( iop ) )
432 std::string orientation;
433 orientation = GetSingleOrientation ( iop )
435 + GetSingleOrientation ( iop + 3 );
440 std::string Orientation::GetSingleOrientation ( float *iop)
442 std::string orientation;
444 char orientationX = iop[0] < 0 ? 'R' : 'L';
445 char orientationY = iop[1] < 0 ? 'A' : 'P';
446 char orientationZ = iop[2] < 0 ? 'F' : 'H';
448 double absX = iop[0];
449 if (absX < 0) absX = -absX;
450 double absY = iop[1];
451 if (absY < 0) absY = -absY;
452 double absZ = iop[2];
453 if (absZ < 0) absZ = -absZ;
455 for (int i=0; i<3; ++i)
457 if (absX>.0001 && absX>absY && absX>absZ)
459 orientation = orientation + orientationX;
462 else if (absY>.0001 && absY>absX && absY>absZ)
464 orientation = orientation + orientationY;
467 else if (absZ>.0001 && absZ>absX && absZ>absY)
469 orientation = orientation + orientationZ;
479 /*-------------------------------------------------------------------
481 Some more stuff, from XMedcon
483 ---> Personal remark from JPRx :
484 --> patient_position (0x0018,0x5100) can be "HFS ", "FFS ", "HFP ", "FFP "
485 --> the cosines may ahave any value -1< <+1, for MR images !
487 enum patient_slice_orientation_type
489 patient_slice_orientation_unknown = 0,
490 supine_headfirst_transaxial,
491 supine_headfirst_sagittal,
492 supine_headfirst_coronal,
493 supine_feetfirst_transaxial,
494 supine_feetfirst_sagittal,
495 supine_feetfirst_coronal,
496 prone_headfirst_transaxial,
497 prone_headfirst_sagittal,
498 prone_headfirst_coronal,
499 prone_feetfirst_transaxial,
500 prone_feetfirst_sagittal,
501 prone_feetfirst_coronal
504 void GetImageOrientationPatient(gdcm::File &h,F32 image_orientation_patient[6])
506 h.GetImageOrientationPatient(image_orientation_patient);
511 // this is all completely cribbed from the xmedcon library, since
512 // we're trying to do what it does, mostly.
513 patient_slice_orientation_type
514 GetPatSliceOrient(gdcm::File &h)
516 F32 image_orientation_patient[6];
518 // protected, do it the hard way
519 // h.GetImageOrientationPatient(image_orientation_patient);
520 GetImageOrientationPatient(h,image_orientation_patient);
522 enum { headfirst, feetfirst } patient_orientation;
523 enum { supine, prone } patient_rotation;
524 enum { transaxial, sagittal, coronal } slice_orientation;
526 std::string patient_position = h.GetEntryByNumber(0x0018,0x5100);
527 if(patient_position == "gdcm::Unfound")
529 patient_position = "HF";
531 if(patient_position.find("HF") != std::string::npos)
533 patient_orientation = headfirst;
535 else if(patient_position.find("FF") != std::string::npos)
537 patient_orientation = feetfirst;
540 if(patient_position.find("S") != std::string::npos)
542 patient_rotation = supine;
544 else if(patient_position.find("P") != std::string::npos)
546 patient_rotation = prone;
549 if((image_orientation_patient[0] == 1 || image_orientation_patient[0] == -1) &&
550 (image_orientation_patient[4] == +1 || image_orientation_patient[4] == -1))
552 slice_orientation = transaxial;
554 else if((image_orientation_patient[1] == 1 || image_orientation_patient[1] == -1) &&
555 (image_orientation_patient[5] == +1 || image_orientation_patient[5] == -1))
557 slice_orientation = sagittal;
559 else if((image_orientation_patient[0] == 1 || image_orientation_patient[0] == -1) &&
560 (image_orientation_patient[5] == +1 || image_orientation_patient[5] == -1))
562 slice_orientation = coronal;
566 patient_slice_orientation_type patient_slice_orientation =
567 patient_slice_orientation_unknown;
568 switch (patient_rotation)
571 switch (patient_orientation)
574 switch (slice_orientation)
577 patient_slice_orientation = supine_headfirst_transaxial;
580 patient_slice_orientation = supine_headfirst_sagittal;
583 patient_slice_orientation = supine_headfirst_coronal;
588 switch (slice_orientation)
591 patient_slice_orientation = supine_feetfirst_transaxial;
594 patient_slice_orientation = supine_feetfirst_sagittal;
597 patient_slice_orientation = supine_feetfirst_coronal;
604 switch (patient_orientation)
607 switch (slice_orientation)
610 patient_slice_orientation = prone_headfirst_transaxial;
613 patient_slice_orientation = prone_headfirst_sagittal;
616 patient_slice_orientation = prone_headfirst_coronal;
621 switch (slice_orientation)
624 patient_slice_orientation = prone_feetfirst_transaxial;
627 patient_slice_orientation = prone_feetfirst_sagittal;
630 patient_slice_orientation = prone_feetfirst_coronal;
637 if(patient_slice_orientation != patient_slice_orientation_unknown)
638 return patient_slice_orientation;
640 // this is what xmedcon does
641 if ((image_orientation_patient[0] == +1) &&
642 (image_orientation_patient[4] == +1))
643 patient_slice_orientation = supine_headfirst_transaxial;
644 else if ((image_orientation_patient[0] == -1) &&
645 (image_orientation_patient[4] == +1))
646 patient_slice_orientation = supine_feetfirst_transaxial;
647 else if ((image_orientation_patient[0] == -1) &&
648 (image_orientation_patient[4] == -1))
649 patient_slice_orientation = prone_headfirst_transaxial;
650 else if ((image_orientation_patient[0] == +1) &&
651 (image_orientation_patient[4] == -1))
652 patient_slice_orientation = prone_feetfirst_transaxial;
654 else if ((image_orientation_patient[1] == +1) &&
655 (image_orientation_patient[5] == -1))
656 patient_slice_orientation = supine_headfirst_sagittal;
657 else if ((image_orientation_patient[1] == +1) &&
658 (image_orientation_patient[5] == +1))
659 patient_slice_orientation = supine_feetfirst_sagittal;
660 else if ((image_orientation_patient[1] == -1) &&
661 (image_orientation_patient[5] == -1))
662 patient_slice_orientation = prone_headfirst_sagittal;
663 else if ((image_orientation_patient[1] == -1) &&
664 (image_orientation_patient[5] == +1))
665 patient_slice_orientation = prone_feetfirst_sagittal;
667 else if ((image_orientation_patient[0] == +1) &&
668 (image_orientation_patient[5] == -1))
669 patient_slice_orientation = supine_headfirst_coronal;
670 else if ((image_orientation_patient[0] == -1) &&
671 (image_orientation_patient[5] == +1))
672 patient_slice_orientation = supine_feetfirst_coronal;
673 else if ((image_orientation_patient[0] == -1) &&
674 (image_orientation_patient[5] == -1))
675 patient_slice_orientation = prone_headfirst_coronal;
676 else if ((image_orientation_patient[0] == +1) &&
677 (image_orientation_patient[5] == +1))
678 patient_slice_orientation = prone_feetfirst_coronal;
679 return patient_slice_orientation;
683 -------------------------------------------------------------------------*/
685 } // end namespace gdcm