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