RTK icon indicating copy to clipboard operation
RTK copied to clipboard

ProjectionsReader support RawImageIO

Open ferdymercury opened this issue 3 years ago • 2 comments

Thanks a lot for this nice package. Here is a feature suggestion:

It would be useful if the class rtk::ProjectionsReader supported reading out raw data files, in the same way than itk::ImageSeriesReader does.

    using ImageIOType = itk::RawImageIO<unsigned short, 3>;
    using ImageType = itk::Image<unsigned short, 3>;
    ImageIOType::Pointer io = ImageIOType::New();
    io->SetDimensions(...);
    io->SetOrigin(...);
    io->SetSpacing(...);
    io->SetByteOrderToLittleEndian();
    using ReaderType = itk::ImageSeriesReader< ImageType >;
    ReaderType::Pointer reader = ReaderType::New();
    reader->SetImageIO( io );
    reader->SetFileNames( nameGenerator->GetFileNames() );
    reader->Update();

Currently, this cannot be done via rtk::ProjectionsReader, as it is missing the SetImageIO functionality.

ferdymercury avatar Feb 13 '22 21:02 ferdymercury

I tried this modification (copy-pasted from itkImageReader) and it seems at least to compile. Not sure though about its correctness as I have no experience with RTK.

diff --git a/include/rtkProjectionsReader.h b/include/rtkProjectionsReader.h
index 3665a0d7..3301eaf2 100644
--- a/include/rtkProjectionsReader.h
+++ b/include/rtkProjectionsReader.h
@@ -258,6 +258,16 @@ public:
   void
   GenerateOutputInformation() override;
 
+    /** Set/Get the ImageIO helper class. Often this is created via the object
+   * factory mechanism that determines whether a particular ImageIO can
+   * read a certain file. This method provides a way to get the ImageIO
+   * instance that is created. Or you can directly specify the ImageIO
+   * to use to read a particular file in case the factory mechanism will
+   * not work properly (e.g., unknown or unusual extension). */
+  void
+  SetImageIO(itk::ImageIOBase * imageIO);
+  //itkGetModifiableObjectMacro(ImageIO, itk::ImageIOBase); this is somehow not working
+
 protected:
   ProjectionsReader();
   ~ProjectionsReader() override = default;
@@ -313,6 +323,9 @@ private:
   /** Image IO object which is stored to create the pipe only when required */
   itk::ImageIOBase::Pointer m_ImageIO{ nullptr };
 
+  bool m_UserSpecifiedImageIO; // keep track whether the
+                               // ImageIO is user specified
+
   /** Copy of parameters for the mini-pipeline. Parameters are checked and
    * propagated when required in the GenerateOutputInformation. Refer to the
    * documentation of the corresponding filter for more information. */
diff --git a/include/rtkProjectionsReader.hxx b/include/rtkProjectionsReader.hxx
index 9ec6367a..b08dbccc 100644
--- a/include/rtkProjectionsReader.hxx
+++ b/include/rtkProjectionsReader.hxx
@@ -120,6 +120,9 @@ ProjectionsReader<TOutputImage>::ProjectionsReader()
   m_UpperBoundaryCropSize.Fill(0);
   m_ShrinkFactors.Fill(1);
   m_MedianRadius.Fill(0);
+
+  m_ImageIO = nullptr;
+  m_UserSpecifiedImageIO = false;
 }
 
 //--------------------------------------------------------------------
@@ -133,6 +136,7 @@ ProjectionsReader<TOutputImage>::PrintSelf(std::ostream & os, itk::Indent indent
     os << indent << "RawDataReader: " << m_RawDataReader->GetNameOfClass() << std::endl;
   if (m_RawToAttenuationFilter.GetPointer())
     os << indent << "RawToProjectionsFilter: " << m_RawToAttenuationFilter->GetNameOfClass() << std::endl;
+  os << indent << "UserSpecifiedImageIO flag: " << m_UserSpecifiedImageIO << std::endl;
 }
 
 //--------------------------------------------------------------------
@@ -148,11 +152,22 @@ ProjectionsReader<TOutputImage>::GenerateOutputInformation()
     rtk::RegisterIOFactories();
   firstTime = false;
 
-  itk::ImageIOBase::Pointer imageIO =
-    itk::ImageIOFactory::CreateImageIO(m_FileNames[0].c_str(), itk::ImageIOFactory::IOFileModeEnum::ReadMode);
+  itk::ImageIOBase::Pointer imageIO = nullptr;
+
+  if (m_UserSpecifiedImageIO == false) // try creating via factory
+  {
+    imageIO = itk::ImageIOFactory::CreateImageIO(m_FileNames[0].c_str(), itk::ImageIOFactory::IOFileModeEnum::ReadMode);
 
-  if (imageIO == nullptr)
-    itkGenericExceptionMacro(<< "Cannot create ImageIOFactory for file " << m_FileNames[0].c_str());
+    if (imageIO == nullptr)
+      itkGenericExceptionMacro(<< "Cannot create ImageIOFactory for file " << m_FileNames[0].c_str());
+  }
+  else
+  {
+    imageIO = m_ImageIO;
+    using ReaderType = itk::ImageSeriesReader<OutputImageType>;
+    typename ReaderType::Pointer reader = ReaderType::New();
+    m_RawDataReader = reader;
+  }
 
   if (m_ImageIO != imageIO)
   {
@@ -728,6 +743,19 @@ ProjectionsReader<TOutputImage>::PropagateI0(itk::ImageBase<OutputImageDimension
   // Pipeline connection for m_RawToAttenuationFilter is done after the call to this function
 }
 
+template <typename TOutputImage>
+void
+ProjectionsReader<TOutputImage>::SetImageIO(itk::ImageIOBase * imageIO)
+{
+  itkDebugMacro("setting ImageIO to " << imageIO);
+  if (this->m_ImageIO != imageIO)
+  {
+    this->m_ImageIO = imageIO;
+    this->Modified();
+  }
+  m_UserSpecifiedImageIO = true;
+}
+
 } // namespace rtk
 
 #endif

ferdymercury avatar Feb 13 '22 23:02 ferdymercury

Looks good to me. Can you create a pull request to test it?

SimonRit avatar Feb 14 '22 06:02 SimonRit