Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is this program still on maintainance, does it support to transfer itk image to svs format #29

Open
zhusihan-python opened this issue Dec 1, 2022 · 9 comments

Comments

@zhusihan-python
Copy link

Question
is this program still on maintainance, does it support to transfer itk image to svs format?
or is there any other way to write the itk::TileMergeImageFilter->GetOutput() to svs format

@zhusihan-python
Copy link
Author

the ITKIOOpenSlide does not support write to svs format, this is my test result

Parameters:

inputImage = 'E:/data/CMU-1.ndpi'
outputImage = 'E:/data/CMU-1.svs'
shouldFail = false
compress = false
approximateStreaming = false
stream = 0
level = 0
associatedImage = ''
downsample = 0
Error:
itk::ImageFileWriterException (000000DA126FF9C0)
Location: "unknown"
File: E:\projects\ITK\Modules\IO\ImageBase\include\itkImageFileWriter.hxx
Line: 118
Description:  Could not create IO object for writing file E:/data/CMU-1.svs
  Tried to create one of the following:
    JPEGImageIO
    PNGImageIO
    TIFFImageIO
  You probably failed to set a file suffix, or
    set the suffix to an unsupported type.

test code:

#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>

#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkOpenSlideImageIO.h"
#include "itkImage.h"
#include "itkRGBAPixel.h"
#include "itkMetaDataObject.h"

#include "itkOpenSlideImageIOFactory.h"
#include "itkImageIOFactory.h"

