1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkMedicalImageProperties.cxx,v
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkMedicalImageProperties.h"
16 #include "vtkObjectFactory.h"
17 
18 #include <string>
19 #include <map>
20 #include <vector>
21 #include <set>
22 
23 #include <ctime> // for strftime
24 #include <cctype> // for isdigit
25 #include <cassert>
26 
27 //----------------------------------------------------------------------------
28 vtkStandardNewMacro(vtkMedicalImageProperties);
29 
30 static const char *vtkMedicalImagePropertiesOrientationString[] = {
31   "AXIAL",
32   "CORONAL",
33   "SAGITTAL",
34   nullptr
35 };
36 
37 
38 //----------------------------------------------------------------------------
39 class vtkMedicalImagePropertiesInternals
40 {
41 public:
42   class WindowLevelPreset
43   {
44   public:
45     double Window;
46     double Level;
47     std::string Comment;
48   };
49 
50   class UserDefinedValue
51   {
52   public:
UserDefinedValue(const char * name=nullptr,const char * value=nullptr)53     UserDefinedValue(const char *name = nullptr, const char *value = nullptr):Name(name ? name : ""),Value(value ? value : "") {}
54     std::string Name;
55     std::string Value;
56     // order for the std::set
operator <(const UserDefinedValue & udv) const57     bool operator<(const UserDefinedValue &udv) const
58     {
59       return Name < udv.Name;
60     }
61   };
62   typedef std::set< UserDefinedValue > UserDefinedValues;
63   UserDefinedValues UserDefinedValuePool;
AddUserDefinedValue(const char * name,const char * value)64   void AddUserDefinedValue(const char *name, const char *value)
65   {
66     if( name && *name && value && *value )
67     {
68       UserDefinedValuePool.insert( UserDefinedValues::value_type(name, value) );
69     }
70     // else raise a warning ?
71   }
GetUserDefinedValue(const char * name) const72   const char *GetUserDefinedValue(const char *name) const
73   {
74     if( name && *name )
75     {
76       UserDefinedValue key(name);
77       UserDefinedValues::const_iterator it = UserDefinedValuePool.find( key );
78       if( it != UserDefinedValuePool.end() )
79       {
80         assert( strcmp(it->Name.c_str(), name) == 0 );
81         return it->Value.c_str();
82       }
83     }
84     return nullptr;
85   }
GetNumberOfUserDefinedValues() const86   unsigned int GetNumberOfUserDefinedValues() const
87   {
88     return static_cast<unsigned int>(UserDefinedValuePool.size());
89   }
GetUserDefinedNameByIndex(unsigned int idx)90   const char *GetUserDefinedNameByIndex(unsigned int idx)
91   {
92     if( idx < UserDefinedValuePool.size() )
93     {
94       UserDefinedValues::const_iterator it = UserDefinedValuePool.begin();
95       while( idx )
96       {
97         ++it;
98         idx--;
99       }
100       return it->Name.c_str();
101     }
102     return nullptr;
103   }
GetUserDefinedValueByIndex(unsigned int idx)104   const char *GetUserDefinedValueByIndex(unsigned int idx)
105   {
106     if( idx < UserDefinedValuePool.size() )
107     {
108       UserDefinedValues::const_iterator it = UserDefinedValuePool.begin();
109       while( idx )
110       {
111         ++it;
112         idx--;
113       }
114       return it->Value.c_str();
115     }
116     return nullptr;
117   }
RemoveAllUserDefinedValues()118   void RemoveAllUserDefinedValues()
119   {
120       UserDefinedValuePool.clear();
121   }
122 
123   typedef std::vector<WindowLevelPreset> WindowLevelPresetPoolType;
124   typedef std::vector<WindowLevelPreset>::iterator WindowLevelPresetPoolIterator;
125 
126   WindowLevelPresetPoolType WindowLevelPresetPool;
127 
128   // It is also useful to have a mapping from DICOM UID to slice id, for application like VolView
129   typedef std::map< unsigned int, std::string> SliceUIDType;
130   typedef std::vector< SliceUIDType > VolumeSliceUIDType;
131   VolumeSliceUIDType UID;
SetNumberOfVolumes(unsigned int n)132   void SetNumberOfVolumes(unsigned int n)
133   {
134     UID.resize(n);
135     Orientation.resize(n);
136   }
SetUID(unsigned int vol,unsigned int slice,const char * uid)137   void SetUID(unsigned int vol, unsigned int slice, const char *uid)
138   {
139     SetNumberOfVolumes( vol + 1 );
140     UID[vol][slice] = uid;
141   }
GetUID(unsigned int vol,unsigned int slice)142   const char *GetUID(unsigned int vol, unsigned int slice)
143   {
144     assert( vol < UID.size() );
145     assert( UID[vol].find(slice) != UID[vol].end() );
146     //if( UID[vol].find(slice) == UID[vol].end() )
147     //  {
148     //  this->Print( cerr, vtkIndent() );
149     //  }
150     return UID[vol].find(slice)->second.c_str();
151   }
152   // Extensive lookup
FindSlice(int & vol,const char * uid)153   int FindSlice(int &vol, const char *uid)
154   {
155     vol = -1;
156     for(unsigned int v = 0; v < UID.size(); ++v )
157     {
158       SliceUIDType::const_iterator cit = UID[v].begin();
159       while (cit != UID[v].end())
160       {
161         if (cit->second == uid)
162         {
163           vol = v;
164           return static_cast<int>(cit->first);
165         }
166         ++cit;
167       }
168     }
169     return -1; // volume not found.
170   }
GetSlice(unsigned int vol,const char * uid)171   int GetSlice(unsigned int vol, const char *uid)
172   {
173     assert( vol < UID.size() );
174     SliceUIDType::const_iterator cit = UID[vol].begin();
175     while (cit != UID[vol].end())
176     {
177       if (cit->second == uid)
178       {
179         return static_cast<int>(cit->first);
180       }
181       ++cit;
182     }
183     return -1; // uid not found.
184   }
Print(ostream & os,vtkIndent indent)185   void Print(ostream &os, vtkIndent indent)
186   {
187     os << indent << "WindowLevel: \n";
188     for( WindowLevelPresetPoolIterator it = WindowLevelPresetPool.begin(); it != WindowLevelPresetPool.end(); ++it )
189     {
190       const WindowLevelPreset &wlp = *it;
191       os << indent.GetNextIndent() << "Window: " << wlp.Window << "\n";
192       os << indent.GetNextIndent() << "Level: " << wlp.Level << "\n";
193       os << indent.GetNextIndent() << "Comment: " << wlp.Comment << "\n";
194     }
195     os << indent << "UID(s):\n";
196     for( VolumeSliceUIDType::const_iterator it = UID.begin();
197       it != UID.end();
198       ++it)
199     {
200       for( SliceUIDType::const_iterator it2 = it->begin();
201         it2 != it->end();
202         ++it2)
203       {
204         os << indent.GetNextIndent()
205            << it2->first <<  "  " << it2->second << "\n";
206       }
207     }
208     os << indent << "Orientation(s):\n";
209     for( std::vector<unsigned int>::const_iterator it = Orientation.begin();
210       it != Orientation.end(); ++it)
211     {
212       os << indent.GetNextIndent()
213          << vtkMedicalImageProperties::GetStringFromOrientationType(*it) << "\n";
214     }
215     os << indent << "User Defined Values: (" << UserDefinedValuePool.size() << ")\n";
216     UserDefinedValues::const_iterator it2 = UserDefinedValuePool.begin();
217     for(; it2 != UserDefinedValuePool.end(); ++it2)
218     {
219       os << indent.GetNextIndent()
220          << it2->Name << " -> " << it2->Value << "\n";
221     }
222   }
223   std::vector<unsigned int> Orientation;
SetOrientation(unsigned int vol,unsigned int ori)224   void SetOrientation(unsigned int vol, unsigned int ori)
225   {
226     // see SetNumberOfVolumes for allocation
227     assert( ori <= vtkMedicalImageProperties::SAGITTAL );
228     Orientation[vol] = ori;
229   }
GetOrientation(unsigned int vol)230   unsigned int GetOrientation(unsigned int vol)
231   {
232     assert( vol < Orientation.size() );
233     const unsigned int &val = Orientation[vol];
234     assert( val <= vtkMedicalImageProperties::SAGITTAL );
235     return val;
236   }
DeepCopy(vtkMedicalImagePropertiesInternals * p)237   void DeepCopy(vtkMedicalImagePropertiesInternals *p)
238   {
239     WindowLevelPresetPool = p->WindowLevelPresetPool;
240     UserDefinedValuePool = p->UserDefinedValuePool;
241     UID = p->UID;
242     Orientation = p->Orientation;
243   }
244 };
245 
246 //----------------------------------------------------------------------------
vtkMedicalImageProperties()247 vtkMedicalImageProperties::vtkMedicalImageProperties()
248 {
249   this->Internals = new vtkMedicalImagePropertiesInternals;
250 
251   this->StudyDate              = nullptr;
252   this->AcquisitionDate        = nullptr;
253   this->StudyTime              = nullptr;
254   this->AcquisitionTime        = nullptr;
255   this->ConvolutionKernel      = nullptr;
256   this->EchoTime               = nullptr;
257   this->EchoTrainLength        = nullptr;
258   this->Exposure               = nullptr;
259   this->ExposureTime           = nullptr;
260   this->GantryTilt             = nullptr;
261   this->ImageDate              = nullptr;
262   this->ImageNumber            = nullptr;
263   this->ImageTime              = nullptr;
264   this->InstitutionName        = nullptr;
265   this->KVP                    = nullptr;
266   this->ManufacturerModelName  = nullptr;
267   this->Manufacturer           = nullptr;
268   this->Modality               = nullptr;
269   this->PatientAge             = nullptr;
270   this->PatientBirthDate       = nullptr;
271   this->PatientID              = nullptr;
272   this->PatientName            = nullptr;
273   this->PatientSex             = nullptr;
274   this->RepetitionTime         = nullptr;
275   this->SeriesDescription      = nullptr;
276   this->SeriesNumber           = nullptr;
277   this->SliceThickness         = nullptr;
278   this->StationName            = nullptr;
279   this->StudyDescription       = nullptr;
280   this->StudyID                = nullptr;
281   this->XRayTubeCurrent        = nullptr;
282 
283   this->DirectionCosine[0] = 1;
284   this->DirectionCosine[1] = 0;
285   this->DirectionCosine[2] = 0;
286   this->DirectionCosine[3] = 0;
287   this->DirectionCosine[4] = 1;
288   this->DirectionCosine[5] = 0;
289 }
290 
291 //----------------------------------------------------------------------------
~vtkMedicalImageProperties()292 vtkMedicalImageProperties::~vtkMedicalImageProperties()
293 {
294   this->Clear();
295 
296   delete this->Internals;
297   this->Internals = nullptr;
298 }
299 
300 //----------------------------------------------------------------------------
AddUserDefinedValue(const char * name,const char * value)301 void vtkMedicalImageProperties::AddUserDefinedValue(const char *name, const char *value)
302 {
303   this->Internals->AddUserDefinedValue(name, value);
304 }
305 
306 //----------------------------------------------------------------------------
GetUserDefinedValue(const char * name)307 const char *vtkMedicalImageProperties::GetUserDefinedValue(const char *name)
308 {
309   return this->Internals->GetUserDefinedValue(name);
310 }
311 
312 //----------------------------------------------------------------------------
GetNumberOfUserDefinedValues()313 unsigned int vtkMedicalImageProperties::GetNumberOfUserDefinedValues()
314 {
315   return this->Internals->GetNumberOfUserDefinedValues();
316 }
317 
318 //----------------------------------------------------------------------------
GetUserDefinedValueByIndex(unsigned int idx)319 const char *vtkMedicalImageProperties::GetUserDefinedValueByIndex(unsigned int idx)
320 {
321   return this->Internals->GetUserDefinedValueByIndex(idx);
322 }
323 
324 //----------------------------------------------------------------------------
GetUserDefinedNameByIndex(unsigned int idx)325 const char *vtkMedicalImageProperties::GetUserDefinedNameByIndex(unsigned int idx)
326 {
327   return this->Internals->GetUserDefinedNameByIndex(idx);
328 }
329 
330 //----------------------------------------------------------------------------
RemoveAllUserDefinedValues()331 void vtkMedicalImageProperties::RemoveAllUserDefinedValues()
332 {
333   this->Internals->RemoveAllUserDefinedValues();
334 }
335 
336 //----------------------------------------------------------------------------
Clear()337 void vtkMedicalImageProperties::Clear()
338 {
339   this->SetStudyDate(nullptr);
340   this->SetAcquisitionDate(nullptr);
341   this->SetStudyTime(nullptr);
342   this->SetAcquisitionTime(nullptr);
343   this->SetConvolutionKernel(nullptr);
344   this->SetEchoTime(nullptr);
345   this->SetEchoTrainLength(nullptr);
346   this->SetExposure(nullptr);
347   this->SetExposureTime(nullptr);
348   this->SetGantryTilt(nullptr);
349   this->SetImageDate(nullptr);
350   this->SetImageNumber(nullptr);
351   this->SetImageTime(nullptr);
352   this->SetInstitutionName(nullptr);
353   this->SetKVP(nullptr);
354   this->SetManufacturerModelName(nullptr);
355   this->SetManufacturer(nullptr);
356   this->SetModality(nullptr);
357   this->SetPatientAge(nullptr);
358   this->SetPatientBirthDate(nullptr);
359   this->SetPatientID(nullptr);
360   this->SetPatientName(nullptr);
361   this->SetPatientSex(nullptr);
362   this->SetRepetitionTime(nullptr);
363   this->SetSeriesDescription(nullptr);
364   this->SetSeriesNumber(nullptr);
365   this->SetSliceThickness(nullptr);
366   this->SetStationName(nullptr);
367   this->SetStudyDescription(nullptr);
368   this->SetStudyID(nullptr);
369   this->SetXRayTubeCurrent(nullptr);
370 
371   this->RemoveAllWindowLevelPresets();
372   this->RemoveAllUserDefinedValues();
373 
374   this->Internals->Orientation.clear();
375   this->Internals->UID.clear();
376 }
377 
378 //----------------------------------------------------------------------------
DeepCopy(vtkMedicalImageProperties * p)379 void vtkMedicalImageProperties::DeepCopy(vtkMedicalImageProperties *p)
380 {
381   if (p == nullptr)
382   {
383     return;
384   }
385 
386   this->Clear();
387 
388   this->SetStudyDate(p->GetStudyDate());
389   this->SetAcquisitionDate(p->GetAcquisitionDate());
390   this->SetStudyTime(p->GetStudyTime());
391   this->SetAcquisitionTime(p->GetAcquisitionTime());
392   this->SetConvolutionKernel(p->GetConvolutionKernel());
393   this->SetEchoTime(p->GetEchoTime());
394   this->SetEchoTrainLength(p->GetEchoTrainLength());
395   this->SetExposure(p->GetExposure());
396   this->SetExposureTime(p->GetExposureTime());
397   this->SetGantryTilt(p->GetGantryTilt());
398   this->SetImageDate(p->GetImageDate());
399   this->SetImageNumber(p->GetImageNumber());
400   this->SetImageTime(p->GetImageTime());
401   this->SetInstitutionName(p->GetInstitutionName());
402   this->SetKVP(p->GetKVP());
403   this->SetManufacturerModelName(p->GetManufacturerModelName());
404   this->SetManufacturer(p->GetManufacturer());
405   this->SetModality(p->GetModality());
406   this->SetPatientAge(p->GetPatientAge());
407   this->SetPatientBirthDate(p->GetPatientBirthDate());
408   this->SetPatientID(p->GetPatientID());
409   this->SetPatientName(p->GetPatientName());
410   this->SetPatientSex(p->GetPatientSex());
411   this->SetRepetitionTime(p->GetRepetitionTime());
412   this->SetSeriesDescription(p->GetSeriesDescription());
413   this->SetSeriesNumber(p->GetSeriesNumber());
414   this->SetSliceThickness(p->GetSliceThickness());
415   this->SetStationName(p->GetStationName());
416   this->SetStudyDescription(p->GetStudyDescription());
417   this->SetStudyID(p->GetStudyID());
418   this->SetXRayTubeCurrent(p->GetXRayTubeCurrent());
419   this->SetDirectionCosine(p->GetDirectionCosine());
420 
421   this->Internals->DeepCopy(p->Internals);
422 }
423 
424 //----------------------------------------------------------------------------
AddWindowLevelPreset(double w,double l)425 int vtkMedicalImageProperties::AddWindowLevelPreset(
426   double w, double l)
427 {
428   if (!this->Internals || this->HasWindowLevelPreset(w, l))
429   {
430     return -1;
431   }
432 
433   vtkMedicalImagePropertiesInternals::WindowLevelPreset preset;
434   preset.Window = w;
435   preset.Level = l;
436   this->Internals->WindowLevelPresetPool.push_back(preset);
437   return static_cast<int>(this->Internals->WindowLevelPresetPool.size() - 1);
438 }
439 
440 //----------------------------------------------------------------------------
GetWindowLevelPresetIndex(double w,double l)441 int vtkMedicalImageProperties::GetWindowLevelPresetIndex(double w, double l)
442 {
443   if (this->Internals)
444   {
445     vtkMedicalImagePropertiesInternals::WindowLevelPresetPoolIterator it =
446       this->Internals->WindowLevelPresetPool.begin();
447     vtkMedicalImagePropertiesInternals::WindowLevelPresetPoolIterator end =
448       this->Internals->WindowLevelPresetPool.end();
449     int index = 0;
450     for (; it != end; ++it, ++index)
451     {
452       if ((*it).Window == w && (*it).Level == l)
453       {
454         return index;
455       }
456     }
457   }
458   return -1;
459 }
460 
461 //----------------------------------------------------------------------------
HasWindowLevelPreset(double w,double l)462 int vtkMedicalImageProperties::HasWindowLevelPreset(double w, double l)
463 {
464   return this->GetWindowLevelPresetIndex(w, l) >= 0 ? 1 : 0;
465 }
466 
467 //----------------------------------------------------------------------------
RemoveWindowLevelPreset(double w,double l)468 void vtkMedicalImageProperties::RemoveWindowLevelPreset(double w, double l)
469 {
470   if (this->Internals)
471   {
472     vtkMedicalImagePropertiesInternals::WindowLevelPresetPoolIterator it =
473       this->Internals->WindowLevelPresetPool.begin();
474     vtkMedicalImagePropertiesInternals::WindowLevelPresetPoolIterator end =
475       this->Internals->WindowLevelPresetPool.end();
476     for (; it != end; ++it)
477     {
478       if ((*it).Window == w && (*it).Level == l)
479       {
480         this->Internals->WindowLevelPresetPool.erase(it);
481         break;
482       }
483     }
484   }
485 }
486 
487 //----------------------------------------------------------------------------
RemoveAllWindowLevelPresets()488 void vtkMedicalImageProperties::RemoveAllWindowLevelPresets()
489 {
490   if (this->Internals)
491   {
492     this->Internals->WindowLevelPresetPool.clear();
493   }
494 }
495 
496 //----------------------------------------------------------------------------
GetNumberOfWindowLevelPresets()497 int vtkMedicalImageProperties::GetNumberOfWindowLevelPresets()
498 {
499   return this->Internals ? static_cast<int>(
500     this->Internals->WindowLevelPresetPool.size()) : 0;
501 }
502 
503 //----------------------------------------------------------------------------
GetNthWindowLevelPreset(int idx,double * w,double * l)504 int vtkMedicalImageProperties::GetNthWindowLevelPreset(
505   int idx, double *w, double *l)
506 {
507   if (this->Internals &&
508       idx >= 0 && idx < this->GetNumberOfWindowLevelPresets())
509   {
510     *w = this->Internals->WindowLevelPresetPool[idx].Window;
511     *l = this->Internals->WindowLevelPresetPool[idx].Level;
512     return 1;
513   }
514   return 0;
515 }
516 
517 //----------------------------------------------------------------------------
GetNthWindowLevelPreset(int idx)518 double* vtkMedicalImageProperties::GetNthWindowLevelPreset(int idx)
519 
520 {
521   static double wl[2];
522   if (this->GetNthWindowLevelPreset(idx, wl, wl + 1))
523   {
524     return wl;
525   }
526   return nullptr;
527 }
528 
529 //----------------------------------------------------------------------------
GetNthWindowLevelPresetComment(int idx)530 const char* vtkMedicalImageProperties::GetNthWindowLevelPresetComment(
531   int idx)
532 {
533   if (this->Internals &&
534       idx >= 0 && idx < this->GetNumberOfWindowLevelPresets())
535   {
536     return this->Internals->WindowLevelPresetPool[idx].Comment.c_str();
537   }
538   return nullptr;
539 }
540 
541 //----------------------------------------------------------------------------
SetNthWindowLevelPresetComment(int idx,const char * comment)542 void vtkMedicalImageProperties::SetNthWindowLevelPresetComment(
543   int idx, const char *comment)
544 {
545   if (this->Internals &&
546       idx >= 0 && idx < this->GetNumberOfWindowLevelPresets())
547   {
548     this->Internals->WindowLevelPresetPool[idx].Comment =
549       (comment ? comment : "");
550   }
551 }
552 
553 //----------------------------------------------------------------------------
GetInstanceUIDFromSliceID(int volumeidx,int sliceid)554 const char *vtkMedicalImageProperties::GetInstanceUIDFromSliceID(
555                                        int volumeidx, int sliceid)
556 {
557   return this->Internals->GetUID(volumeidx, sliceid);
558 }
559 
560 //----------------------------------------------------------------------------
GetSliceIDFromInstanceUID(int & volumeidx,const char * uid)561 int vtkMedicalImageProperties::GetSliceIDFromInstanceUID(
562                                    int &volumeidx, const char *uid)
563 {
564   if( volumeidx == -1 )
565   {
566     return this->Internals->FindSlice(volumeidx, uid);
567   }
568   else
569   {
570     return this->Internals->GetSlice(volumeidx, uid);
571   }
572 }
573 
574 //----------------------------------------------------------------------------
SetInstanceUIDFromSliceID(int volumeidx,int sliceid,const char * uid)575 void vtkMedicalImageProperties::SetInstanceUIDFromSliceID(
576                       int volumeidx, int sliceid, const char *uid)
577 {
578   this->Internals->SetUID(volumeidx,sliceid, uid);
579 }
580 
581 //----------------------------------------------------------------------------
SetOrientationType(int volumeidx,int orientation)582 void vtkMedicalImageProperties::SetOrientationType(int volumeidx, int orientation)
583 {
584   this->Internals->SetOrientation(volumeidx, orientation);
585 }
586 
587 //----------------------------------------------------------------------------
GetOrientationType(int volumeidx)588 int vtkMedicalImageProperties::GetOrientationType(int volumeidx)
589 {
590   return this->Internals->GetOrientation(volumeidx);
591 }
592 
593 //----------------------------------------------------------------------------
GetStringFromOrientationType(unsigned int type)594 const char *vtkMedicalImageProperties::GetStringFromOrientationType(unsigned int type)
595 {
596   static unsigned int numtypes = 0;
597   // find length of table
598   if (!numtypes)
599   {
600     while (vtkMedicalImagePropertiesOrientationString[numtypes] != nullptr)
601     {
602       numtypes++;
603     }
604   }
605 
606   if (type < numtypes)
607   {
608     return vtkMedicalImagePropertiesOrientationString[type];
609   }
610 
611   return nullptr;
612 }
613 
614 //----------------------------------------------------------------------------
GetSliceThicknessAsDouble()615 double vtkMedicalImageProperties::GetSliceThicknessAsDouble()
616 {
617   if (this->SliceThickness)
618   {
619     return atof(this->SliceThickness);
620   }
621   return 0;
622 }
623 
624 //----------------------------------------------------------------------------
GetGantryTiltAsDouble()625 double vtkMedicalImageProperties::GetGantryTiltAsDouble()
626 {
627   if (this->GantryTilt)
628   {
629     return atof(this->GantryTilt);
630   }
631   return 0;
632 }
633 //----------------------------------------------------------------------------
GetAgeAsFields(const char * age,int & year,int & month,int & week,int & day)634 int vtkMedicalImageProperties::GetAgeAsFields(const char *age, int &year,
635   int &month, int &week, int &day)
636 {
637   year = month = week = day = -1;
638   if( !age )
639   {
640     return 0;
641   }
642 
643   size_t len = strlen(age);
644   if( len == 4 )
645   {
646     // DICOM V3
647     unsigned int val;
648     char type;
649     if( !isdigit(age[0])
650      || !isdigit(age[1])
651      || !isdigit(age[2]))
652     {
653       return 0;
654     }
655     if( sscanf(age, "%3u%c", &val, &type) != 2 )
656     {
657       return 0;
658     }
659     switch(type)
660     {
661     case 'Y':
662       year = static_cast<int>(val);
663       break;
664     case 'M':
665       month = static_cast<int>(val);
666       break;
667     case 'W':
668       week = static_cast<int>(val);
669       break;
670     case 'D':
671       day = static_cast<int>(val);
672       break;
673     default:
674       return 0;
675     }
676   }
677   else
678   {
679     return 0;
680   }
681 
682   return 1;
683 }
684 
685 //----------------------------------------------------------------------------
GetPatientAgeYear()686 int vtkMedicalImageProperties::GetPatientAgeYear()
687 {
688   const char *age = this->GetPatientAge();
689   int year, month, week, day;
690   vtkMedicalImageProperties::GetAgeAsFields(age, year, month, week, day);
691   return year;
692 }
693 //----------------------------------------------------------------------------
GetPatientAgeMonth()694 int vtkMedicalImageProperties::GetPatientAgeMonth()
695 {
696   const char *age = this->GetPatientAge();
697   int year, month, week, day;
698   vtkMedicalImageProperties::GetAgeAsFields(age, year, month, week, day);
699   return month;
700 }
701 //----------------------------------------------------------------------------
GetPatientAgeWeek()702 int vtkMedicalImageProperties::GetPatientAgeWeek()
703 {
704   const char *age = this->GetPatientAge();
705   int year, month, week, day;
706   vtkMedicalImageProperties::GetAgeAsFields(age, year, month, week, day);
707   return week;
708 }
709 //----------------------------------------------------------------------------
GetPatientAgeDay()710 int vtkMedicalImageProperties::GetPatientAgeDay()
711 {
712   const char *age = this->GetPatientAge();
713   int year, month, week, day;
714   vtkMedicalImageProperties::GetAgeAsFields(age, year, month, week, day);
715   return day;
716 }
717 
718 //----------------------------------------------------------------------------
GetTimeAsFields(const char * time,int & hour,int & minute,int & second)719 int vtkMedicalImageProperties::GetTimeAsFields(const char *time, int &hour,
720   int &minute, int &second /* , long &milliseconds */)
721 {
722   if( !time )
723   {
724     return 0;
725   }
726 
727   size_t len = strlen(time);
728   if( len == 6 )
729   {
730     // DICOM V3
731     if( sscanf(time, "%02d%02d%02d", &hour, &minute, &second) != 3 )
732     {
733       return 0;
734     }
735   }
736   else if( len == 8 )
737   {
738     // Some *very* old ACR-NEMA
739     if( sscanf(time, "%02d.%02d.%02d", &hour, &minute, &second) != 3 )
740     {
741       return 0;
742     }
743   }
744   else
745   {
746     return 0;
747   }
748 
749   return 1;
750 }
751 //----------------------------------------------------------------------------
GetDateAsFields(const char * date,int & year,int & month,int & day)752 int vtkMedicalImageProperties::GetDateAsFields(const char *date, int &year,
753   int &month, int &day)
754 {
755   if( !date )
756   {
757     return 0;
758   }
759 
760   size_t len = strlen(date);
761   if( len == 8 )
762   {
763     // DICOM V3
764     if( sscanf(date, "%04d%02d%02d", &year, &month, &day) != 3 )
765     {
766       return 0;
767     }
768   }
769   else if( len == 10 )
770   {
771     // Some *very* old ACR-NEMA
772     if( sscanf(date, "%04d.%02d.%02d", &year, &month, &day) != 3 )
773     {
774       return 0;
775     }
776   }
777   else
778   {
779     return 0;
780   }
781 
782   return 1;
783 }
784 
785 //----------------------------------------------------------------------------
786 // Some  buggy versions of gcc complain about the use of %c: warning: `%c'
787 // yields only last 2 digits of year in some locales.  Of course  program-
788 // mers  are  encouraged  to  use %c, it gives the preferred date and time
789 // representation. One meets all kinds of strange obfuscations to  circum-
790 // vent this gcc problem. A relatively clean one is to add an intermediate
791 // function. This is described as bug #3190 in gcc bugzilla:
792 // [-Wformat-y2k doesn't belong to -Wall - it's hard to avoid]
793 inline size_t
my_strftime(char * s,size_t max,const char * fmt,const struct tm * tm)794 my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
795 {
796   return strftime(s, max, fmt, tm);
797 }
798 // Helper function to convert a DICOM iso date format into a locale one
799 // locale buffer should be typically char locale[200]
GetDateAsLocale(const char * iso,char * locale)800 int vtkMedicalImageProperties::GetDateAsLocale(const char *iso, char *locale)
801 {
802   int year, month, day;
803   if( vtkMedicalImageProperties::GetDateAsFields(iso, year, month, day) )
804   {
805     if (year < 1900 || month < 1 || month > 12 || day < 1 || day > 31)
806     {
807       *locale = '\0';
808     }
809     else
810     {
811       struct tm date;
812       memset(&date,0, sizeof(date));
813       date.tm_mday = day;
814       // month are expressed in the [0-11] range:
815       date.tm_mon = month - 1;
816       // structure is date starting at 1900
817       date.tm_year = year - 1900;
818       my_strftime(locale, 200, "%x", &date);
819     }
820     return 1;
821   }
822   return 0;
823 }
824 //----------------------------------------------------------------------------
GetPatientBirthDateYear()825 int vtkMedicalImageProperties::GetPatientBirthDateYear()
826 {
827   const char *date = this->GetPatientBirthDate();
828   int year = 0, month = 0, day = 0;
829   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
830   return year;
831 }
832 //----------------------------------------------------------------------------
GetPatientBirthDateMonth()833 int vtkMedicalImageProperties::GetPatientBirthDateMonth()
834 {
835   const char *date = this->GetPatientBirthDate();
836   int year = 0, month = 0, day = 0;
837   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
838   return month;
839 }
840 //----------------------------------------------------------------------------
GetPatientBirthDateDay()841 int vtkMedicalImageProperties::GetPatientBirthDateDay()
842 {
843   const char *date = this->GetPatientBirthDate();
844   int year = 0, month = 0, day = 0;
845   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
846   return day;
847 }
848 //----------------------------------------------------------------------------
GetAcquisitionDateYear()849 int vtkMedicalImageProperties::GetAcquisitionDateYear()
850 {
851   const char *date = this->GetAcquisitionDate();
852   int year = 0, month = 0, day = 0;
853   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
854   return year;
855 }
856 //----------------------------------------------------------------------------
GetAcquisitionDateMonth()857 int vtkMedicalImageProperties::GetAcquisitionDateMonth()
858 {
859   const char *date = this->GetAcquisitionDate();
860   int year = 0, month = 0, day = 0;
861   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
862   return month;
863 }
864 //----------------------------------------------------------------------------
GetAcquisitionDateDay()865 int vtkMedicalImageProperties::GetAcquisitionDateDay()
866 {
867   const char *date = this->GetAcquisitionDate();
868   int year = 0, month = 0, day = 0;
869   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
870   return day;
871 }
872 //----------------------------------------------------------------------------
GetImageDateYear()873 int vtkMedicalImageProperties::GetImageDateYear()
874 {
875   const char *date = this->GetImageDate();
876   int year = 0, month = 0, day = 0;
877   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
878   return year;
879 }
880 //----------------------------------------------------------------------------
GetImageDateMonth()881 int vtkMedicalImageProperties::GetImageDateMonth()
882 {
883   const char *date = this->GetImageDate();
884   int year = 0, month = 0, day = 0;
885   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
886   return month;
887 }
888 //----------------------------------------------------------------------------
GetImageDateDay()889 int vtkMedicalImageProperties::GetImageDateDay()
890 {
891   const char *date = this->GetImageDate();
892   int year = 0, month = 0, day = 0;
893   vtkMedicalImageProperties::GetDateAsFields(date, year, month, day);
894   return day;
895 }
896 
897 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)898 void vtkMedicalImageProperties::PrintSelf(ostream& os, vtkIndent indent)
899 {
900   this->Superclass::PrintSelf(os, indent);
901 
902   os << indent << "PatientName: ";
903   if (this->PatientName)
904   {
905     os << this->PatientName;
906   }
907 
908   os << "\n" << indent << "PatientID: ";
909   if (this->PatientID)
910   {
911     os << this->PatientID;
912   }
913 
914   os << "\n" << indent << "PatientAge: ";
915   if (this->PatientAge)
916   {
917     os << this->PatientAge;
918   }
919 
920   os << "\n" << indent << "PatientSex: ";
921   if (this->PatientSex)
922   {
923     os << this->PatientSex;
924   }
925 
926   os << "\n" << indent << "PatientBirthDate: ";
927   if (this->PatientBirthDate)
928   {
929     os << this->PatientBirthDate;
930   }
931 
932   os << "\n" << indent << "ImageDate: ";
933   if (this->ImageDate)
934   {
935     os << this->ImageDate;
936   }
937 
938   os << "\n" << indent << "ImageTime: ";
939   if (this->ImageTime)
940   {
941     os << this->ImageTime;
942   }
943 
944   os << "\n" << indent << "ImageNumber: ";
945   if (this->ImageNumber)
946   {
947     os << this->ImageNumber;
948   }
949 
950   os << "\n" << indent << "StudyDate: ";
951   if (this->StudyDate)
952   {
953     os << this->StudyDate;
954   }
955 
956   os << "\n" << indent << "AcquisitionDate: ";
957   if (this->AcquisitionDate)
958   {
959     os << this->AcquisitionDate;
960   }
961 
962   os << "\n" << indent << "StudyTime: ";
963   if (this->StudyTime)
964   {
965     os << this->StudyTime;
966   }
967 
968   os << "\n" << indent << "AcquisitionTime: ";
969   if (this->AcquisitionTime)
970   {
971     os << this->AcquisitionTime;
972   }
973 
974   os << "\n" << indent << "SeriesNumber: ";
975   if (this->SeriesNumber)
976   {
977     os << this->SeriesNumber;
978   }
979 
980   os << "\n" << indent << "SeriesDescription: ";
981   if (this->SeriesDescription)
982   {
983     os << this->SeriesDescription;
984   }
985 
986   os << "\n" << indent << "StudyDescription: ";
987   if (this->StudyDescription)
988   {
989     os << this->StudyDescription;
990   }
991 
992   os << "\n" << indent << "StudyID: ";
993   if (this->StudyID)
994   {
995     os << this->StudyID;
996   }
997 
998   os << "\n" << indent << "Modality: ";
999   if (this->Modality)
1000   {
1001     os << this->Modality;
1002   }
1003 
1004   os << "\n" << indent << "ManufacturerModelName: ";
1005   if (this->ManufacturerModelName)
1006   {
1007     os << this->ManufacturerModelName;
1008   }
1009 
1010   os << "\n" << indent << "Manufacturer: ";
1011   if (this->Manufacturer)
1012   {
1013     os << this->Manufacturer;
1014   }
1015 
1016   os << "\n" << indent << "StationName: ";
1017   if (this->StationName)
1018   {
1019     os << this->StationName;
1020   }
1021 
1022   os << "\n" << indent << "InstitutionName: ";
1023   if (this->InstitutionName)
1024   {
1025     os << this->InstitutionName;
1026   }
1027 
1028   os << "\n" << indent << "ConvolutionKernel: ";
1029   if (this->ConvolutionKernel)
1030   {
1031     os << this->ConvolutionKernel;
1032   }
1033 
1034   os << "\n" << indent << "SliceThickness: ";
1035   if (this->SliceThickness)
1036   {
1037     os << this->SliceThickness;
1038   }
1039 
1040   os << "\n" << indent << "KVP: ";
1041   if (this->KVP)
1042   {
1043     os << this->KVP;
1044   }
1045 
1046   os << "\n" << indent << "GantryTilt: ";
1047   if (this->GantryTilt)
1048   {
1049     os << this->GantryTilt;
1050   }
1051 
1052   os << "\n" << indent << "EchoTime: ";
1053   if (this->EchoTime)
1054   {
1055     os << this->EchoTime;
1056   }
1057 
1058   os << "\n" << indent << "EchoTrainLength: ";
1059   if (this->EchoTrainLength)
1060   {
1061     os << this->EchoTrainLength;
1062   }
1063 
1064   os << "\n" << indent << "RepetitionTime: ";
1065   if (this->RepetitionTime)
1066   {
1067     os << this->RepetitionTime;
1068   }
1069 
1070   os << "\n" << indent << "ExposureTime: ";
1071   if (this->ExposureTime)
1072   {
1073     os << this->ExposureTime;
1074   }
1075 
1076   os << "\n" << indent << "XRayTubeCurrent: ";
1077   if (this->XRayTubeCurrent)
1078   {
1079     os << this->XRayTubeCurrent;
1080   }
1081 
1082   os << "\n" << indent << "Exposure: ";
1083   if (this->Exposure)
1084   {
1085     os << this->Exposure;
1086   }
1087 
1088   os << "\n" << indent << "DirectionCosine: ("
1089      << this->DirectionCosine[0] << ", " << this->DirectionCosine[1]
1090      << ", " << this->DirectionCosine[2] << "), ("
1091      << this->DirectionCosine[3] << ", " << this->DirectionCosine[4]
1092      << ", " << this->DirectionCosine[5] << ")\n";
1093 
1094   this->Internals->Print(os, indent);
1095 }
1096 
1097