diff --git a/Core/Code/IO/mitkItkPictureWrite.cpp b/Core/Code/IO/mitkItkPictureWrite.cpp index 9cc888da5f..a796fc5c21 100644 --- a/Core/Code/IO/mitkItkPictureWrite.cpp +++ b/Core/Code/IO/mitkItkPictureWrite.cpp @@ -1,167 +1,164 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkPictureWrite.h" #include #include #include #include #include -template < typename TPixel, unsigned int VImageDimension > -void _mitkItkPictureWrite(itk::Image< TPixel, VImageDimension >* itkImage, const std::string& fileName) +/** Set the filenames to the specified writer in dependace on the number of images passed in */ +template< class WriterType > +void SetOutputNames( typename WriterType::Pointer writer, const std::string& baseFileName, unsigned int numberOfImages ) { - typedef itk::Image< TPixel, VImageDimension > TImageType; - - typedef itk::Image OutputImage3DType; - typedef itk::Image OutputImage2DType; - typename itk::RescaleIntensityImageFilter::Pointer rescaler = itk::RescaleIntensityImageFilter::New(); - rescaler->SetInput(itkImage); - rescaler->SetOutputMinimum(0); - rescaler->SetOutputMaximum(255); - itk::ImageSeriesWriter::Pointer writer = itk::ImageSeriesWriter::New(); - - // Fix initialize the numberOfSlices to one as default - // test if image has dimension >2 to set the number of slices according to the value of GetSize()[2] - int numberOfSlices = 1; - if( VImageDimension > 2 ) + if( numberOfImages > 1 ) { itk::NumericSeriesFileNames::Pointer numericFileNameWriter = itk::NumericSeriesFileNames::New(); - numberOfSlices = itkImage->GetLargestPossibleRegion().GetSize()[2]; - std::string finalFileName = fileName; - std::string::size_type pos = fileName.find_last_of(".",fileName.length()-1); + std::string finalFileName = baseFileName; + std::string::size_type pos = baseFileName.find_last_of(".",baseFileName.length()-1); if(pos==std::string::npos) finalFileName.append(".%d.png"); else finalFileName.insert(pos,".%d"); - numericFileNameWriter->SetEndIndex(numberOfSlices); + std::cout << "Filename: " << finalFileName << std::endl; + + numericFileNameWriter->SetEndIndex(numberOfImages); numericFileNameWriter->SetSeriesFormat(finalFileName.c_str()); numericFileNameWriter->Modified(); writer->SetFileNames(numericFileNameWriter->GetFileNames()); } // if the given image is an 2D-png image, do not use the numericFileNameWriter // to generate the name, since it alters the fileName given as parameter else { - writer->SetFileName(fileName); + writer->SetFileName( baseFileName.c_str() ); + } +} + +template < typename TPixel, unsigned int VImageDimension > +void _mitkItkPictureWrite(itk::Image< TPixel, VImageDimension >* itkImage, const std::string& fileName) +{ + typedef itk::Image< TPixel, VImageDimension > TImageType; + + typedef itk::Image UCharOutputImage3DType; + typedef itk::Image ShortOutputImage3DType; + typedef itk::Image OutputImage2D_8bitType; + typedef itk::Image OutputImage2D_16bitType; + + typedef itk::ImageSeriesWriter UCharWriterType; + typedef itk::ImageSeriesWriter ShortWriterType; + + typedef itk::RescaleIntensityImageFilter UCharRescalerFilterType; + typedef itk::RescaleIntensityImageFilter ShortRescalerFilterType; + + // get the size info + size_t inputTypeSize = sizeof(TPixel); + size_t supportedOutputMaxSize = 1; // default value 8bit + + // the PNG and TIFF formats can handle up-to 16-bit images + if( fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos ) + { + supportedOutputMaxSize = 2; } - writer->SetInput( rescaler->GetOutput() ); - writer->Update(); + // get the dimension info + unsigned int numberOfImages = 1; + if( itkImage->GetImageDimension() > 2) + numberOfImages = itkImage->GetLargestPossibleRegion().GetSize()[2]; + + typename ShortRescalerFilterType::Pointer sh_rescaler = ShortRescalerFilterType::New(); + sh_rescaler->SetInput( itkImage ); + sh_rescaler->SetOutputMinimum( 0 ); + sh_rescaler->SetOutputMaximum( 65535 ); + + typename UCharRescalerFilterType::Pointer rescaler = UCharRescalerFilterType::New(); + rescaler->SetInput( itkImage ); + rescaler->SetOutputMinimum( 0 ); + rescaler->SetOutputMaximum( 255 ); + + // + if( inputTypeSize == 1) + { + UCharWriterType::Pointer writer = UCharWriterType::New(); + SetOutputNames( writer, fileName, numberOfImages ); + writer->SetInput( rescaler->GetOutput() ); + writer->Update(); + } + // 16bit 16bit possible + else if ( inputTypeSize == supportedOutputMaxSize && supportedOutputMaxSize == 2 ) + { + ShortWriterType::Pointer writer = ShortWriterType::New(); + SetOutputNames( writer, fileName, numberOfImages ); + writer->SetInput( sh_rescaler->GetOutput() ); + writer->Update(); + } + // rescaling to maximum of supported format + else + { + if( supportedOutputMaxSize == 2) + { + typename ShortWriterType::Pointer writer = ShortWriterType::New(); + SetOutputNames( writer, fileName, numberOfImages ); + writer->SetInput(sh_rescaler->GetOutput() ); + writer->Update(); + } + else + { + typename UCharWriterType::Pointer writer = UCharWriterType::New(); + SetOutputNames( writer, fileName, numberOfImages ); + writer->SetInput( rescaler->GetOutput() ); + writer->Update(); + } + } } template < typename TPixel, unsigned int VImageDimension > void _mitkItkPictureWriteComposite(itk::Image< TPixel, VImageDimension >* itkImage, const std::string& fileName) { typedef itk::Image< TPixel, VImageDimension > TImageType; typedef itk::ImageFileWriter< TImageType > WriterType; typename WriterType::Pointer simpleWriter = WriterType::New(); simpleWriter->SetFileName( fileName ); simpleWriter->SetInput( itkImage ); try { simpleWriter->Update(); } catch( itk::ExceptionObject &e) { std::cerr << "Caught exception while writing image with composite type: \n" << e.what(); } } #define InstantiateAccessFunction__mitkItkPictureWrite(pixelType, dim) \ template MITK_CORE_EXPORT void _mitkItkPictureWrite(itk::Image*, const std::string&); #define InstantiateAccessFunction__mitkItkPictureWriteComposite(pixelType, dim) \ template MITK_CORE_EXPORT void _mitkItkPictureWriteComposite(itk::Image*, const std::string&); InstantiateAccessFunction(_mitkItkPictureWrite) InstantiateAccessFunctionForFixedPixelType( _mitkItkPictureWriteComposite, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ) -// typedef itk::Image, 2> itkImageRGBUC2; -// template <> void _mitkItkImageWrite, 2>(itkImageRGBUC2* itkImage, const std::string& fileName) -// { -// typedef itkImageRGBUC2 TImageType; -// -// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); -// writer->SetInput( itkImage ); -// writer->SetFileName( fileName.c_str() ); -// writer->Update(); -// }; -// -// typedef itk::Image, 3> itkImageRGBUC3; -// template <> void _mitkItkImageWrite, 3>(itkImageRGBUC3* itkImage, const std::string& fileName) -// { -// typedef itkImageRGBUC3 TImageType; -// -// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); -// writer->SetInput( itkImage ); -// writer->SetFileName( fileName.c_str() ); -// writer->Update(); -// }; -// -// typedef itk::Image, 3> itkImageDTIF3; -// template <> void _mitkItkImageWrite, 3>(itkImageDTIF3* itkImage, const std::string& fileName) -// { -// typedef itkImageDTIF3 TImageType; -// -// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); -// writer->SetInput( itkImage ); -// writer->SetFileName( fileName.c_str() ); -// writer->Update(); -// }; -// -// typedef itk::Image, 3> itkImageDTID3; -// template <> void _mitkItkImageWrite, 3>(itkImageDTID3* itkImage, const std::string& fileName) -// { -// typedef itkImageDTID3 TImageType; -// -// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); -// writer->SetInput( itkImage ); -// writer->SetFileName( fileName.c_str() ); -// writer->Update(); -// }; -// -// typedef itk::Image, 2> itkImageDTIF2; -// template <> void _mitkItkImageWrite, 2>(itkImageDTIF2* itkImage, const std::string& fileName) -// { -// typedef itkImageDTIF2 TImageType; -// -// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); -// writer->SetInput( itkImage ); -// writer->SetFileName( fileName.c_str() ); -// writer->Update(); -// }; -// -// typedef itk::Image, 2> itkImageDTID2; -// template <> void _mitkItkImageWrite, 2>(itkImageDTID2* itkImage, const std::string& fileName) -// { -// typedef itkImageDTID2 TImageType; -// -// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); -// writer->SetInput( itkImage ); -// writer->SetFileName( fileName.c_str() ); -// writer->Update(); -// }; - diff --git a/Core/Code/Testing/mitkImageWriterTest.cpp b/Core/Code/Testing/mitkImageWriterTest.cpp index b748b6fc81..52cef87147 100644 --- a/Core/Code/Testing/mitkImageWriterTest.cpp +++ b/Core/Code/Testing/mitkImageWriterTest.cpp @@ -1,254 +1,253 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageWriter.h" #include "mitkDataNodeFactory.h" #include "mitkTestingMacros.h" #include "mitkItkImageFileReader.h" #include "mitkException.h" +#include +#include "mitkIOUtil.h" + #include #include #ifdef WIN32 #include "process.h" #else #include #endif std::string AppendExtension(const std::string &filename, const char *extension) { std::string new_filename = filename; new_filename += extension; return new_filename; } -mitk::Image::Pointer LoadMyImage( std::string filename ) -{ - mitk::ItkImageFileReader::Pointer reader = mitk::ItkImageFileReader::New(); - - try - { - reader->SetFileName ( filename.c_str() ); - reader->Update(); - } - //catch( mitk::Exception e ) - catch(...) //todo: Alfred warum kann man die mitk::Exception hier nicht fangen? - { - MITK_TEST_FAILED_MSG(<< "Exception during image loading "); // << e.what() ); - } - - if ( reader->GetOutput() == NULL ) - itkGenericExceptionMacro("File "<GetOutput(); - return image; -} - bool CompareImageMetaData( mitk::Image::Pointer image, mitk::Image::Pointer reference) { // switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed if( image->GetDimension() != reference->GetDimension() ) { - MITK_ERROR << "The image dimension differs"; + MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF(" << reference->GetDimension() << ")"; return false; } // pixel type - if( image->GetPixelType() != reference->GetPixelType() ) + if( image->GetPixelType() != reference->GetPixelType() + && image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent() ) { MITK_ERROR << "Pixeltype differs "; return false; } return true; } /** * test for "ImageWriter". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ int mitkImageWriterTest(int argc , char* argv[]) { // always start with this! MITK_TEST_BEGIN("ImageWriter") - // let's create an object of our class - mitk::ImageWriter::Pointer myImageWriter = mitk::ImageWriter::New(); + // let's create an object of our class + mitk::ImageWriter::Pointer myImageWriter = mitk::ImageWriter::New(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(myImageWriter.IsNotNull(),"Testing instantiation") - // write your own tests here and use the macros from mitkTestingMacros.h !!! - // do not write to std::cout and do not return from this function yourself! - - // load image + // write your own tests here and use the macros from mitkTestingMacros.h !!! + // do not write to std::cout and do not return from this function yourself! - MITK_TEST_CONDITION_REQUIRED(argc != 0, "File to load has been specified"); + // load image + MITK_TEST_CONDITION_REQUIRED(argc > 1, "File to load has been specified"); mitk::Image::Pointer image = NULL; - mitk::DataNodeFactory::Pointer factory = mitk::DataNodeFactory::New(); try { MITK_TEST_OUTPUT(<< "Loading file: " << argv[1]); - factory->SetFileName( argv[1] ); - factory->Update(); - MITK_TEST_CONDITION_REQUIRED(factory->GetNumberOfOutputs() > 0, "file loaded"); - - mitk::DataNode::Pointer node = factory->GetOutput( 0 ); - image = dynamic_cast(node->GetData()); - if(image.IsNull()) - { - std::cout<<"file "<< argv[1]<< "is not an image - test will not be applied."<SetInput(image); MITK_TEST_CONDITION_REQUIRED(myImageWriter->GetInput()==image,"test Set/GetInput()"); myImageWriter->SetFileName(filename); MITK_TEST_CONDITION_REQUIRED(!strcmp(myImageWriter->GetFileName(),filename.c_str()),"test Set/GetFileName()"); myImageWriter->SetFilePrefix("pref"); MITK_TEST_CONDITION_REQUIRED(!strcmp(myImageWriter->GetFilePrefix(),"pref"),"test Set/GetFilePrefix()"); myImageWriter->SetFilePattern("pattern"); MITK_TEST_CONDITION_REQUIRED(!strcmp(myImageWriter->GetFilePattern(),"pattern"),"test Set/GetFilePattern()"); // write ITK .mhd image (2D and 3D only) if( image->GetDimension() <= 3 ) { try { myImageWriter->SetExtension(".mhd"); myImageWriter->Update(); - mitk::Image::Pointer compareImage = LoadMyImage(AppendExtension(filename, ".mhd").c_str()); + mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage( AppendExtension(filename, ".mhd").c_str() ); MITK_TEST_CONDITION_REQUIRED( compareImage.IsNotNull(), "Image stored in MHD format was succesfully loaded again! "); std::string rawExtension = ".raw"; std::fstream rawPartIn; rawPartIn.open(AppendExtension(filename, ".raw").c_str()); if( !rawPartIn.is_open() ) { rawExtension = ".zraw"; rawPartIn.open(AppendExtension(filename, ".zraw").c_str()); } MITK_TEST_CONDITION_REQUIRED(rawPartIn.is_open(),"Write .raw file"); rawPartIn.close(); // delete remove(AppendExtension(filename, ".mhd").c_str()); remove(AppendExtension(filename, rawExtension.c_str()).c_str()); } catch (...) { MITK_TEST_FAILED_MSG(<< "Exception during .mhd file writing"); } } //testing more component image writing as nrrd files try { myImageWriter->SetExtension(".nrrd"); myImageWriter->Update(); std::fstream fin; - mitk::Image::Pointer compareImage = LoadMyImage(AppendExtension(filename, ".nrrd").c_str()); + mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage(AppendExtension(filename, ".nrrd").c_str()); MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored in NRRD format was succesfully loaded again"); fin.close(); remove(AppendExtension(filename, ".nrrd").c_str()); } catch(...) { MITK_TEST_FAILED_MSG(<< "Exception during .nrrd file writing"); } - // testing image writing as png files - // test only for 2D images since the PNG is using a series writer in case a 3D image - // should be saved -> the output name comparison would fail and also the use case - // is very uncommon - // write ITK .mhd image (2D and 3D only) - if( image->GetDimension() == 2 ) + + mitk::Image::Pointer singleSliceImage = NULL; + if( image->GetDimension() == 3 ) + { + mitk::ExtractSliceFilter::Pointer extractFilter = mitk::ExtractSliceFilter::New(); + extractFilter->SetInput( image ); + extractFilter->SetWorldGeometry( image->GetSlicedGeometry()->GetGeometry2D(0) ); + + extractFilter->Update(); + singleSliceImage = extractFilter->GetOutput(); + + // test 3D writing in format supporting only 2D + myImageWriter->SetExtension(".png"); + myImageWriter->Update(); + + // test images + unsigned int foundImagesCount = 0; + for( unsigned int i=0; i< image->GetDimension(2); i++) + { + std::stringstream series_filenames; + series_filenames << filename << "." << i+1 << ".png"; + mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage( series_filenames.str() ); + if( compareImage.IsNotNull() ) + { + foundImagesCount++; + MITK_TEST_CONDITION(CompareImageMetaData( singleSliceImage, compareImage ), "Image meta data unchanged after writing and loading again. "); + } + remove( series_filenames.str().c_str() ); + } + MITK_TEST_CONDITION( foundImagesCount == image->GetDimension(2), "All 2D-Slices of a 3D image were stored correctly as PNGs."); + } + else if( image->GetDimension() == 2 ) + { + singleSliceImage = image; + } + + // test 2D writing + if( singleSliceImage.IsNotNull() ) { try { + myImageWriter->SetInput( singleSliceImage ); myImageWriter->SetExtension(".png"); myImageWriter->Update(); - std::fstream fin; - mitk::Image::Pointer compareImage = LoadMyImage(AppendExtension(filename, ".png").c_str()); - MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored in PNG format was succesfully loaded again"); - MITK_TEST_CONDITION_REQUIRED( CompareImageMetaData(image, compareImage ), "Image meta data unchanged after writing and loading again. "); - fin.close(); + mitk::Image::Pointer compareImage = mitk::IOUtil::LoadImage( AppendExtension(filename, ".png").c_str()); + MITK_TEST_CONDITION_REQUIRED( compareImage.IsNotNull(), "Image stored in PNG format was succesfully loaded again"); + + MITK_TEST_CONDITION_REQUIRED( CompareImageMetaData(singleSliceImage, compareImage ), "Image meta data unchanged after writing and loading again. "); remove(AppendExtension(filename, ".png").c_str()); } catch(itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "Exception during .png file writing: " << e.what() ); } + } // test for exception handling try { MITK_TEST_FOR_EXCEPTION_BEGIN(itk::ExceptionObject) myImageWriter->SetInput(image); myImageWriter->SetFileName("/usr/bin"); myImageWriter->Update(); MITK_TEST_FOR_EXCEPTION_END(itk::ExceptionObject) } catch(...) { //this means that a wrong exception (i.e. no itk:Exception) has been thrown MITK_TEST_FAILED_MSG(<< "Wrong exception (i.e. no itk:Exception) caught during write"); } // always end with this! MITK_TEST_END(); }