Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkITKDICOMSeriesReaderHelper.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 //#define MBILOG_ENABLE_DEBUG
18 
19 #include <dcvrdt.h>
20 
21 #define BOOST_DATE_TIME_NO_LIB
22 //Prevent unnecessary/unwanted auto link in this compilation when activating boost libraries in the MITK superbuild
23 //It is necessary because BOOST_ALL_DYN_LINK overwrites BOOST_DATE_TIME_NO_LIB
24 #if defined(BOOST_ALL_DYN_LINK)
25  #undef BOOST_ALL_DYN_LINK
26 #endif
27 
28 #include <boost/date_time/posix_time/posix_time_types.hpp>
29 
32 
35 
36 #define switch3DCase( IOType, T ) \
37  case IOType: \
38  return LoadDICOMByITK<T>( filenames, correctTilt, tiltInfo, io );
39 
41 {
42  MITK_DEBUG << "ITKDICOMSeriesReaderHelper::CanHandleFile " << filename;
44  return tester->CanReadFile( filename.c_str() );
45 }
46 
48  bool correctTilt,
49  const GantryTiltInformation& tiltInfo )
50 {
51  if ( filenames.empty() )
52  {
54  << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic.";
55  return nullptr; // this is not actually an error but the result is very simple
56  }
57 
58  typedef itk::GDCMImageIO DcmIoType;
60 
61  try
62  {
63  if ( io->CanReadFile( filenames.front().c_str() ) )
64  {
65  io->SetFileName( filenames.front().c_str() );
66  io->ReadImageInformation();
67 
68  if ( io->GetPixelType() == itk::ImageIOBase::SCALAR )
69  {
70  switch ( io->GetComponentType() )
71  {
72  switch3DCase(DcmIoType::UCHAR, unsigned char) switch3DCase(DcmIoType::CHAR, char) switch3DCase(
73  DcmIoType::USHORT, unsigned short) switch3DCase(DcmIoType::SHORT, short)
74  switch3DCase(DcmIoType::UINT, unsigned int) switch3DCase(DcmIoType::INT, int) switch3DCase(
75  DcmIoType::ULONG, long unsigned int) switch3DCase(DcmIoType::LONG, long int)
76  switch3DCase(DcmIoType::FLOAT, float) switch3DCase(DcmIoType::DOUBLE, double) default
77  : MITK_ERROR
78  << "Found unsupported DICOM scalar pixel type: (enum value) "
79  << io->GetComponentType();
80  }
81  }
82  else if ( io->GetPixelType() == itk::ImageIOBase::RGB )
83  {
84  switch ( io->GetComponentType() )
85  {
86  switch3DCase(DcmIoType::UCHAR, itk::RGBPixel<unsigned char>) switch3DCase(
87  DcmIoType::CHAR, itk::RGBPixel<char>) switch3DCase(DcmIoType::USHORT,
88  itk::RGBPixel<unsigned short>)
89  switch3DCase(DcmIoType::SHORT, itk::RGBPixel<short>) switch3DCase(
90  DcmIoType::UINT, itk::RGBPixel<unsigned int>) switch3DCase(DcmIoType::INT, itk::RGBPixel<int>)
91  switch3DCase(DcmIoType::ULONG, itk::RGBPixel<long unsigned int>)
92  switch3DCase(DcmIoType::LONG, itk::RGBPixel<long int>) switch3DCase(
93  DcmIoType::FLOAT, itk::RGBPixel<float>) switch3DCase(DcmIoType::DOUBLE,
94  itk::RGBPixel<double>) default
95  : MITK_ERROR
96  << "Found unsupported DICOM scalar pixel type: (enum value) "
97  << io->GetComponentType();
98  }
99  }
100 
101  MITK_ERROR << "Unsupported DICOM pixel type";
102  return nullptr;
103  }
104  }
105  catch ( const itk::MemoryAllocationError& e )
106  {
107  MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what();
108  }
109  catch ( const std::exception& e )
110  {
111  MITK_ERROR << "Error encountered when loading DICOM series:" << e.what();
112  }
113  catch ( ... )
114  {
115  MITK_ERROR << "Unspecified error encountered when loading DICOM series.";
116  }
117 
118  return nullptr;
119 }
120 
121 #define switch3DnTCase( IOType, T ) \
122  case IOType: \
123  return LoadDICOMByITK3DnT<T>( filenamesLists, correctTilt, tiltInfo, io );
124 
126  bool correctTilt,
127  const GantryTiltInformation& tiltInfo )
128 {
129  if ( filenamesLists.empty() || filenamesLists.front().empty() )
130  {
131  MITK_DEBUG
132  << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic.";
133  return nullptr; // this is not actually an error but the result is very simple
134  }
135 
136  typedef itk::GDCMImageIO DcmIoType;
138 
139  try
140  {
141  if ( io->CanReadFile( filenamesLists.front().front().c_str() ) )
142  {
143  io->SetFileName( filenamesLists.front().front().c_str() );
144  io->ReadImageInformation();
145 
146  if ( io->GetPixelType() == itk::ImageIOBase::SCALAR )
147  {
148  switch ( io->GetComponentType() )
149  {
150  switch3DnTCase(DcmIoType::UCHAR, unsigned char) switch3DnTCase(DcmIoType::CHAR, char)
151  switch3DnTCase(DcmIoType::USHORT, unsigned short) switch3DnTCase(
152  DcmIoType::SHORT, short) switch3DnTCase(DcmIoType::UINT,
153  unsigned int) switch3DnTCase(DcmIoType::INT, int)
154  switch3DnTCase(DcmIoType::ULONG, long unsigned int) switch3DnTCase(DcmIoType::LONG, long int)
155  switch3DnTCase(DcmIoType::FLOAT, float) switch3DnTCase(DcmIoType::DOUBLE, double) default
156  : MITK_ERROR
157  << "Found unsupported DICOM scalar pixel type: (enum value) "
158  << io->GetComponentType();
159  }
160  }
161  else if ( io->GetPixelType() == itk::ImageIOBase::RGB )
162  {
163  switch ( io->GetComponentType() )
164  {
165  switch3DnTCase(DcmIoType::UCHAR, itk::RGBPixel<unsigned char>)
166  switch3DnTCase(DcmIoType::CHAR, itk::RGBPixel<char>) switch3DnTCase(
167  DcmIoType::USHORT, itk::RGBPixel<unsigned short>) switch3DnTCase(DcmIoType::SHORT,
168  itk::RGBPixel<short>)
169  switch3DnTCase(DcmIoType::UINT, itk::RGBPixel<unsigned int>) switch3DnTCase(
170  DcmIoType::INT, itk::RGBPixel<int>) switch3DnTCase(DcmIoType::ULONG,
171  itk::RGBPixel<long unsigned int>)
172  switch3DnTCase(DcmIoType::LONG, itk::RGBPixel<long int>) switch3DnTCase(
173  DcmIoType::FLOAT, itk::RGBPixel<float>) switch3DnTCase(DcmIoType::DOUBLE,
174  itk::RGBPixel<double>) default
175  : MITK_ERROR
176  << "Found unsupported DICOM scalar pixel type: (enum value) "
177  << io->GetComponentType();
178  }
179  }
180 
181  MITK_ERROR << "Unsupported DICOM pixel type";
182  return nullptr;
183  }
184  }
185  catch ( const itk::MemoryAllocationError& e )
186  {
187  MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what();
188  }
189  catch ( const std::exception& e )
190  {
191  MITK_ERROR << "Error encountered when loading DICOM series:" << e.what();
192  }
193  catch ( ... )
194  {
195  MITK_ERROR << "Unspecified error encountered when loading DICOM series.";
196  }
197 
198  return nullptr;
199 }
200 
201 bool ConvertDICOMDateTimeString( const std::string& dateString,
202  const std::string& timeString,
203  OFDateTime& time )
204 {
205  OFString content( timeString.c_str() );
206 
207  if ( !dateString.empty() )
208  {
209  content = OFString( dateString.c_str() ).append( content );
210  }
211 
212  const OFCondition result = DcmDateTime::getOFDateTimeFromString( content, time );
213 
214  return result.good();
215 }
216 
217 boost::posix_time::ptime ConvertOFDateTimeToPTime( const OFDateTime& time )
218 {
219  const boost::gregorian::date boostDate(
220  time.getDate().getYear(), time.getDate().getMonth(), time.getDate().getDay() );
221 
222  const boost::posix_time::time_duration boostTime =
223  boost::posix_time::hours( time.getTime().getHour() )
224  + boost::posix_time::minutes( time.getTime().getMinute() )
225  + boost::posix_time::seconds( time.getTime().getSecond() )
226  + boost::posix_time::milliseconds( time.getTime().getMilliSecond() );
227 
228  boost::posix_time::ptime result( boostDate, boostTime );
229 
230  return result;
231 }
232 
233 OFDateTime GetLowerDateTime( const OFDateTime& time1, const OFDateTime& time2 )
234 {
235  OFDateTime result = time1;
236 
237  if ( ( time2.getDate() < time1.getDate() )
238  || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() < time1.getTime() ) ) )
239  {
240  result = time2;
241  }
242 
243  return result;
244 }
245 
246 OFDateTime GetUpperDateTime( const OFDateTime& time1, const OFDateTime& time2 )
247 {
248  OFDateTime result = time1;
249 
250  if ( ( time2.getDate() > time1.getDate() )
251  || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() > time1.getTime() ) ) )
252  {
253  result = time2;
254  }
255 
256  return result;
257 }
258 
259 double ComputeMiliSecDuration( const OFDateTime& start, const OFDateTime& stop )
260 {
261  const boost::posix_time::ptime startTime = ConvertOFDateTimeToPTime( start );
262  const boost::posix_time::ptime stopTime = ConvertOFDateTimeToPTime( stop );
263 
264  ::boost::posix_time::time_duration duration = stopTime - startTime;
265 
266  double result = duration.total_milliseconds();
267 
268  return result;
269 }
270 
271 bool mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeStep(
272  const StringContainer& filenamesOfTimeStep, DateTimeBounds& bounds )
273 {
274  const DICOMTag acquisitionDateTag( 0x0008, 0x0022 );
275  const DICOMTag acquisitionTimeTag( 0x0008, 0x0032 );
276 
278  filescanner->SetInputFiles( filenamesOfTimeStep );
279  filescanner->AddTag( acquisitionDateTag );
280  filescanner->AddTag( acquisitionTimeTag );
281  filescanner->Scan();
282 
283  const DICOMDatasetAccessingImageFrameList frameList = filescanner->GetFrameInfoList();
284 
285  bool result = false;
286  bool first = true;
287 
288  for (DICOMDatasetAccessingImageFrameList::const_iterator pos = frameList.cbegin(); pos != frameList.cend(); ++pos)
289  {
290  const std::string dateStr = ( *pos )->GetTagValueAsString( acquisitionDateTag ).value;
291  const std::string timeStr = ( *pos )->GetTagValueAsString( acquisitionTimeTag ).value;
292 
293  OFDateTime time;
294  const bool convertResult = ConvertDICOMDateTimeString( dateStr, timeStr, time );
295 
296  if ( convertResult )
297  {
298  if ( first )
299  {
300  bounds[0] = time;
301  bounds[1] = time;
302  first = false;
303  }
304  else
305  {
306  bounds[0] = GetLowerDateTime( bounds[0], time );
307  bounds[1] = GetUpperDateTime( bounds[1], time );
308  }
309  result = true;
310  }
311  }
312 
313  return result;
314 };
315 
316 mitk::ITKDICOMSeriesReaderHelper::TimeBoundsList
317  mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeSteps(
318  const StringContainerList& filenamesOfTimeSteps )
319 {
320  TimeBoundsList result;
321 
322  OFDateTime baseLine;
323  bool baseLineSet = false;
324 
325  for ( StringContainerList::const_iterator pos = filenamesOfTimeSteps.cbegin();
326  pos != filenamesOfTimeSteps.cend();
327  ++pos )
328  {
329  TimeBounds bounds( 0.0 );
330  DateTimeBounds dateTimeBounds;
331 
332  if ( ExtractTimeBoundsOfTimeStep( *pos, dateTimeBounds ) )
333  {
334  if ( !baseLineSet )
335  {
336  baseLineSet = true;
337  baseLine = dateTimeBounds[0];
338  }
339 
340  bounds[0] = ComputeMiliSecDuration( baseLine, dateTimeBounds[0] );
341  bounds[1] = ComputeMiliSecDuration( baseLine, dateTimeBounds[1] );
342  }
343 
344  result.push_back( bounds );
345  }
346 
347  return result;
348 };
349 
351  mitk::ITKDICOMSeriesReaderHelper::GenerateTimeGeometry( const BaseGeometry* templateGeometry,
352  const TimeBoundsList& boundsList )
353 {
354  TimeGeometry::Pointer timeGeometry;
355 
356  double check = 0.0;
357  const auto boundListSize = boundsList.size();
358  for ( auto pos = 0; pos < boundListSize; ++pos )
359  {
360  check += boundsList[pos][0];
361  check += boundsList[pos][1];
362  }
363 
364  if ( check < mitk::eps )
365  { // if all bounds are zero we assume that the bounds could not be correctly determined
366  // and as a fallback generate a time geometry in the old mitk style
368  newTimeGeometry->Initialize( templateGeometry, boundListSize );
369  timeGeometry = newTimeGeometry.GetPointer();
370  }
371  else
372  {
374  newTimeGeometry->ClearAllGeometries();
375  newTimeGeometry->ReserveSpaceForGeometries( boundListSize );
376 
377  for ( auto pos = 0; pos < boundListSize; ++pos )
378  {
379  TimeBounds bounds = boundsList[pos];
380  if ( pos + 1 < boundListSize )
381  {
382  bounds[1] = boundsList[pos + 1][0];
383  }
384 
385  newTimeGeometry->AppendTimeStepClone( templateGeometry, bounds[1], bounds[0] );
386  }
387  timeGeometry = newTimeGeometry.GetPointer();
388  }
389 
390  return timeGeometry;
391 };
itk::SmartPointer< Self > Pointer
OFDateTime GetUpperDateTime(const OFDateTime &time1, const OFDateTime &time2)
itk::FixedArray< ScalarType, 2 > TimeBounds
Standard typedef for time-bounds.
#define MITK_ERROR
Definition: mitkLogMacros.h:24
static bool CanHandleFile(const std::string &filename)
Image::Pointer Load(const StringContainer &filenames, bool correctTilt, const GantryTiltInformation &tiltInfo)
#define MITK_DEBUG
Definition: mitkLogMacros.h:26
static Pointer New()
OFDateTime GetLowerDateTime(const OFDateTime &time1, const OFDateTime &time2)
Image::Pointer Load3DnT(const StringContainerList &filenamesLists, bool correctTilt, const GantryTiltInformation &tiltInfo)
#define switch3DnTCase(IOType, T)
static const std::string filename
bool ConvertDICOMDateTimeString(const std::string &dateString, const std::string &timeString, OFDateTime &time)
std::list< StringContainer > StringContainerList
itk::SmartPointer< Self > Pointer
std::vector< DICOMDatasetAccessingImageFrameInfo::Pointer > DICOMDatasetAccessingImageFrameList
Gantry tilt analysis result.
itk::SmartPointer< Self > Pointer
boost::posix_time::ptime ConvertOFDateTimeToPTime(const OFDateTime &time)
MITKCORE_EXPORT const ScalarType eps
double ComputeMiliSecDuration(const OFDateTime &start, const OFDateTime &stop)
#define switch3DCase(IOType, T)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.