Raw File
itkImageFileReaderKW.hxx
/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkImageFileReaderKW_hxx
#define itkImageFileReaderKW_hxx
#include "itkImageFileReaderKW.h"

#include "itkObjectFactory.h"
#include "itkImageIOFactory.h"
#include "itkConvertPixelBuffer.h"
#include "itkPixelTraits.h"
#include "itkVectorImage.h"

#include "itksys/SystemTools.hxx"
#include <fstream>

namespace itk
{
    template< typename TOutputImage, typename ConvertPixelTraits >
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::ImageFileReaderKW()
    {
        m_ImageIO = ITK_NULLPTR;
        this->SetFileName("");
        m_UserSpecifiedImageIO = false;
        m_UseStreaming = true;
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::~ImageFileReaderKW()
    {}

    template< typename TOutputImage, typename ConvertPixelTraits >
    void ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::PrintSelf(std::ostream & os, Indent indent) const
    {
        Superclass::PrintSelf(os, indent);

        itkPrintSelfObjectMacro( ImageIO );

        os << indent << "UserSpecifiedImageIO flag: " << m_UserSpecifiedImageIO << "\n";
        os << indent << "m_UseStreaming: " << m_UseStreaming << "\n";
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    void
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::SetImageIO(ImageIOBase *imageIO)
    {
        itkDebugMacro("setting ImageIO to " << imageIO);
        if ( this->m_ImageIO != imageIO )
        {
            this->m_ImageIO = imageIO;
            this->Modified();
        }
        m_UserSpecifiedImageIO = true;
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    void
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::GenerateOutputInformation(void)
    {
        typename TOutputImage::Pointer output = this->GetOutput();

        itkDebugMacro(<< "Reading file for GenerateOutputInformation()" << this->GetFileName());

        // Check to see if we can read the file given the name or prefix
        //
        if ( this->GetFileName() == "" )
        {
            throw ImageFileReaderException(__FILE__, __LINE__, "FileName must be specified", ITK_LOCATION);
        }

        // Test if the file exists and if it can be opened.
        // An exception will be thrown otherwise.
        // We catch the exception because some ImageIO's may not actually
        // open a file. Still reports file error if no ImageIO is loaded.

        try
        {
            m_ExceptionMessage = "";
            this->TestFileExistanceAndReadability();
        }
        catch ( itk::ExceptionObject & err )
        {
            m_ExceptionMessage = err.GetDescription();
        }

#if !defined(SPECIFIC_IMAGEIO_MODULE_TEST)
        if ( m_UserSpecifiedImageIO == false ) //try creating via factory
        {
            m_ImageIO = ImageIOFactory::CreateImageIO(this->GetFileName().c_str(), ImageIOFactory::ReadMode);
        }
#endif

        if ( m_ImageIO.IsNull() )
        {
            std::ostringstream msg;
            msg << " Could not create IO object for reading file "
                << this->GetFileName().c_str() << std::endl;
            if ( m_ExceptionMessage.size() )
            {
                msg << m_ExceptionMessage;
            }
            else
            {
                std::list< LightObject::Pointer > allobjects =
                        ObjectFactoryBase::CreateAllInstance("itkImageIOBase");
                if (allobjects.size() > 0)
                {
                    msg << "  Tried to create one of the following:" << std::endl;
                    for ( std::list< LightObject::Pointer >::iterator i = allobjects.begin();
                          i != allobjects.end(); ++i )
                    {
                        ImageIOBase *io = dynamic_cast< ImageIOBase * >( i->GetPointer() );
                        msg << "    " << io->GetNameOfClass() << std::endl;
                    }
                    msg << "  You probably failed to set a file suffix, or" << std::endl;
                    msg << "    set the suffix to an unsupported type." << std::endl;
                }
                else
                {
                    msg << "  There are no registered IO factories." << std::endl;
                    msg << "  Please visit https://www.itk.org/Wiki/ITK/FAQ#NoFactoryException to diagnose the problem." << std::endl;
                }
            }
            ImageFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION);
            throw e;
            return;
        }

        // Got to allocate space for the image. Determine the characteristics of
        // the image.
        //
        m_ImageIO->SetFileName( this->GetFileName().c_str() );
        m_ImageIO->ReadImageInformation();

        SizeType dimSize;
        double   spacing[TOutputImage::ImageDimension];
        double   origin[TOutputImage::ImageDimension];
        typename TOutputImage::DirectionType direction;

        std::vector< std::vector< double > > directionIO;

        const unsigned int numberOfDimensionsIO = m_ImageIO->GetNumberOfDimensions();

        if ( numberOfDimensionsIO > TOutputImage::ImageDimension )
        {
            for ( unsigned int k = 0; k < numberOfDimensionsIO; ++k )
            {
                directionIO.push_back( m_ImageIO->GetDefaultDirection(k) );
            }
        }
        else
        {
            for ( unsigned int k = 0; k < numberOfDimensionsIO; ++k )
            {
                directionIO.push_back( m_ImageIO->GetDirection(k) );
            }
        }

        std::vector< double > axis;

        for ( unsigned int i = 0; i < TOutputImage::ImageDimension; ++i )
        {
            if ( i < numberOfDimensionsIO )
            {
                dimSize[i] = m_ImageIO->GetDimensions(i);
                spacing[i] = m_ImageIO->GetSpacing(i);

                // KW hack
                if (spacing[i] == 0) {
                    spacing[i] = 1;
                }

                origin[i]  = m_ImageIO->GetOrigin(i);

                // Please note: direction cosines are stored as columns of the
                // direction matrix
                axis = directionIO[i];
                for ( unsigned j = 0; j < TOutputImage::ImageDimension; ++j )
                {
                    if ( j < numberOfDimensionsIO )
                    {
                        direction[j][i] = axis[j];
                    }
                    else
                    {
                        direction[j][i] = 0.0;
                    }
                }
            }
            else
            {
                // Number of dimensions in the output is more than number of dimensions
                // in the ImageIO object (the file).  Use default values for the size,
                // spacing, origin and direction for the final (degenerate) dimensions.
                dimSize[i] = 1;
                spacing[i] = 1.0;
                origin[i] = 0.0;
                for ( unsigned j = 0; j < TOutputImage::ImageDimension; ++j )
                {
                    if ( i == j )
                    {
                        direction[j][i] = 1.0;
                    }
                    else
                    {
                        direction[j][i] = 0.0;
                    }
                }
            }
        }
        // Spacing is expected to be greater than 0
        // If negative, flip image direction along this axis.
        for ( unsigned int i = 0; i < TOutputImage::ImageDimension; ++i )
        {
            if(spacing[i] < 0)
            {
                spacing[i] = -spacing[i];
                for ( unsigned j = 0; j < TOutputImage::ImageDimension; ++j )
                {
                    direction[j][i] = -direction[j][i];
                }
            }
        }
        output->SetSpacing(spacing);       // Set the image spacing
        output->SetOrigin(origin);         // Set the image origin
        output->SetDirection(direction);   // Set the image direction cosines

        //Copy MetaDataDictionary from instantiated reader to output image.
        output->SetMetaDataDictionary( m_ImageIO->GetMetaDataDictionary() );
        this->SetMetaDataDictionary( m_ImageIO->GetMetaDataDictionary() );

        IndexType start;
        start.Fill(0);

        ImageRegionType region;
        region.SetSize(dimSize);
        region.SetIndex(start);

        // If a VectorImage, this requires us to set the
        // VectorLength before allocate
        if ( strcmp(output->GetNameOfClass(), "VectorImage") == 0 )
        {
            typedef typename TOutputImage::AccessorFunctorType AccessorFunctorType;
            AccessorFunctorType::SetVectorLength( output, m_ImageIO->GetNumberOfComponents() );
        }

        output->SetLargestPossibleRegion(region);
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    void
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::TestFileExistanceAndReadability()
    {
        // Test if the file exists.
        if ( !itksys::SystemTools::FileExists( this->GetFileName().c_str() ) )
        {
            ImageFileReaderException e(__FILE__, __LINE__);
            std::ostringstream       msg;
            msg << "The file doesn't exist. "
                << std::endl << "Filename = " << this->GetFileName()
                << std::endl;
            e.SetDescription( msg.str().c_str() );
            throw e;
            return;
        }

        // Test if the file can be open for reading access.
        std::ifstream readTester;
        readTester.open( this->GetFileName().c_str() );
        if ( readTester.fail() )
        {
            readTester.close();
            std::ostringstream msg;
            msg << "The file couldn't be opened for reading. "
                << std::endl << "Filename: " << this->GetFileName()
                << std::endl;
            ImageFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION);
            throw e;
            return;
        }
        readTester.close();
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    void
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::EnlargeOutputRequestedRegion(DataObject *output)
    {
        itkDebugMacro (<< "Starting EnlargeOutputRequestedRegion() ");
        typename TOutputImage::Pointer out = dynamic_cast< TOutputImage * >( output );
        typename TOutputImage::RegionType largestRegion = out->GetLargestPossibleRegion();
        ImageRegionType streamableRegion;

        // The following code converts the ImageRegion (templated over dimension)
        // into an ImageIORegion (not templated over dimension).
        ImageRegionType imageRequestedRegion = out->GetRequestedRegion();
        ImageIORegion   ioRequestedRegion(TOutputImage::ImageDimension);

        typedef ImageIORegionAdaptor< TOutputImage::ImageDimension > ImageIOAdaptor;

        ImageIOAdaptor::Convert( imageRequestedRegion, ioRequestedRegion, largestRegion.GetIndex() );

        // Tell the IO if we should use streaming while reading
        m_ImageIO->SetUseStreamedReading(m_UseStreaming);

        // Delegate to the ImageIO the computation of how the
        // requested region must be enlarged.
        m_ActualIORegion  =
                m_ImageIO->GenerateStreamableReadRegionFromRequestedRegion(ioRequestedRegion);

        // the m_ActualIORegion may be more dimensions then the output
        // Image, in which case we still need to read this larger region to
        // support reading the "first slice" of a larger image
        // see bug 9212

        // convert the IORegion to a ImageRegion (which is dimension templated)
        // if the ImageIO must read a higher dimension region, this will
        // truncate the last dimensions
        ImageIOAdaptor::Convert( m_ActualIORegion, streamableRegion, largestRegion.GetIndex() );

        // Check whether the imageRequestedRegion is fully contained inside the
        // streamable region. Since, ImageRegion::IsInside regards zero
        // sized regions, as not being inside any other region, we must
        // specially check this condition to enable zero sized regions to
        // pass the region propagation phase of the pipeline.
        if ( !streamableRegion.IsInside(imageRequestedRegion)
             && imageRequestedRegion.GetNumberOfPixels() != 0 )
        {
            // we must use a InvalidRequestedRegionError since
            // DataObject::PropagateRequestedRegion() has an exception
            // specification
            std::ostringstream message;
            message << "ImageIO returns IO region that does not fully contain the requested region"
                    << "Requested region: " << imageRequestedRegion
                    << "StreamableRegion region: " << streamableRegion;
            InvalidRequestedRegionError e(__FILE__, __LINE__);
            e.SetLocation(ITK_LOCATION);
            e.SetDescription( message.str().c_str() );
            throw e;
        }

        itkDebugMacro (
                << "RequestedRegion is set to:" << streamableRegion << " while the m_ActualIORegion is: " << m_ActualIORegion);

        out->SetRequestedRegion(streamableRegion);
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    void ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::GenerateData()
    {
        this->UpdateProgress( 0.0f );

        typename TOutputImage::Pointer output = this->GetOutput();

        itkDebugMacro (<< "ImageFileReaderKW::GenerateData() \n"
                               << "Allocating the buffer with the EnlargedRequestedRegion \n"
                               << output->GetRequestedRegion() << "\n");

        // allocated the output image to the size of the enlarge requested region
        this->AllocateOutputs();

        // Test if the file exists and if it can be opened.
        // An exception will be thrown otherwise, since we can't
        // successfully read the file. We catch the exception because some
        // ImageIO's may not actually open a file. Still
        // reports file error if no ImageIO is loaded.

        try
        {
            m_ExceptionMessage = "";
            this->TestFileExistanceAndReadability();
        }
        catch ( itk::ExceptionObject & err )
        {
            m_ExceptionMessage = err.GetDescription();
        }

        // Tell the ImageIO to read the file
        m_ImageIO->SetFileName( this->GetFileName().c_str() );

        itkDebugMacro (<< "Setting imageIO IORegion to: " << m_ActualIORegion);
        m_ImageIO->SetIORegion(m_ActualIORegion);

        char *loadBuffer = ITK_NULLPTR;
        // the size of the buffer is computed based on the actual number of
        // pixels to be read and the actual size of the pixels to be read
        // (as opposed to the sizes of the output)
        size_t sizeOfActualIORegion = m_ActualIORegion.GetNumberOfPixels()
                                      * ( m_ImageIO->GetComponentSize() * m_ImageIO->GetNumberOfComponents() );

        try
        {
            ImageIOBase::IOComponentType ioType =
                    ImageIOBase
                    ::MapPixelType< typename ConvertPixelTraits::ComponentType >::CType;
            if ( m_ImageIO->GetComponentType() != ioType
                 || ( m_ImageIO->GetNumberOfComponents() !=
                      ConvertPixelTraits::GetNumberOfComponents() ) )
            {
                // the pixel types don't match so a type conversion needs to be
                // performed
                itkDebugMacro( << "Buffer conversion required from: "
                                       << m_ImageIO->GetComponentTypeAsString(m_ImageIO->GetComponentType())
                                       << " to: "
                                       << m_ImageIO->GetComponentTypeAsString(ioType)
                                       << " ConvertPixelTraits::NumComponents "
                                       << ConvertPixelTraits::GetNumberOfComponents()
                                       << " m_ImageIO->NumComponents "
                                       << m_ImageIO->GetNumberOfComponents() );

                loadBuffer = new char[sizeOfActualIORegion];
                m_ImageIO->Read( static_cast< void * >( loadBuffer ) );

                // See note below as to why the buffered region is needed and
                // not actualIOregion
                this->DoConvertBuffer( static_cast< void * >( loadBuffer ),
                                       output->GetBufferedRegion().GetNumberOfPixels() );
            }
            else if ( m_ActualIORegion.GetNumberOfPixels() !=
                      output->GetBufferedRegion().GetNumberOfPixels() )
            {
                // NOTE:
                // for the number of pixels read and the number of pixels
                // requested to not match, the dimensions of the two regions may
                // be different, therefore we buffer and copy the pixels

                itkDebugMacro(<< "Buffer required because file dimension is greater then image dimension");

                OutputImagePixelType *outputBuffer = output->GetPixelContainer()->GetBufferPointer();

                loadBuffer = new char[sizeOfActualIORegion];
                m_ImageIO->Read( static_cast< void * >( loadBuffer ) );

                // we use std::copy here as it should be optimized to memcpy for
                // plain old data, but still is oop
                std::copy(reinterpret_cast< const OutputImagePixelType * >( loadBuffer ),
                          reinterpret_cast< const OutputImagePixelType * >( loadBuffer ) + output->GetBufferedRegion().GetNumberOfPixels(),
                          outputBuffer);
            }
            else
            {
                itkDebugMacro(<< "No buffer conversion required.");

                OutputImagePixelType *outputBuffer = output->GetPixelContainer()->GetBufferPointer();
                m_ImageIO->Read(outputBuffer);
            }
        }
        catch ( ... )
        {
            // if an exception is thrown catch it

            // clean up
            delete[] loadBuffer;
            loadBuffer = ITK_NULLPTR;

            // then rethrow
            throw;
        }

        this->UpdateProgress( 1.0f );

        // clean up
        delete[] loadBuffer;
        loadBuffer = ITK_NULLPTR;
    }

    template< typename TOutputImage, typename ConvertPixelTraits >
    void
    ImageFileReaderKW< TOutputImage, ConvertPixelTraits >
    ::DoConvertBuffer(void *inputData,
                      size_t numberOfPixels)
    {
        // get the pointer to the destination buffer
        OutputImagePixelType *outputData =
                this->GetOutput()->GetPixelContainer()->GetBufferPointer();
        bool isVectorImage(strcmp(this->GetOutput()->GetNameOfClass(),
                                  "VectorImage") == 0);
        // TODO:
        // Pass down the PixelType (RGB, VECTOR, etc.) so that any vector to
        // scalar conversion be type specific. i.e. RGB to scalar would use
        // a formula to convert to luminance, VECTOR to scalar would use
        // vector magnitude.

        // Create a macro as this code is a bit lengthy and repetitive
        // if the ImageIO pixel type is typeid(type) then use the ConvertPixelBuffer
        // class to convert the data block to TOutputImage's pixel type
        // see DefaultConvertPixelTraits and ConvertPixelBuffer

        // The first else if block applies only to images of type itk::VectorImage
        // VectorImage needs to copy out the buffer differently.. The buffer is of
        // type InternalPixelType, but each pixel is really 'k' consecutive pixels.

#define ITK_CONVERT_BUFFER_IF_BLOCK(_CType,type)                        \
  else if(m_ImageIO->GetComponentType() == _CType)                      \
    {                                                                   \
    if (isVectorImage)                                                  \
      {                                                                 \
      ConvertPixelBuffer<type,                                          \
                         OutputImagePixelType,                          \
                         ConvertPixelTraits                             \
                         >                                              \
        ::ConvertVectorImage(static_cast< type * >( inputData ),        \
                             m_ImageIO->GetNumberOfComponents(),        \
                             outputData,                                \
                             numberOfPixels);                           \
      }                                                                 \
    else                                                                \
      {                                                                 \
      ConvertPixelBuffer<type,                                          \
                         OutputImagePixelType,                          \
                         ConvertPixelTraits                             \
                         >                                              \
        ::Convert(static_cast< type * >( inputData ),                   \
                  m_ImageIO->GetNumberOfComponents(),                   \
                  outputData,                                           \
                  numberOfPixels);                                      \
      }                                                                 \
    }

        if(0) {}
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::UCHAR,unsigned char)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::CHAR,char)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::USHORT,unsigned short)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::SHORT,short)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::UINT,unsigned int)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::INT,int)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::ULONG,unsigned long)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::LONG,long)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::FLOAT,float)
        ITK_CONVERT_BUFFER_IF_BLOCK(ImageIOBase::DOUBLE,double)
        else
        {
#define TYPENAME(x)                                     \
    m_ImageIO->GetComponentTypeAsString                 \
      (ImageIOBase::MapPixelType<x>::CType)

            ImageFileReaderException e(__FILE__, __LINE__);
            std::ostringstream       msg;
            msg << "Couldn't convert component type: "
                << std::endl << "    "
                << m_ImageIO->GetComponentTypeAsString( m_ImageIO->GetComponentType() )
                << std::endl << "to one of: "
                << std::endl << "    " << TYPENAME( unsigned char )
                << std::endl << "    " << TYPENAME( char )
                << std::endl << "    " << TYPENAME( unsigned short )
                << std::endl << "    " << TYPENAME( short )
                << std::endl << "    " << TYPENAME( unsigned int )
                << std::endl << "    " << TYPENAME( int )
                << std::endl << "    " << TYPENAME( unsigned long )
                << std::endl << "    " << TYPENAME( long )
                << std::endl << "    " << TYPENAME( float )
                << std::endl << "    " << TYPENAME( double )
                << std::endl;
            e.SetDescription( msg.str().c_str() );
            e.SetLocation(ITK_LOCATION);
            throw e;
            return;
        }
#undef ITK_CONVERT_BUFFER_IF_BLOCK
    }
} //namespace ITK

#endif
back to top