bool ParseValue(const char *p_cValue, std::string &strCommand, std::string &strValue) {
  strCommand.clear();
  strValue.clear();

  if (p_cValue == NULL)
    return false;

  const char *p = strchr(p_cValue, '=');

  if (p == NULL) {
    strCommand = p_cValue;
    return true;
  }

  // is p the beginning or end of the string?
  if (p == p_cValue)
    return false;

  strCommand.assign(p_cValue, (p-p_cValue));
  strValue = p+1;

  return true;
}
int main(int argc, char **argv)
{
  using PixelType = itk::RGBAPixel<unsigned char>;
  using ImageType = itk::Image<PixelType, 2>;
  using ReaderIOType = itk::OpenSlideImageIO;
  using ReaderType = itk::ImageFileReader<ImageType>;
  using WriterType = itk::ImageFileWriter<ImageType>;

  if( argc < 3 ) {
    std::cerr << "Usage: " << argv[0] << " inputImage outputImage [command1 command2 ...]\n";
    return EXIT_FAILURE;
  }

  //const char * const p_cArg0 = argv[0];
  const char * const p_cInputImage = argv[1];
  const char * const p_cOutputImage = argv[2];

  argc -= 3;
  argv += 3;

  bool bShouldFail = false;
  bool bUseCompression = false;
  bool bApproximateStreaming = false;
  unsigned int uiNumStreams = 0; // 0 means no streaming
  int iLevel = 0;
  std::string strAssociatedImageName;
  double dDownsampleFactor = 0.0; // 0 means no down sample

  for (int i = 0; i < argc; ++i) {
    std::string strCommand;
    std::string strValue;

    if (!ParseValue(argv[i], strCommand, strValue)) {
      std::cerr << "Error: Could not parse value '" << argv[i] << "'." << std::endl;
      return EXIT_FAILURE;
    }

    if (strCommand == "shouldFail") {
      bShouldFail = true;
    }
    else if (strCommand == "compress") {
      bUseCompression = true;
    }
    else if (strCommand == "approximateStreaming") {
      bApproximateStreaming = true;
    }
    else if (strCommand == "level") {
      if (strValue.empty()) {
        std::cerr << "Error: Expected level parameter." << std::endl;
        return EXIT_FAILURE;
      }

      char *p = NULL;
      iLevel = strtol(strValue.c_str(), &p, 10);
      if (*p != '\0') {
        std::cerr << "Error: Could not parse level '" << strValue << "'." << std::endl;
        return EXIT_FAILURE;
      }
    }
    else if (strCommand == "associatedImage") {
      if (strValue.empty()) {
        std::cerr << "Error: Expected associated image name." << std::endl;
        return EXIT_FAILURE;
      }

      strAssociatedImageName = strValue;
    }
    else if (strCommand == "downsample") {
      if (strValue.empty()) {
        std::cerr << "Error: Expected downsample factor." << std::endl;
        return EXIT_FAILURE;
      }

      char *p = NULL;
      dDownsampleFactor = strtod(strValue.c_str(), &p);
      if (*p != '\0') {
        std::cerr << "Error: Could not parse downsample factor '" << strValue << "'." << std::endl;
        return EXIT_FAILURE;
      }
    }
    else if (strCommand == "stream") {
      if (strValue.empty()) {
        std::cerr << "Error: Expected number of streams." << std::endl;
        return EXIT_FAILURE;
      }

      char *p = NULL;
      uiNumStreams = strtoul(strValue.c_str(), &p, 10);
      if (*p != '\0') {
        std::cerr << "Error: Could not parse number of streams '" << strValue << "'." << std::endl;
        return EXIT_FAILURE;
      }
    }
    else {
      std::cout << "Error: Unknown command '" << argv[i] << "'." << std::endl;
      return EXIT_FAILURE;
    }
  }

  const int iFailCode = (bShouldFail ? EXIT_SUCCESS : EXIT_FAILURE);
  const int iSuccessCode = (iFailCode == EXIT_FAILURE ? EXIT_SUCCESS : EXIT_FAILURE);

  std::cout << "Parameters:\n" << std::endl;
  std::cout << "inputImage = '" << p_cInputImage << '\'' << std::endl;
  std::cout << "outputImage = '" << p_cOutputImage << '\'' << std::endl;
  std::cout << "shouldFail = " << std::boolalpha << bShouldFail << std::endl;
  std::cout << "compress = " << std::boolalpha << bUseCompression << std::endl;
  std::cout << "approximateStreaming = " << std::boolalpha << bApproximateStreaming << std::endl;
  std::cout << "stream = " << uiNumStreams << std::endl;
  std::cout << "level = " << iLevel << std::endl;
  std::cout << "associatedImage = '" << strAssociatedImageName << '\'' << std::endl;
  std::cout << "downsample = " << dDownsampleFactor << std::endl;

  ReaderIOType::Pointer p_clImageIO = ReaderIOType::New();
  ReaderType::Pointer p_clReader = ReaderType::New();
  WriterType::Pointer p_clWriter = WriterType::New();

  p_clImageIO->SetFileName(p_cInputImage);

  p_clReader->SetImageIO(p_clImageIO);
  p_clReader->SetFileName(p_cInputImage);

  p_clWriter->SetInput(p_clReader->GetOutput());
  p_clWriter->SetFileName(p_cOutputImage);

  try {
    p_clImageIO->ReadImageInformation();
  }
  catch (itk::ExceptionObject &e) {
    std::cerr << "Error: " << e << std::endl;
    return iFailCode;
  }

  if (strAssociatedImageName.size() > 0)
    p_clImageIO->SetAssociatedImageName(strAssociatedImageName);
  else
    p_clImageIO->SetLevel(iLevel);

  if (dDownsampleFactor > 0.0 && !p_clImageIO->SetLevelForDownsampleFactor(dDownsampleFactor))
    return iFailCode;

  if (uiNumStreams > 0) {
    if (!p_clImageIO->CanStreamRead())
      return iFailCode;

    p_clImageIO->UseStreamedReadingOn();
    p_clImageIO->SetApproximateStreaming(bApproximateStreaming);

    itk::ImageIOBase::Pointer p_clWriterIO = itk::ImageIOFactory::CreateImageIO(p_cOutputImage, itk::IOFileModeEnum::WriteMode);
    if (!p_clWriterIO) {
      std::cerr << "Error: Could not create ImageIO for output image '" << p_cOutputImage << "'." << std::endl;
      return iFailCode;
    }

    if (bUseCompression)
      std::cout << "Warning: Compression may disable streaming." << std::endl;

    p_clWriterIO->UseStreamedWritingOn();

    p_clWriter->SetImageIO(p_clWriterIO);
    p_clWriter->SetNumberOfStreamDivisions(uiNumStreams);
  }

  // XXX: Just so you know, this might disable streaming
  p_clWriter->SetUseCompression(bUseCompression);

  try {
    p_clWriter->Update();
  }
  catch (itk::ExceptionObject &e) {
    std::cerr << "Error: " << e << std::endl;
    return iFailCode;
  }

  // Use this to compress output images when updating tests
  //CompressImageFile(p_cOutputImage);

  return iSuccessCode;
}

