[Insight-developers] Coordinate system semantics of ImageIOBase?

Luis Ibanez luis.ibanez at kitware.com
Mon Dec 29 12:10:22 EST 2008


Hi Steve,

Since you list is long and very detailed, please find my
comments below, interleaved with your text.


    Thanks


       Luis



----------------------------
Steve M. Robbins wrote:
> Hi,
> 
> I'd like to help MINC2 support move from Review into ITK proper.
> Mantis issue #6038 suggests there are bugs lurking, so I'm writing
> test cases for itkMINC2ImageIO.
> 


         Yes,
         Killing Bugs !
         Adding Tests !

         Go for it 1


> To do so successfully, I need to understand the semantics of
> its base class, ImageIOBase.  I'm getting tangled up in
> coordinate system issues.
> 
> For example, "GetDimensions" is documented as follows
> 
>   /** Set/Get the image dimensions in the x, y, z, etc. directions.
>    * GetDimensions() is typically used after reading the data; the
>    * SetDimensions() is used prior to writing the data. */
> 
> ... but I think the intent is that the dimension are NOT
> world-space x, y, and z, but rather column, row, and slice.
> Is that correct?
> 


   GetDimensions() and SetDimensions() relate to the number of Pixel
   along each of the directions of space.

   An image of 512x512x200 pixels will trigger calls such as:

       SetDimensions( 0, 512 );  // along X
       SetDimensions( 1, 512 );  // along Y
       SetDimensions( 2, 512 );  // along Z



> The method GetOrigin(int i) returns a double; I assume that
> GetOrigin(0), GetOrigin(1), GetOrigin(2) return the column,
> row, and slice, respectively, of the world-space origin.
> Is that correct?
> 

        Yes.

        If an image has origin at the point ( 0.345, 89.7,  235.4 )
        then the calls

        double ox = this->GetOrigin(0);
        double oy = this->GetOrigin(1);
        double oz = this->GetOrigin(2);

     will populate the variables as

        ox = 0.345
        oy = 89.7
        oz = 235.4

      This follows straight from Insight/Code/IO/itkImageIOBase.h
      lines 109-112:

      virtual double GetOrigin(unsigned int i) const
        {
        return m_Origin[i];
        }


> What should one assume about the world space coordinates?  I believe
> it is safe to assume a right-handed coordinate system.  Do we assume
> they should be LPS (as in DICOM)?  If so, do we convert incoming
> coordinates (MINC uses the RAS convention)?  I would assume not as
> I've read about folks using ITK for non-medical images like in
> astronomy.  But if no conversion is done, how does an application
> handle loading from multiple formats; e.g. DICOM and MINC?
> 


       This is a delicate point.

       The standard in ITK is to use LPS.

       If your data is not in LPS, then

        a)  DO NOT permute the pixels.

        b)  Simply store the actual direction cosines
            in the "Direction" matrix.

            Note that this are direction cosines *with respect*
            to an LPS orientation. Which means that if your data
            is in:

            1) LPS, then your direction matrix is

                   1  0  0
                   0  1  0
                   0  0  1

             2) RAS, then your direction matrix is

                  -1  0  0
                   0 -1  0
                   0  0  1


> The methods Get/SetDirection() deal with std::vector<double>; should
> we assume the vector length for each axis is the same as
> GetNumberOfDimensions()?  Is there any plan to address Mantis #5573?
> 


        Yes, you should assume that the size of the vector container
        is the same as the dimension of the space in which image the
        image was sampled.

        Yes, we have plans to address this bug, but we don't have
        concensus on how to do it.

        One of the proposals is to introduce a scene graph representation
        in ITK, where we have a tree structure with nodes that include
        transforms. Other suggestions are welcome.


> There is no guarantee documented that ReadImageInformation() is called
> before Read().  That effectively means that Read() has to call
> ReadImageInformation() itself to ensure the dimensions, origin, etc
> are set up, correct?
> 


      It is guarranteed that

                 ReadImageInformation()

      will be called *before*

                 Read()

      this is because ImageIO->ReadImageInformation() is called from
      the itk::ImageFileReader::GenerateOutputInformation() method in

      Insight/Code/IO/itkImageFileReader.txx : line 153


      while ImageIO->Read() is called from the
      the itk::ImageFileReader::GenerateData() method in

      Insight/Code/IO/itkImageFileReader.txx : lines 398 and 409.


      ProcessObjects call GenerateOutputInformation() before they
      call GenerateData(). This is explained in the ITK Software Guide

               http://www.itk.org/ItkSoftwareGuide.pdf

      Please read Chater Thirteen (no superstition here...),
      "How to Write a Filter", in PDF pages 789-804.



> The documentation for Read() is as follows
> 
>   /** Reads the data from disk into the memory buffer provided. */
>   virtual void Read(void* buffer) = 0;
> 
> Do we dump the bits into buffer exactly as they appear on disk with no
> type conversion?  We're supposed to pay attention to the
> ImageIORegion, right?  So the buffer is guaranteed to be large enough
> to hold the region last set with SetIORegion() ?
> 

        Yes, you must pay attention to (e.g. Respect) the ImageIORegion.

        In the Read() method, you should simply read the pixel
        data the way it is stored in the image file.

        Pixel type conversion will be managed for you by the
        ImageFileReader. Please see lines: 401-417 of
        Insight/Code/IO/itkImageFileReader.txx



> MINC files can have either per-slice or per-volume intensity
> transformation (i.e. slope and intercept); where should this be done?
> 


         Potential intensity transformations must be done in the
         Read() method. See for example: itkGDCMImageIO.txx:
         lines 575-738.

         Note that you must pay attention here to the pixel type,
         to make sure that the resulting intensities can be stored
         without losses.


> A MINC file has metadata that describes the image layout; e.g. whether
> it is XYZ (axial), XZY (coronal), etc.  When Read(buffer) is called,
> do we dump the bits into buffer as they exist on disk or could they
> get reordered into XYZ order?
> 


         The image layout (axial,coronal,sagittal) should be
         indicated in the Direction matrix.

         Your Read() method should simply dump pixels.



> Thanks for reading this far.  I realize I could probably glean most of
> what I'm after by looking at code for existing ImageIOBbase
> subclasses.  However, some of those could be buggy, so I'd like to get
> the consensus from the developers.  I will try to put all the answers
> I get into improving the documentation of ImageIOBase.
> 



          Please let us know if you have other questions,



> Regards,
> -Steve
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Insight-developers mailing list
> Insight-developers at itk.org
> http://www.itk.org/mailman/listinfo/insight-developers


More information about the Insight-developers mailing list