1 /*=========================================================================
4 Module: $RCSfile: gdcmOrientation.cxx,v $
6 Date: $Date: 2005/09/29 14:23:58 $
7 Version: $Revision: 1.13 $
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
54 double Orientation::TypeOrientation( File *f )
57 bool succ = f->GetImageOrientationPatient( iop );
60 gdcmErrorMacro( "No Image Orientation (0020,0037) found in the file, cannot proceed." )
64 std::cout << " iop : ";
66 std::cout << iop[i] << " ";
67 std::cout << std::endl;
72 ori1.x = iop[0]; ori1.y = iop[1]; ori1.z = iop[2];
73 ori2.x = iop[3]; ori2.y = iop[4]; ori2.z = iop[5];
75 // two perpendicular vectors describe one plane
76 double dicPlane[6][2][3] =
77 { { { 1, 0, 0 },{ 0, 1, 0 } }, // Axial
78 { { 1, 0, 0 },{ 0, 0, -1 } }, // Coronal
79 { { 0, 1, 0 },{ 0, 0, -1 } }, // Sagittal
80 { { 0.8, 0.5, 0.0 },{-0.1, 0.1 , -0.95 } }, // Axial - HEART
81 { { 0.8, 0.5, 0.0 },{-0.6674, 0.687, 0.1794} }, // Coronal - HEART
82 { {-0.1, 0.1, -0.95},{-0.6674, 0.687, 0.1794} } // Sagittal - HEART
88 Res res; // [ <result> , <memory of the last succes calcule> ]
92 for (int numDicPlane=0; numDicPlane<6; numDicPlane++)
96 refA.x = dicPlane[numDicPlane][0][0];
97 refA.y = dicPlane[numDicPlane][0][1];
98 refA.z = dicPlane[numDicPlane][0][2];
100 refB.x = dicPlane[numDicPlane][1][0];
101 refB.y = dicPlane[numDicPlane][1][1];
102 refB.z = dicPlane[numDicPlane][1][2];
103 res=VerfCriterion( i, CalculLikelyhood2Vec(refA,refB,ori1,ori2), res );
104 res=VerfCriterion( -i, CalculLikelyhood2Vec(refB,refA,ori1,ori2), res );
109 // res=[0,99999] ## [ <result> , <memory of the last succes calculus> ]
110 // for plane in dicPlane:
114 // res=self.VerfCriterion( i , self.CalculLikelyhood2Vec(refA,refB,ori1,ori2) , res )
115 // res=self.VerfCriterion( -i , self.CalculLikelyhood2Vec(refB,refA,ori1,ori2) , res )
121 Orientation::VerfCriterion(int typeCriterion, double criterionNew, Res const &in)
124 double type = in.first;
125 double criterion = in.second;
126 if (/*criterionNew < 0.1 && */criterionNew < criterion)
128 type = typeCriterion;
129 criterion = criterionNew;
132 res.second = criterion;
136 // criterion = res[1]
137 // # if criterionNew<0.1 and criterionNew<criterion:
138 // if criterionNew<criterion:
139 // criterion=criterionNew
140 // type=typeCriterion
141 // return [ type , criterion ]
146 inline double square_dist(vector3D const &v1, vector3D const &v2)
149 res = (v1.x - v2.x)*(v1.x - v2.x) +
150 (v1.y - v2.y)*(v1.y - v2.y) +
151 (v1.z - v2.z)*(v1.z - v2.z);
155 //------------------------- Purpose : -----------------------------------
156 //- This function determines the orientation similarity of two planes.
157 // Each plane is described by two vectors.
158 //------------------------- Parameters : --------------------------------
159 //- <refA> : - type : vector 3D (double)
160 //- <refB> : - type : vector 3D (double)
161 // - Description of the first plane
162 //- <ori1> : - type : vector 3D (double)
163 //- <ori2> : - type : vector 3D (double)
164 // - Description of the second plane
165 //------------------------- Return : ------------------------------------
166 // double : 0 if the planes are perpendicular. While the difference of
167 // the orientation between the planes are big more enlarge is
169 //------------------------- Other : -------------------------------------
170 // The calculus is based with vectors normalice
172 Orientation::CalculLikelyhood2Vec(vector3D const &refA, vector3D const &refB,
173 vector3D const &ori1, vector3D const &ori2 )
176 vector3D ori3 = ProductVectorial(ori1,ori2);
177 vector3D refC = ProductVectorial(refA,refB);
178 double res = square_dist(refC, ori3);
183 //------------------------- Purpose : -----------------------------------
184 //- Calculus of the poduct vectorial between two vectors 3D
185 //------------------------- Parameters : --------------------------------
186 //- <vec1> : - type : vector 3D (double)
187 //- <vec2> : - type : vector 3D (double)
188 //------------------------- Return : ------------------------------------
189 // (vec) : - Vector 3D
190 //------------------------- Other : -------------------------------------
192 Orientation::ProductVectorial(vector3D const &vec1, vector3D const &vec2)
195 vec3.x = vec1.y*vec2.z - vec1.z*vec2.y;
196 vec3.y = -( vec1.x*vec2.z - vec1.z*vec2.x);
197 vec3.z = vec1.x*vec2.y - vec1.y*vec2.x;
204 // ---------------------------------------------------------------------------
205 // Here is the original Python code, kindly supplied by THERALYS
207 // C++ code doesn't give good results
212 def TypeOrientation(self,file0):
214 # ------------------------- Purpose : -----------------------------------
215 # - This function compare the orientation of the given image and the
216 # basics orientations (Axial, Cornal, Sagital)
217 # ------------------------- Parameters : --------------------------------
218 # - <file0> : - type : string
219 # - The name of the first image file of the serie
220 # ------------------------- Return : ------------------------------------
224 # -2 : Coronal invert
226 # -3 : Sagital invert
228 # -4 : Heart Axial invert
230 # -5 : Heart Coronal invert
232 # -6 : Heart Sagital invert
234 # ------------------------- Other : -------------------------------------
235 # This method finds the most similar basic orientation.
238 toRead = gdcm.File(file0)
239 ValDict = GetValuesDict(toRead)
241 imageOrientation=ValDict["Image Orientation (Patient)"]
243 imageOrientation=ValDict["Image Orientation"]
245 ori1=[float(split(imageOrientation,"\\")[0]),\
246 float(split(imageOrientation,"\\")[1]),\
247 float(split(imageOrientation,"\\")[2])]
248 ori2=[float(split(imageOrientation,"\\")[3]),\
249 float(split(imageOrientation,"\\")[4]),\
250 float(split(imageOrientation,"\\")[5])]
252 ## two vectors perpendicular describe one plane
253 dicPlane=[ [ [1,0,0],[0,1,0] ], ## Axial
254 [ [1,0,0],[0,0,-1] ], ## Coronal
255 [ [0,1,0],[0,0,-1] ], ## Sagittal
256 [ [ 0.8 , 0.5 , 0.0 ],[-0.1 , 0.1 , -0.95] ],## Axial - HEART
257 [ [ 0.8 , 0.5 , 0.0 ],[-0.6674 , 0.687 , 0.1794] ],## Coronal - HEART
258 [ [-0.1 , 0.1 , -0.95],[-0.6674 , 0.687 , 0.1794] ] ] ## Sagittal - HEART
261 res=[0,99999] ## [ <result> , <memory of the last succes calcule> ]
262 for plane in dicPlane:
266 res=self.VerfCriterion( i , self.CalculLikelyhood2Vec(refA,refB,ori1,ori2) , res )
267 res=self.VerfCriterion( -i , self.CalculLikelyhood2Vec(refB,refA,ori1,ori2) , res )
274 def VerfCriterion(self,typeCriterion,criterionNew,res):
277 # if criterionNew<0.1 and criterionNew<criterion:
278 if criterionNew<criterion:
279 criterion=criterionNew
281 return [ type , criterion ]
284 def CalculLikelyhood2Vec(self,refA,refB,ori1,ori2):
286 # ------------------------- Purpose : -----------------------------------
287 # - This function determine the orientation similarity of two planes.
288 # Each plane is described by two vector.
289 # ------------------------- Parameters : --------------------------------
290 # - <refA> : - type : vector 3D (float)
291 # - <refB> : - type : vector 3D (float)
292 # - Description of the first plane
293 # - <ori1> : - type : vector 3D (float)
294 # - <ori2> : - type : vector 3D (float)
295 # - Description of the second plane
296 # ------------------------- Return : ------------------------------------
297 # float : 0 if the planes are perpendicular.
298 # While the difference of the orientation between the planes
299 # are big more enlarge is
301 # ------------------------- Other : -------------------------------------
302 # The calculus is based with vectors normalice
305 ori3=self.ProductVectorial(ori1,ori2)
306 refC=self.ProductVectorial(refA,refB)
307 res=math.pow(refC[0]-ori3[0],2) + math.pow(refC[1]-ori3[1],2) + math.pow(refC[2]-ori3[2],2)
308 return math.sqrt(res)
310 def ProductVectorial(self,vec1,vec2):
312 # ------------------------- Purpose : -----------------------------------
313 # - Calculus of the poduct vectorial between two vectors 3D
314 # ------------------------- Parameters : --------------------------------
315 # - <vec1> : - type : vector 3D (float)
316 # - <vec2> : - type : vector 3D (float)
317 # ------------------------- Return : ------------------------------------
318 # (vec) : - Vector 3D
319 # ------------------------- Other : -------------------------------------
322 vec3[0]=vec1[1]*vec2[2] - vec1[2]*vec2[1]
323 vec3[1]=-( vec1[0]*vec2[2] - vec1[2]*vec2[0])
324 vec3[2]=vec1[0]*vec2[1] - vec1[1]*vec2[0]
327 def GetValuesDict(image):
329 Returns a dictionnary containing values associated with Field Names
330 dict["Dicom Field Name"]="Dicom field value"
332 val=image.GetFirstEntry()
335 if isinstance(val,gdcm.ValEntryPtr):
336 dic[val.GetName()]=val.GetValue()
337 val=image.GetNextEntry()
343 // ------------------------------------------------------------------------
345 2.2.2 Orientation of DICOM images
348 http://www.dclunie.com/medical-image-faq/html/part2.html#DICOMOrientation
351 A question that is frequently asked in comp.protocols.dicom is how to determine
352 which side of an image is which (e.g. left, right) and so on.
353 The short answer is that for projection radiographs this is specified
354 explicitly using the "Patient Orientation" attribute, and for cross-sectional
355 images it needs to be derived from the "Image Orientation (Patient)" direction
356 cosines. In the standard these are explained as follows:
358 * "C.7.6.1.1.1 Patient Orientation.
359 The Patient Orientation (0020,0020) relative to the image
360 plane shall be specified by two values that designate the
361 anatomical direction of the positive row axis (left to right)
362 and the positive column axis (top to bottom).
363 The first entry is the direction of the rows, given by the
364 direction of the last pixel in the first row from the first
366 The second entry is the direction of the columns, given by
367 the direction of the last pixel in the first column from the
368 first pixel in that column.
369 Anatomical direction shall be designated by the capital
370 letters: A (anterior), P (posterior), R (right),L (left),
372 Each value of the orientation attribute shall contain at
373 least one of these characters.
374 If refinements in the orientation descriptions are to be
375 specified, then they shall be designated by one or two
376 additional letters in each value.
377 Within each value, the letters shall be ordered with the
378 principal orientation designated in the first character."
380 * "C.7.6.2.1.1 Image Position And Image Orientation.
381 The "Image Position (Patient)" (0020,0032) specifies the x, y,
382 and z coordinates of the upper left hand corner of the image;
383 it is the center of the first voxel transmitted.
384 The "Image Orientation (Patient)" (0020,0037) specifies the
385 direction cosines of the first row and the first column
386 with respect to the patient.
387 These Attributes shall be provided as a pair.
388 Row value for the x, y, and z axes respectively followed by
389 the Column value for the x, y, and z axes respectively.
390 The direction of the axes is defined fully by the patient's
392 The x-axis is increasing to the left hand side of the patient.
393 The y-axis is increasing to the posterior side of the patient.
394 The z-axis is increasing toward the head of the patient.
395 The patient based coordinate system is a right handed system,
396 i.e. the vector cross product of a unit vector along the
397 positive x-axis and a unit vector along the positive y-axis
398 is equal to a unit vector along the positive z-axis."
400 Some simple code to take one of the direction cosines (vectors) from the
401 Image Orientation (Patient) attribute and generate strings equivalent to one
402 of the values of Patient Orientation looks like this (noting that if the vector
403 is not aligned exactly with one of the major axes, the resulting string will
404 have multiple letters in as described under "refinements" in C.7.6.1.1.1):
409 * \brief computes the Patient Orientation relative to the image plane
410 * from the 'Image Orientation (Patient)'
411 * The first entry is the direction of the rows, given by the
412 * direction of the last pixel in the first row from the first
414 * The second entry is the direction of the columns, given by
415 * the direction of the last pixel in the first column from the
416 * first pixel in that column.
417 * Anatomical direction is designated by the capital
418 * letters: A (anterior), P (posterior), R (right),L (left),
419 * H (head), F (foot).
420 * Refinements in the orientation descriptions are designated
421 * by one or two additional letters in each value.
422 * Use it when "Patient Orientation" (0020,0020) is not found
423 * @return orientation string as "rawOrientation\columnsOrientation"
425 std::string Orientation::GetOrientation ( File *f )
428 if ( !f->GetImageOrientationPatient( iop ) )
431 std::string orientation;
432 orientation = GetSingleOrientation ( iop )
434 + GetSingleOrientation ( iop + 3 );
439 std::string Orientation::GetSingleOrientation ( float *iop)
441 std::string orientation;
443 char orientationX = iop[0] < 0 ? 'R' : 'L';
444 char orientationY = iop[1] < 0 ? 'A' : 'P';
445 char orientationZ = iop[2] < 0 ? 'F' : 'H';
447 double absX = iop[0];
448 if (absX < 0) absX = -absX;
449 double absY = iop[1];
450 if (absY < 0) absY = -absY;
451 double absZ = iop[2];
452 if (absZ < 0) absZ = -absZ;
454 for (int i=0; i<3; ++i)
456 if (absX>.0001 && absX>absY && absX>absZ)
458 orientation = orientation + orientationX;
461 else if (absY>.0001 && absY>absX && absY>absZ)
463 orientation = orientation + orientationY;
466 else if (absZ>.0001 && absZ>absX && absZ>absY)
468 orientation = orientation + orientationZ;
479 } // end namespace gdcm