Tomato
itkImageFileReaderKW.hxx
1 /*=========================================================================
2  *
3  * Copyright Insight Software Consortium
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *=========================================================================*/
18 #ifndef itkImageFileReaderKW_hxx
19 #define itkImageFileReaderKW_hxx
20 #include "itkImageFileReaderKW.h"
21 
22 #include "itkObjectFactory.h"
23 #include "itkImageIOFactory.h"
24 #include "itkConvertPixelBuffer.h"
25 #include "itkPixelTraits.h"
26 #include "itkVectorImage.h"
27 
28 #include "itksys/SystemTools.hxx"
29 #include <fstream>
30 
31 namespace itk
32 {
33  template< typename TOutputImage, typename ConvertPixelTraits >
34  ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
35  ::ImageFileReaderKW()
36  {
37  m_ImageIO = ITK_NULLPTR;
38  this->SetFileName("");
39  m_UserSpecifiedImageIO = false;
40  m_UseStreaming = true;
41  }
42 
43  template< typename TOutputImage, typename ConvertPixelTraits >
44  ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
45  ::~ImageFileReaderKW()
46  {}
47 
48  template< typename TOutputImage, typename ConvertPixelTraits >
49  void ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
50  ::PrintSelf(std::ostream & os, Indent indent) const
51  {
52  Superclass::PrintSelf(os, indent);
53 
54  itkPrintSelfObjectMacro( ImageIO );
55 
56  os << indent << "UserSpecifiedImageIO flag: " << m_UserSpecifiedImageIO << "\n";
57  os << indent << "m_UseStreaming: " << m_UseStreaming << "\n";
58  }
59 
60  template< typename TOutputImage, typename ConvertPixelTraits >
61  void
63  ::SetImageIO(ImageIOBase *imageIO)
64  {
65  itkDebugMacro("setting ImageIO to " << imageIO);
66  if ( this->m_ImageIO != imageIO )
67  {
68  this->m_ImageIO = imageIO;
69  this->Modified();
70  }
71  m_UserSpecifiedImageIO = true;
72  }
73 
74  template< typename TOutputImage, typename ConvertPixelTraits >
75  void
78  {
79  typename TOutputImage::Pointer output = this->GetOutput();
80 
81  itkDebugMacro(<< "Reading file for GenerateOutputInformation()" << this->GetFileName());
82 
83  // Check to see if we can read the file given the name or prefix
84  //
85  if ( this->GetFileName() == "" )
86  {
87  throw ImageFileReaderException(__FILE__, __LINE__, "FileName must be specified", ITK_LOCATION);
88  }
89 
90  // Test if the file exists and if it can be opened.
91  // An exception will be thrown otherwise.
92  // We catch the exception because some ImageIO's may not actually
93  // open a file. Still reports file error if no ImageIO is loaded.
94 
95  try
96  {
97  m_ExceptionMessage = "";
98  this->TestFileExistanceAndReadability();
99  }
100  catch ( itk::ExceptionObject & err )
101  {
102  m_ExceptionMessage = err.GetDescription();
103  }
104 
105 #if !defined(SPECIFIC_IMAGEIO_MODULE_TEST)
106  if ( m_UserSpecifiedImageIO == false ) //try creating via factory
107  {
108  m_ImageIO = ImageIOFactory::CreateImageIO(this->GetFileName().c_str(), ImageIOFactory::ReadMode);
109  }
110 #endif
111 
112  if ( m_ImageIO.IsNull() )
113  {
114  std::ostringstream msg;
115  msg << " Could not create IO object for reading file "
116  << this->GetFileName().c_str() << std::endl;
117  if ( m_ExceptionMessage.size() )
118  {
119  msg << m_ExceptionMessage;
120  }
121  else
122  {
123  std::list< LightObject::Pointer > allobjects =
124  ObjectFactoryBase::CreateAllInstance("itkImageIOBase");
125  if (allobjects.size() > 0)
126  {
127  msg << " Tried to create one of the following:" << std::endl;
128  for ( std::list< LightObject::Pointer >::iterator i = allobjects.begin();
129  i != allobjects.end(); ++i )
130  {
131  ImageIOBase *io = dynamic_cast< ImageIOBase * >( i->GetPointer() );
132  msg << " " << io->GetNameOfClass() << std::endl;
133  }
134  msg << " You probably failed to set a file suffix, or" << std::endl;
135  msg << " set the suffix to an unsupported type." << std::endl;
136  }
137  else
138  {
139  msg << " There are no registered IO factories." << std::endl;
140  msg << " Please visit https://www.itk.org/Wiki/ITK/FAQ#NoFactoryException to diagnose the problem." << std::endl;
141  }
142  }
143  ImageFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION);
144  throw e;
145  return;
146  }
147 
148  // Got to allocate space for the image. Determine the characteristics of
149  // the image.
150  //
151  m_ImageIO->SetFileName( this->GetFileName().c_str() );
152  m_ImageIO->ReadImageInformation();
153 
154  SizeType dimSize;
155  double spacing[TOutputImage::ImageDimension];
156  double origin[TOutputImage::ImageDimension];
157  typename TOutputImage::DirectionType direction;
158 
159  std::vector< std::vector< double > > directionIO;
160 
161  const unsigned int numberOfDimensionsIO = m_ImageIO->GetNumberOfDimensions();
162 
163  if ( numberOfDimensionsIO > TOutputImage::ImageDimension )
164  {
165  for ( unsigned int k = 0; k < numberOfDimensionsIO; ++k )
166  {
167  directionIO.push_back( m_ImageIO->GetDefaultDirection(k) );
168  }
169  }
170  else
171  {
172  for ( unsigned int k = 0; k < numberOfDimensionsIO; ++k )
173  {
174  directionIO.push_back( m_ImageIO->GetDirection(k) );
175  }
176  }
177 
178  std::vector< double > axis;
179 
180  for ( unsigned int i = 0; i < TOutputImage::ImageDimension; ++i )
181  {
182  if ( i < numberOfDimensionsIO )
183  {
184  dimSize[i] = m_ImageIO->GetDimensions(i);
185  spacing[i] = m_ImageIO->GetSpacing(i);
186 
187  // KW hack
188  if (spacing[i] == 0) {
189  spacing[i] = 1;
190  }
191 
192  origin[i] = m_ImageIO->GetOrigin(i);
193 
194  // Please note: direction cosines are stored as columns of the
195  // direction matrix
196  axis = directionIO[i];
197  for ( unsigned j = 0; j < TOutputImage::ImageDimension; ++j )
198  {
199  if ( j < numberOfDimensionsIO )
200  {
201  direction[j][i] = axis[j];
202  }
203  else
204  {
205  direction[j][i] = 0.0;
206  }
207  }
208  }
209  else
210  {
211  // Number of dimensions in the output is more than number of dimensions
212  // in the ImageIO object (the file). Use default values for the size,
213  // spacing, origin and direction for the final (degenerate) dimensions.
214  dimSize[i] = 1;
215  spacing[i] = 1.0;
216  origin[i] = 0.0;
217  for ( unsigned j = 0; j < TOutputImage::ImageDimension; ++j )
218  {
219  if ( i == j )
220  {
221  direction[j][i] = 1.0;
222  }
223  else
224  {
225  direction[j][i] = 0.0;
226  }
227  }
228  }
229  }
230  // Spacing is expected to be greater than 0
231  // If negative, flip image direction along this axis.
232  for ( unsigned int i = 0; i < TOutputImage::ImageDimension; ++i )
233  {
234  if(spacing[i] < 0)
235  {
236  spacing[i] = -spacing[i];
237  for ( unsigned j = 0; j < TOutputImage::ImageDimension; ++j )
238  {
239  direction[j][i] = -direction[j][i];
240  }
241  }
242  }
243  output->SetSpacing(spacing); // Set the image spacing
244  output->SetOrigin(origin); // Set the image origin
245  output->SetDirection(direction); // Set the image direction cosines
246 
247  //Copy MetaDataDictionary from instantiated reader to output image.
248  output->SetMetaDataDictionary( m_ImageIO->GetMetaDataDictionary() );
249  this->SetMetaDataDictionary( m_ImageIO->GetMetaDataDictionary() );
250 
251  IndexType start;
252  start.Fill(0);
253 
254  ImageRegionType region;
255  region.SetSize(dimSize);
256  region.SetIndex(start);
257 
258  // If a VectorImage, this requires us to set the
259  // VectorLength before allocate
260  if ( strcmp(output->GetNameOfClass(), "VectorImage") == 0 )
261  {
262  typedef typename TOutputImage::AccessorFunctorType AccessorFunctorType;
263  AccessorFunctorType::SetVectorLength( output, m_ImageIO->GetNumberOfComponents() );
264  }
265 
266  output->SetLargestPossibleRegion(region);
267  }
268 
269  template< typename TOutputImage, typename ConvertPixelTraits >
270  void
273  {
274  // Test if the file exists.
275  if ( !itksys::SystemTools::FileExists( this->GetFileName().c_str() ) )
276  {
277  ImageFileReaderException e(__FILE__, __LINE__);
278  std::ostringstream msg;
279  msg << "The file doesn't exist. "
280  << std::endl << "Filename = " << this->GetFileName()
281  << std::endl;
282  e.SetDescription( msg.str().c_str() );
283  throw e;
284  return;
285  }
286 
287  // Test if the file can be open for reading access.
288  std::ifstream readTester;
289  readTester.open( this->GetFileName().c_str() );
290  if ( readTester.fail() )
291  {
292  readTester.close();
293  std::ostringstream msg;
294  msg << "The file couldn't be opened for reading. "
295  << std::endl << "Filename: " << this->GetFileName()
296  << std::endl;
297  ImageFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION);
298  throw e;
299  return;
300  }
301  readTester.close();
302  }
303 
304  template< typename TOutputImage, typename ConvertPixelTraits >
305  void
308  {
309  itkDebugMacro (<< "Starting EnlargeOutputRequestedRegion() ");
310  typename TOutputImage::Pointer out = dynamic_cast< TOutputImage * >( output );
311  typename TOutputImage::RegionType largestRegion = out->GetLargestPossibleRegion();
312  ImageRegionType streamableRegion;
313 
314  // The following code converts the ImageRegion (templated over dimension)
315  // into an ImageIORegion (not templated over dimension).
316  ImageRegionType imageRequestedRegion = out->GetRequestedRegion();
317  ImageIORegion ioRequestedRegion(TOutputImage::ImageDimension);
318 
319  typedef ImageIORegionAdaptor< TOutputImage::ImageDimension > ImageIOAdaptor;
320 
321  ImageIOAdaptor::Convert( imageRequestedRegion, ioRequestedRegion, largestRegion.GetIndex() );
322 
323  // Tell the IO if we should use streaming while reading
324  m_ImageIO->SetUseStreamedReading(m_UseStreaming);
325 
326  // Delegate to the ImageIO the computation of how the
327  // requested region must be enlarged.
328  m_ActualIORegion =
329  m_ImageIO->GenerateStreamableReadRegionFromRequestedRegion(ioRequestedRegion);
330 
331  // the m_ActualIORegion may be more dimensions then the output
332  // Image, in which case we still need to read this larger region to
333  // support reading the "first slice" of a larger image
334  // see bug 9212
335 
336  // convert the IORegion to a ImageRegion (which is dimension templated)
337  // if the ImageIO must read a higher dimension region, this will
338  // truncate the last dimensions
339  ImageIOAdaptor::Convert( m_ActualIORegion, streamableRegion, largestRegion.GetIndex() );
340 
341  // Check whether the imageRequestedRegion is fully contained inside the
342  // streamable region. Since, ImageRegion::IsInside regards zero
343  // sized regions, as not being inside any other region, we must
344  // specially check this condition to enable zero sized regions to
345  // pass the region propagation phase of the pipeline.
346  if ( !streamableRegion.IsInside(imageRequestedRegion)
347  && imageRequestedRegion.GetNumberOfPixels() != 0 )
348  {
349  // we must use a InvalidRequestedRegionError since
350  // DataObject::PropagateRequestedRegion() has an exception
351  // specification
352  std::ostringstream message;
353  message << "ImageIO returns IO region that does not fully contain the requested region"
354  << "Requested region: " << imageRequestedRegion
355  << "StreamableRegion region: " << streamableRegion;
356  InvalidRequestedRegionError e(__FILE__, __LINE__);
357  e.SetLocation(ITK_LOCATION);
358  e.SetDescription( message.str().c_str() );
359  throw e;
360  }
361 
362  itkDebugMacro (
363  << "RequestedRegion is set to:" << streamableRegion << " while the m_ActualIORegion is: " << m_ActualIORegion);
364 
365  out->SetRequestedRegion(streamableRegion);
366  }
367 
368  template< typename TOutputImage, typename ConvertPixelTraits >
371  {
372  this->UpdateProgress( 0.0f );
373 
374  typename TOutputImage::Pointer output = this->GetOutput();
375 
376  itkDebugMacro (<< "ImageFileReaderKW::GenerateData() \n"
377  << "Allocating the buffer with the EnlargedRequestedRegion \n"
378  << output->GetRequestedRegion() << "\n");
379 
380  // allocated the output image to the size of the enlarge requested region
381  this->AllocateOutputs();
382 
383  // Test if the file exists and if it can be opened.
384  // An exception will be thrown otherwise, since we can't
385  // successfully read the file. We catch the exception because some
386  // ImageIO's may not actually open a file. Still
387  // reports file error if no ImageIO is loaded.
388 
389  try
390  {
391  m_ExceptionMessage = "";
392  this->TestFileExistanceAndReadability();
393  }
394  catch ( itk::ExceptionObject & err )
395  {
396  m_ExceptionMessage = err.GetDescription();
397  }
398 
399  // Tell the ImageIO to read the file
400  m_ImageIO->SetFileName( this->GetFileName().c_str() );
401 
402  itkDebugMacro (<< "Setting imageIO IORegion to: " << m_ActualIORegion);
403  m_ImageIO->SetIORegion(m_ActualIORegion);
404 
405  char *loadBuffer = ITK_NULLPTR;
406  // the size of the buffer is computed based on the actual number of
407  // pixels to be read and the actual size of the pixels to be read
408  // (as opposed to the sizes of the output)
409  size_t sizeOfActualIORegion = m_ActualIORegion.GetNumberOfPixels()
410  * ( m_ImageIO->GetComponentSize() * m_ImageIO->GetNumberOfComponents() );
411 
412  try
413  {
414  ImageIOBase::IOComponentType ioType =
415  ImageIOBase
416  ::MapPixelType< typename ConvertPixelTraits::ComponentType >::CType;
417  if ( m_ImageIO->GetComponentType() != ioType
418  || ( m_ImageIO->GetNumberOfComponents() !=
419  ConvertPixelTraits::GetNumberOfComponents() ) )
420  {
421  // the pixel types don't match so a type conversion needs to be
422  // performed
423  itkDebugMacro( << "Buffer conversion required from: "
424  << m_ImageIO->GetComponentTypeAsString(m_ImageIO->GetComponentType())
425  << " to: "
426  << m_ImageIO->GetComponentTypeAsString(ioType)
427  << " ConvertPixelTraits::NumComponents "
428  << ConvertPixelTraits::GetNumberOfComponents()
429  << " m_ImageIO->NumComponents "
430  << m_ImageIO->GetNumberOfComponents() );
431 
432  loadBuffer = new char[sizeOfActualIORegion];
433  m_ImageIO->Read( static_cast< void * >( loadBuffer ) );
434 
435  // See note below as to why the buffered region is needed and
436  // not actualIOregion
437  this->DoConvertBuffer( static_cast< void * >( loadBuffer ),
438  output->GetBufferedRegion().GetNumberOfPixels() );
439  }
440  else if ( m_ActualIORegion.GetNumberOfPixels() !=
441  output->GetBufferedRegion().GetNumberOfPixels() )
442  {
443  // NOTE:
444  // for the number of pixels read and the number of pixels
445  // requested to not match, the dimensions of the two regions may
446  // be different, therefore we buffer and copy the pixels
447 
448  itkDebugMacro(<< "Buffer required because file dimension is greater then image dimension");
449 
450  OutputImagePixelType *outputBuffer = output->GetPixelContainer()->GetBufferPointer();
451 
452  loadBuffer = new char[sizeOfActualIORegion];
453  m_ImageIO->Read( static_cast< void * >( loadBuffer ) );
454 
455  // we use std::copy here as it should be optimized to memcpy for
456  // plain old data, but still is oop
457  std::copy(reinterpret_cast< const OutputImagePixelType * >( loadBuffer ),
458  reinterpret_cast< const OutputImagePixelType * >( loadBuffer ) + output->GetBufferedRegion().GetNumberOfPixels(),
459  outputBuffer);
460  }
461  else
462  {
463  itkDebugMacro(<< "No buffer conversion required.");
464 
465  OutputImagePixelType *outputBuffer = output->GetPixelContainer()->GetBufferPointer();
466  m_ImageIO->Read(outputBuffer);
467  }
468  }
469  catch ( ... )
470  {
471  // if an exception is thrown catch it
472 
473  // clean up
474  delete[] loadBuffer;
475  loadBuffer = ITK_NULLPTR;
476 
477  // then rethrow
478  throw;
479  }
480 
481  this->UpdateProgress( 1.0f );
482 
483  // clean up
484  delete[] loadBuffer;
485  loadBuffer = ITK_NULLPTR;
486  }
487 
488  template< typename TOutputImage, typename ConvertPixelTraits >
489  void
491  ::DoConvertBuffer(void *inputData,
492  size_t numberOfPixels)
493  {
494  // get the pointer to the destination buffer
495  OutputImagePixelType *outputData =
496  this->GetOutput()->GetPixelContainer()->GetBufferPointer();
497  bool isVectorImage(strcmp(this->GetOutput()->GetNameOfClass(),
498  "VectorImage") == 0);
499  // TODO:
500  // Pass down the PixelType (RGB, VECTOR, etc.) so that any vector to
501  // scalar conversion be type specific. i.e. RGB to scalar would use
502  // a formula to convert to luminance, VECTOR to scalar would use
503  // vector magnitude.
504 
505  // Create a macro as this code is a bit lengthy and repetitive
506  // if the ImageIO pixel type is typeid(type) then use the ConvertPixelBuffer
507  // class to convert the data block to TOutputImage's pixel type
508  // see DefaultConvertPixelTraits and ConvertPixelBuffer
509 
510  // The first else if block applies only to images of type itk::VectorImage
511  // VectorImage needs to copy out the buffer differently.. The buffer is of
512  // type InternalPixelType, but each pixel is really 'k' consecutive pixels.
513 
514 #define ITK_CONVERT_BUFFER_IF_BLOCK(_CType,type) \
515  else if(m_ImageIO->GetComponentType() == _CType) \
516  { \
517  if (isVectorImage) \
518  { \
519  ConvertPixelBuffer<type, \
520  OutputImagePixelType, \
521  ConvertPixelTraits \
522  > \
523  ::ConvertVectorImage(static_cast< type * >( inputData ), \
524  m_ImageIO->GetNumberOfComponents(), \
525  outputData, \
526  numberOfPixels); \
527  } \
528  else \
529  { \
530  ConvertPixelBuffer<type, \
531  OutputImagePixelType, \
532  ConvertPixelTraits \
533  > \
534  ::Convert(static_cast< type * >( inputData ), \
535  m_ImageIO->GetNumberOfComponents(), \
536  outputData, \
537  numberOfPixels); \
538  } \
539  }
540 
541  if(0) {}
542  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::UCHAR,unsigned char)
543  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::CHAR,char)
544  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::USHORT,unsigned short)
545  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::SHORT,short)
546  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::UINT,unsigned int)
547  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::INT,int)
548  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::ULONG,unsigned long)
549  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::LONG,long)
550  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::FLOAT,float)
551  ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::DOUBLE,double)
552  else
553  {
554 #define TYPENAME(x) \
555  m_ImageIO->GetComponentTypeAsString \
556  (ImageIOBase::MapPixelType<x>::CType)
557 
558  ImageFileReaderException e(__FILE__, __LINE__);
559  std::ostringstream msg;
560  msg << "Couldn't convert component type: "
561  << std::endl << " "
562  << m_ImageIO->GetComponentTypeAsString( m_ImageIO->GetComponentType() )
563  << std::endl << "to one of: "
564  << std::endl << " " << TYPENAME( unsigned char )
565  << std::endl << " " << TYPENAME( char )
566  << std::endl << " " << TYPENAME( unsigned short )
567  << std::endl << " " << TYPENAME( short )
568  << std::endl << " " << TYPENAME( unsigned int )
569  << std::endl << " " << TYPENAME( int )
570  << std::endl << " " << TYPENAME( unsigned long )
571  << std::endl << " " << TYPENAME( long )
572  << std::endl << " " << TYPENAME( float )
573  << std::endl << " " << TYPENAME( double )
574  << std::endl;
575  e.SetDescription( msg.str().c_str() );
576  e.SetLocation(ITK_LOCATION);
577  throw e;
578  return;
579  }
580 #undef ITK_CONVERT_BUFFER_IF_BLOCK
581  }
582 } //namespace ITK
583 
584 #endif
TOutputImage::InternalPixelType OutputImagePixelType
Definition: itkImageFileReaderKW.h:100
TOutputImage::RegionType ImageRegionType
Definition: itkImageFileReaderKW.h:97
TOutputImage::SizeType SizeType
Definition: itkImageFileReaderKW.h:91
virtual void GenerateOutputInformation(void) ITK_OVERRIDE
Definition: itkImageFileReaderKW.hxx:77
Definition: itkImageFileReaderKW.h:31
virtual void GenerateData() ITK_OVERRIDE
Definition: itkImageFileReaderKW.hxx:370
virtual void EnlargeOutputRequestedRegion(DataObject *output) ITK_OVERRIDE
Definition: itkImageFileReaderKW.hxx:307
void TestFileExistanceAndReadability()
Definition: itkImageFileReaderKW.hxx:272
void SetImageIO(ImageIOBase *imageIO)
Definition: itkImageFileReaderKW.hxx:63
TOutputImage::IndexType IndexType
Definition: itkImageFileReaderKW.h:94
void DoConvertBuffer(void *buffer, size_t numberOfPixels)
Definition: itkImageFileReaderKW.hxx:491