[Insight-users] IO factory creation problem for writing

Hans J. Johnson hjohnson at engineering . uiowa . edu
Tue, 18 Jun 2002 14:16:53 -0500


This is a multi-part message in MIME format.
--------------040601060808030202010207
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Hello,

PROBLEM #1
==========
There is a possible design problem with the way automatic 
XXXImageIOFactory  works for writing of images.  If you want the 
ImageFileWriter to determine the output image type automatically, the 
current implementation may fail. 

The problem is that when itk::ImageFileWriter tries to Write the image, 
it fails to find a suitable derived class of ImageIOBase.  The failure 
occurs because the write function calls CreateImageIO, which then calls 
CanReadFile to determine if it is an accepable derived ImageIOBase is 
found.  For this to work properly, the CreateImageIO function needs to 
call CanWriteFile from the class derived from ImageIOBase when doing the 
Write operation, and CanReadFile when doing the Read operation.

CALLTREE:
itk::PNGImageIO::CanReadFile,                                      
itk::ImageIOFactory::CreateImageIO,                                
itk::ImageFileWriter<itk::Image<unsigned char,(unsigned int)2> >::Write

I have attached a simple test to demonstrate this.  Notice that if you 
first create an image that can be read, then you can use the automatic 
mechanism to write the image.

Example outputs from enclosed program:

[hjohnson@robin FileWriteTest]$ FileWriteTest
Determining file output type automatically (should be PNG)
Problem found while writing image ./newtest.png
Unknown
itk::ERROR: ImageFileWriter(0x8107db0): No ImageIO set, or none could be 
created.

[hjohnson@robin FileWriteTest]$ FileWriteTest "BeExplicit"
Explicitly setting file output to PNG
File: ./newtest.png
 Image ./newtest.png Written To Disk

[hjohnson@robin FileWriteTest]$ FileWriteTest
Determining file output type automatically (should be PNG)
File: ./newtest.png
 Image ./newtest.png Written To Disk

[hjohnson@robin FileWriteTest]$ rm newtest.png
[hjohnson@robin FileWriteTest]$ FileWriteTest
Determining file output type automatically (should be PNG)
Problem found while writing image ./newtest.png
Unknown
itk::ERROR: ImageFileWriter(0x8107db0): No ImageIO set, or none could be 
created.

SOLUTION:
==========
Replace function called CreateImageIO with function called CreateImageIO 
Reader,  then duplicate
that function into one called CreateImageIOWriter that is exactly the 
same except that it uses CanWriteFile() call instead of CanReadFile() call.

This change only affects the following four files (including all of the 
examples):
./Code/IO/itkImageIOFactory.h
./Code/IO/itkImageIOFactory.cxx
./Code/IO/itkImageFileReader.txx
./Code/IO/itkImageFileWriter.txx

patches to implement the described changes are attached to this document.


PROBLEM #2
===========
PNGImageIO::CanWriteFile(const char * filename)
       add one line ---> m_FileName=filename;
SOLUTION
=========
Apply patches attached to this e-mail.


PROBLEM #3
===========
MetaImageIO::Write() not finished
SOLUTION
=========
Finish writing this.  I can do this if it has not yet been done.

Regards,
Hans J. Johnson
hans-johnson@uiowa.edu

PS:  Is there some other more efficient way to get changes committed to 
the CVS repository?   Is it possible to get CVS write access?

PPS:  I have written a file reader for Analyze v7.5 file format.  It has 
been tested for both reading and writing of images.  If anybody else 
needs this, just send me an e-mail.




--------------040601060808030202010207
Content-Type: text/plain;
 name="CMakeLists.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="CMakeLists.txt"

PROJECT(FileWriteTest)

# 
# Find ITK
#
FIND_PATH( ITK_BINARY_DIR itkConfigure.h )

# Load in the values from ITK if found
IF ( ITK_BINARY_DIR )
  LOAD_CACHE(${ITK_BINARY_DIR})
  INCLUDE (${ITK_SOURCE_DIR}/itkCMakeOptions.cmake)
ENDIF (ITK_BINARY_DIR )


INCLUDE_DIRECTORIES(
    ${ITK_SOURCE_DIR}/Code/BasicFilters
    ${ITK_SOURCE_DIR}/Code/Algorithms
    ${CMAKE_INSTALL_PREFIX}/include
)

LINK_DIRECTORIES(
${ITK_BINARY_DIR}/Code/Common
${ITK_BINARY_DIR}/Code/IO
    ${CMAKE_INSTALL_PREFIX}/lib
)

LINK_LIBRARIES (
VXLNumerics
ITKCommon
ITKIO
)


ADD_EXECUTABLE(FileWriteTest FileWriteTest )


--------------040601060808030202010207
Content-Type: text/plain;
 name="FileWriteTest.cxx"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="FileWriteTest.cxx"

/*=========================================================================
  Program:   FileWriteTest
  Module:    $RCSfile: FileWriteTest.cxx,v $
  Language:  C++
  \author Hans J. Johnson
//  This project is designed to test if you write out an image file 
//  based on the image extension alone.
=========================================================================*/


#include "itkImage.h"
#include "itkImageFileWriter.h"
#include "itkImageIOFactory.h"
#include "itkPNGImageIOFactory.h"
#include "itkPNGImageIO.h"

int main(int argc, char *argv[])
{
  //Allocate Images
  enum { ImageDimension = itk::Image<unsigned char,2>::ImageDimension };
  const itk::Image<unsigned char,2>::SizeType size = {{100,100}};
  const itk::Image<unsigned char,2>::IndexType index = {{0,0}};
  itk::Image<unsigned char,2>::RegionType region;
  region.SetSize( size );
  region.SetIndex( index );

  itk::Image<unsigned char,2>::Pointer img = itk::Image<unsigned char,2>::New();
  img->SetLargestPossibleRegion( region );
  img->SetBufferedRegion( region );
  img->SetRequestedRegion( region );
  img->Allocate();

  itk::PNGImageIOFactory::RegisterOneFactory();
  itk::ImageFileWriter<  itk::Image<unsigned char,2>  >::Pointer ImageWriterPointer =  itk::ImageFileWriter<  itk::Image<unsigned char,2>  > ::New();

    //Set the output filename
    char outputFileName[] = "./newtest.png";
    ImageWriterPointer->SetFileName(outputFileName);
    //Not Necessary if using file extensions to determine. ImageWriterPointer->SetImageIO( Analyzeio );
    //Make a png image writer
    itk::PNGImageIO::Pointer pngio;
    if(argc>1)
    {
      std::cout << "Explicitly setting file output to PNG"<< std::endl;
      pngio= itk::PNGImageIO::New();
      ImageWriterPointer->SetImageIO( pngio );
    }
    else
    {
      std::cout << "Determining file output type automatically (should be PNG)"<< std::endl;
    }

    //Attach input image to the writer.
    ImageWriterPointer->SetInput( img );
    //Determine file type and instantiate appropriate ImageIO class if not
    //explicitly stated with SetImageIO, then write to disk.
    try {
        ImageWriterPointer->Write();
        std::cout <<" Image "<< outputFileName << " Written To Disk" << std::endl;
    }
    catch ( itk::ExceptionObject & ex )
    {
        std::string message;
        message = "Problem found while writing image ";
        message += outputFileName;
        message += "\n";
        message += ex.GetLocation();
        message += "\n";
        message += ex.GetDescription();
        std::cerr << message << std::endl;
    }
  return 0;
}

--------------040601060808030202010207
Content-Type: text/plain;
 name="itkPNGImageIO.h.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="itkPNGImageIO.h.patch"

--- itkPNGImageIO.h.old	Tue Jun 18 14:04:42 2002
+++ itkPNGImageIO.h	Tue Jun 18 14:05:20 2002
@@ -48,7 +48,7 @@
 
   /** Determine the file type. Returns true if this ImageIO can read the
    * file specified. */
-  virtual bool CanReadFile(const char*);
+  virtual bool CanReadFile(const char * FileNameToCheck);
   
   /** Set the spacing and diemention information for the set filename. */
   virtual void ReadImageInformation();
@@ -68,7 +68,7 @@
 
   /** Determine the file type. Returns true if this ImageIO can read the
    * file specified. */
-  virtual bool CanWriteFile(const char*);
+  virtual bool CanWriteFile(const char* FileNameToCheck);
 
   /** Writes the spacing and dimentions of the image.
    * Assumes SetFileName has been called with a valid file name. */

--------------040601060808030202010207
Content-Type: text/plain;
 name="itkPNGImageIO.cxx.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="itkPNGImageIO.cxx.patch"

--- itkPNGImageIO.cxx.old	Tue Jun 18 14:04:48 2002
+++ itkPNGImageIO.cxx	Tue Jun 18 14:06:49 2002
@@ -41,9 +41,10 @@
 
 
 