@nslay
Copy link
Collaborator

nslay commented Dec 1, 2022

Since openslide itself cannot write slide format images, neither can ITKIOOpenSlide. You're unfortunately limited to only reading slide images.

@nslay
Copy link
Collaborator

nslay commented Dec 1, 2022

Aha! I remembered coming across the C/C++ library libvips that can write slide images. There's also BioFormats but that's Java. Maybe I could write an ITKIOVIPS or something like that! But I'm already strapped for time and often not even working on pathology projects (I'm working mostly with radiology images these days).

@zhusihan-python
Copy link
Author

Aha! I remembered coming across the C/C++ library libvips that can write slide images. There's also BioFormats but that's Java. Maybe I could write an ITKIOVIPS or something like that! But I'm already strapped for time and often not even working on pathology projects (I'm working mostly with radiology images these days).

that will be great if there are iovips or iosvs! I have found a repo use libvips to generate svs, but I don't know how to combine it with itk or itk montage.

@nslay
Copy link
Collaborator

nslay commented Dec 1, 2022

Unfortunately, it doesn't look like libvips can write .svs files either.
libvips/pyvips#69

I guess the workaround is to write a pyramidal tiff image. See the issue with code for the workaround.

@zhusihan-python
Copy link
Author

Unfortunately, it doesn't look like libvips can write .svs files either. libvips/pyvips#69

I guess the workaround is to write a pyramidal tiff image. See the issue with code for the workaround.

yes sir, vips can not write svs directly. but
this tiny project can convert almost all images format supported by vips to svs in around 500 line cpp code.

@zhusihan-python
Copy link
Author

Aha! I remembered coming across the C/C++ library libvips that can write slide images. There's also BioFormats but that's Java. Maybe I could write an ITKIOVIPS or something like that! But I'm already strapped for time and often not even working on pathology projects (I'm working mostly with radiology images these days).

i build SCIFIO ImageIO successfully, it did not support write svs format surprisingly
image

@nslay
Copy link
Collaborator

nslay commented Dec 4, 2022

I don't see the SCIFIOImageIO listed. You could try to force the use of SCIFIImageIO by creating SCIFIImageIO (SCIFIOImageIO::New()) and feeding it to itk::ImageFileWriter::SetImageIO().

@zhusihan-python
Copy link
Author

writeSCIFIO is not set on i guess in the RunTest so the SetImageIO did not trigger
image
then i enable write-scifio by running
SCIFIOTestDriver.exe itkSCIFIOImageIOTest e:/data/CMU-1.ndpi e:/data/CMU-1_trans.svs --write-scifio
get some errors in java code, still can not recognize svs format

reader->GetUseStreaming(): 1
done checking streaming usage

itk::ExceptionObject (0000002173F6EE08)
Location: "unknown"
File: F:\build_io_scifio\src\itkSCIFIOImageIO.cxx
Line: 180
Description: ITK ERROR: SCIFIOImageIO(000001D4EAB1D040): SCIFIOImageIO exited abnormally. Exception in thread "main" loci.formats.UnknownFormatException: Unknown file format: e:/data/CMU-1_trans.svs
        at loci.formats.ImageWriter.getWriter(ImageWriter.java:158)
        at loci.formats.ImageWriter.setId(ImageWriter.java:462)
        at io.scif.itk.SCIFIOITKBridge.write(SCIFIOITKBridge.java:555)
        at io.scif.itk.SCIFIOITKBridge.executeCommand(SCIFIOITKBridge.java:203)
        at io.scif.itk.SCIFIOITKBridge.executeCommand(SCIFIOITKBridge.java:115)
        at io.scif.itk.SCIFIOITKBridge.waitForInput(SCIFIOITKBridge.java:90)
        at io.scif.itk.SCIFIOITKBridge.executeCommand(SCIFIOITKBridge.java:170)
        at io.scif.itk.SCIFIOITKBridge.main(SCIFIOITKBridge.java:774)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants