Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.