-bool PNGImageIO::CanReadFile(const char* file) 
-{ 
-  PNGFileWrapper pngfp(file,"rb");
+bool PNGImageIO::CanReadFile(const char* FileNameToCheck)
+{
+    m_FileName=FileNameToCheck;
+  PNGFileWrapper pngfp(FileNameToCheck,"rb");
   FILE* fp = pngfp.m_FilePointer;
   if(!fp)
     {
@@ -367,8 +368,9 @@
   return;
 }
 
-bool PNGImageIO::CanWriteFile(const char*)
+bool PNGImageIO::CanWriteFile(const char * FileNameToCheck)
 {
+    m_FileName=FileNameToCheck;
   if ( m_FileName != "" &&
        m_FileName.find(".png") < m_FileName.length() )
     {

--------------040601060808030202010207
Content-Type: text/plain;
 name="itkImageIOFactory.h.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="itkImageIOFactory.h.patch"

--- itkImageIOFactory.h.old	Tue Jun 18 13:36:27 2002
+++ itkImageIOFactory.h	Tue Jun 18 13:36:27 2002
@@ -43,7 +43,8 @@
   typedef ::itk::ImageIOBase::Pointer ImageIOBasePointer;
 
   /** Create the appropriate ImageIO depending on the particulars of the file. */
-  static ImageIOBasePointer CreateImageIO(const char* path);
+  static ImageIOBasePointer CreateImageIOReader(const char* path);
+  static ImageIOBasePointer CreateImageIOWriter(const char* path);
 
 protected:
   ImageIOFactory();

--------------040601060808030202010207
Content-Type: text/plain;
 name="itkImageIOFactory.cxx.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="itkImageIOFactory.cxx.patch"

--- itkImageIOFactory.cxx.old	Tue Jun 18 13:36:27 2002
+++ itkImageIOFactory.cxx	Tue Jun 18 13:36:27 2002
@@ -19,11 +19,10 @@
 
 namespace itk
 {
-  
 
-  
+
 ImageIOBase::Pointer 
-ImageIOFactory::CreateImageIO(const char* path)
+ImageIOFactory::CreateImageIOReader(const char* path)
 {
   std::list<ImageIOBase::Pointer> possibleImageIO;
   std::list<LightObject::Pointer> allobjects = 
@@ -50,6 +49,38 @@
       {
       return *k;
       }
+    }
+  return 0;
+}
+
+ImageIOBase::Pointer 
+ImageIOFactory::CreateImageIOWriter(const char* path)
+{
+  std::list<ImageIOBase::Pointer> possibleImageIO;
+  std::list<LightObject::Pointer> allobjects = 
+                  ObjectFactoryBase::CreateAllInstance("itkImageIOBase");
+  for(std::list<LightObject::Pointer>::iterator i = allobjects.begin();
+      i != allobjects.end(); ++i)
+    {
+    ImageIOBase* io = dynamic_cast<ImageIOBase*>(i->GetPointer());
+    if(io)
+      {
+      possibleImageIO.push_back(io);
+      }
+    else
+      {
+      std::cerr << "Error ImageIO factory did not return an ImageIOBase: "
+                << (*i)->GetNameOfClass() 
+                << std::endl;
+      }
+    }
+  for(std::list<ImageIOBase::Pointer>::iterator k = possibleImageIO.begin();
+      k != possibleImageIO.end(); ++k)
+    { 
+    if((*k)->CanWriteFile(path))
+      {
+      return *k;
+      }
     }
   return 0;
 }

--------------040601060808030202010207
Content-Type: text/plain;
 name="itkImageFileWriter.txx.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="itkImageFileWriter.txx.patch"

--- itkImageFileWriter.txx.old	Tue Jun 18 13:36:27 2002
+++ itkImageFileWriter.txx	Tue Jun 18 13:36:27 2002
@@ -101,7 +101,7 @@
 
   if ( m_ImageIO == 0 ) //try creating via factory
     {
-    m_ImageIO = ImageIOFactory::CreateImageIO(m_FileName.c_str());
+    m_ImageIO = ImageIOFactory::CreateImageIOWriter(m_FileName.c_str());
     }
 
   if ( m_ImageIO == 0 )

--------------040601060808030202010207
Content-Type: text/plain;
 name="itkImageFileReader.txx.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="itkImageFileReader.txx.patch"

--- itkImageFileReader.txx.old	Tue Jun 18 13:36:27 2002
+++ itkImageFileReader.txx	Tue Jun 18 13:36:27 2002
@@ -82,7 +82,7 @@
   if ( m_ImageIO == 0 ) //try creating via factory
     {
     m_UserSpecified = false;
-    m_ImageIO = ImageIOFactory::CreateImageIO(m_FileName.c_str());
+    m_ImageIO = ImageIOFactory::CreateImageIOReader(m_FileName.c_str());
     }
   else
     {

--------------040601060808030202010207--