diff --git a/Modules/BasicImageProcessing/MiniApps/CMakeLists.txt b/Modules/BasicImageProcessing/MiniApps/CMakeLists.txt index 2df1db81db..c4254f4df3 100644 --- a/Modules/BasicImageProcessing/MiniApps/CMakeLists.txt +++ b/Modules/BasicImageProcessing/MiniApps/CMakeLists.txt @@ -1,98 +1,99 @@ option(BUILD_BasicImageProcessingMiniApps "Build commandline tools for Basic Image Processing" OFF) if(BUILD_BasicImageProcessingMiniApps OR MITK_BUILD_ALL_APPS) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of miniapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( basicImageProcessingMiniApps FileConverter^^MitkCore ImageTypeConverter^^MitkCore + RectifyImage^^MitkCore SingleImageArithmetic^^MitkCore_MitkBasicImageProcessing TwoImageArithmetic^^MitkCore_MitkBasicImageProcessing ImageAndValueArithmetic^^MitkCore_MitkBasicImageProcessing MaskRangeBasedFiltering^^MitkCore_MitkBasicImageProcessing MaskOutlierFiltering^^MitkCore_MitkBasicImageProcessing ResampleImage^^MitkCore_MitkBasicImageProcessing ResampleMask^^MitkCore_MitkBasicImageProcessing LaplacianOfGaussian^^MitkCore_MitkBasicImageProcessing MultiResolutionPyramid^^MitkCore_MitkBasicImageProcessing ForwardWavelet^^MitkCore_MitkBasicImageProcessing ) foreach(basicImageProcessingMiniApp ${basicImageProcessingMiniApps}) # extract mini app name and dependencies string(REPLACE "^^" "\\;" miniapp_info ${basicImageProcessingMiniApp}) set(miniapp_info_list ${miniapp_info}) list(GET miniapp_info_list 0 appname) list(GET miniapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitk_create_executable(${appname} DEPENDS MitkCore MitkCommandLine ${dependencies_list} PACKAGE_DEPENDS ITK CPP_FILES ${appname}.cpp ) # CPP_FILES ${appname}.cpp mitkCommandLineParser.cpp if(EXECUTABLE_IS_ENABLED) # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() get_target_property(_is_bundle ${EXECUTABLE_TARGET} MACOSX_BUNDLE) if(APPLE) if(_is_bundle) set(_target_locations ${EXECUTABLE_TARGET}.app) set(${_target_locations}_qt_plugins_install_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_bundle_dest_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_plugins_for_current_bundle ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_conf_install_dirs ${EXECUTABLE_TARGET}.app/Contents/Resources) install(TARGETS ${EXECUTABLE_TARGET} BUNDLE DESTINATION . ) else() if(NOT MACOSX_BUNDLE_NAMES) set(_qt_conf_install_dirs bin) set(_target_locations bin/${EXECUTABLE_TARGET}) set(${_target_locations}_qt_plugins_install_dir bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) else() foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) list(APPEND _qt_conf_install_dirs ${bundle_name}.app/Contents/Resources) set(_current_target_location ${bundle_name}.app/Contents/MacOS/${EXECUTABLE_TARGET}) list(APPEND _target_locations ${_current_target_location}) set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) message( " set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) ") install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/) endforeach() endif() endif() else() set(_target_locations bin/${EXECUTABLE_TARGET}${CMAKE_EXECUTABLE_SUFFIX}) set(${_target_locations}_qt_plugins_install_dir bin) set(_qt_conf_install_dirs bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) endif() endif() endforeach() # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() if(EXECUTABLE_IS_ENABLED) MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) endif() endif() diff --git a/Modules/BasicImageProcessing/MiniApps/RectifyImage.cpp b/Modules/BasicImageProcessing/MiniApps/RectifyImage.cpp new file mode 100644 index 0000000000..664c13adb0 --- /dev/null +++ b/Modules/BasicImageProcessing/MiniApps/RectifyImage.cpp @@ -0,0 +1,194 @@ +/*=================================================================== + +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 +#include +#include +#include + +template +void RectifyImage(mitk::Image::Pointer inputImage, mitk::Image::Pointer outputImage) +{ + mitk::ImagePixelReadAccessor pixelReadAccess(inputImage); + mitk::ImagePixelWriteAccessor pixelWriteAccess(outputImage); + + const auto DEPTH = static_cast(outputImage->GetDimension(2)); + const auto HEIGHT = static_cast(outputImage->GetDimension(1)); + const auto WIDTH = static_cast(outputImage->GetDimension(0)); + + auto geometry = outputImage->GetGeometry(); + itk::Index<3> index; + mitk::Point3D worldCoords; + + for (index[2] = 0; index[2] < DEPTH; ++index[2]) + { + for (index[1] = 0; index[1] < HEIGHT; ++index[1]) + { + for (index[0] = 0; index[0] < WIDTH; ++index[0]) + { + geometry->IndexToWorld(index, worldCoords); + pixelWriteAccess.SetPixelByIndex(index, pixelReadAccess.GetPixelByWorldCoordinates(worldCoords)); + } + } + } +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Rectify Image"); + parser.setCategory("Basic Image Processing"); + parser.setDescription("Resample image based on standard world to index transform"); + parser.setContributor("German Cancer Research Center (DKFZ)"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("input", "i", mitkCommandLineParser::Image, "Input Image", "Path to input image", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::Image, "Output Image", "Path to output image", us::Any(), false); + + auto parsedArgs = parser.parseArguments(argc, argv); + + if (2 != parsedArgs.size() || 0 == parsedArgs.count("input") || 0 == parsedArgs.count("output")) + return EXIT_FAILURE; + + auto inputImagePath = us::any_value_to_string(parsedArgs["input"]); + mitk::Image::Pointer inputImage; + + try + { + inputImage = dynamic_cast(mitk::IOUtil::Load(inputImagePath)[0].GetPointer()); + } + catch (const mitk::Exception&) + { + return EXIT_FAILURE; + } + + if (3 != inputImage->GetDimension()) + { + MITK_ERROR << "Only 3-d images are supported."; + return EXIT_FAILURE; + } + + auto inputGeometry = inputImage->GetGeometry(); + + mitk::Point3D minInputIndex; + mitk::FillVector3D(minInputIndex, 0.0, 0.0, 0.0); + + mitk::Point3D minInputIndexInWorld; + inputGeometry->IndexToWorld(minInputIndex, minInputIndexInWorld); + + mitk::Point3D maxInputIndex; + for (int i = 0; i < 3; ++i) + maxInputIndex[i] = inputGeometry->GetExtent(i) - 1; + + mitk::Point3D maxInputIndexInWorld; + inputGeometry->IndexToWorld(maxInputIndex, maxInputIndexInWorld); + + mitk::Point3D minOutputIndexInWorld; + for (int i = 0; i < 3; ++i) + minOutputIndexInWorld[i] = std::min(minInputIndexInWorld[i], maxInputIndexInWorld[i]); + + mitk::Point3D maxOutputIndexInWorld; + for (int i = 0; i < 3; ++i) + maxOutputIndexInWorld[i] = std::max(minInputIndexInWorld[i], maxInputIndexInWorld[i]); + + mitk::Vector3D spacing = inputGeometry->GetSpacing(); + auto transform = inputGeometry->GetIndexToWorldTransform()->Clone(); + auto matrix = transform->GetMatrix(); + + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + matrix[i][j] = std::abs(matrix[i][j]) / spacing[j]; + } + } + + transform->SetMatrix(matrix); + spacing = transform->TransformVector(spacing); + + mitk::Vector3D outputExtent = (maxOutputIndexInWorld - minOutputIndexInWorld + spacing); + for (int i = 0; i < 3; ++i) + outputExtent[i] /= spacing[i]; + + mitk::Point3D origin = minOutputIndexInWorld; + + mitk::Vector3D right; + mitk::FillVector3D(right, outputExtent[0], 0.0, 0.0); + + mitk::Vector3D down; + mitk::FillVector3D(down, 0.0, outputExtent[1], 0.0); + + auto planeGeometry = mitk::PlaneGeometry::New(); + planeGeometry->InitializeStandardPlane(right, down, &spacing); + planeGeometry->SetOrigin(origin); + planeGeometry->SetImageGeometry(true); + + auto slicedGeometry = mitk::SlicedGeometry3D::New(); + slicedGeometry->InitializeEvenlySpaced(planeGeometry, static_cast(outputExtent[2])); + + auto outputGeometry = mitk::ProportionalTimeGeometry::New(); + outputGeometry->SetTimeStepGeometry(slicedGeometry, 0); + + auto pixelType = inputImage->GetPixelType(); + + auto outputImage = mitk::Image::New(); + outputImage->Initialize(pixelType, *outputGeometry); + + try + { + switch (pixelType.GetComponentType()) + { + case itk::ImageIOBase::CHAR: + RectifyImage(inputImage, outputImage); + break; + + case itk::ImageIOBase::UCHAR: + RectifyImage(inputImage, outputImage); + break; + + case itk::ImageIOBase::SHORT: + RectifyImage(inputImage, outputImage); + break; + + case itk::ImageIOBase::USHORT: + RectifyImage(inputImage, outputImage); + break; + + default: + MITK_ERROR << "Pixel type is not supported."; + return EXIT_FAILURE; + } + } + catch (const mitk::Exception &e) + { + MITK_ERROR << e.GetDescription(); + return EXIT_FAILURE; + } + + auto outputImagePath = us::any_value_to_string(parsedArgs["output"]); + + try + { + mitk::IOUtil::Save(outputImage, outputImagePath); + } + catch (const mitk::Exception &) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp index a6f23cf779..643fd7dc94 100644 --- a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp +++ b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp @@ -1,220 +1,217 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkDICOMPMIO__cpp #define __mitkDICOMPMIO__cpp #include "mitkDICOMPMIO.h" #include "mitkDICOMPMIOMimeTypes.h" #include #include #include #include #include #include #include #include #include #include "mitkParamapPresetsParser.h" // us #include #include // model fit parameters #include "mitkModelFitConstants.h" namespace mitk { DICOMPMIO::DICOMPMIO() : AbstractFileIO(Image::GetStaticNameOfClass(), mitk::MitkDICOMPMIOMimeTypes::DICOMPM_MIMETYPE_NAME(), "DICOM PM") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel DICOMPMIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const Image *PMinput = static_cast(this->GetInput()); if (PMinput) { auto modalityProperty = PMinput->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str()); if (modalityProperty.IsNotNull()) { std::string modality = modalityProperty->GetValueAsString(); if (modality == "PM") { return Supported; } else return Unsupported; } else return Unsupported; } else return Unsupported; } void DICOMPMIO::Write() { ValidateOutputLocation(); mitk::LocaleSwitch localeSwitch("C"); LocalFile localFile(this); const std::string path = localFile.GetFileName(); auto PMinput = dynamic_cast(this->GetInput()); if (PMinput == nullptr) mitkThrow() << "Cannot write non-image data"; // Get DICOM information from referenced image vector> dcmDatasetsSourceImage; std::unique_ptr readFileFormat(new DcmFileFormat()); try { // Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the // property list mitk::StringLookupTableProperty::Pointer filesProp = dynamic_cast(PMinput->GetProperty("referenceFiles").GetPointer()); if (filesProp.IsNull()) { mitkThrow() << "No property with dicom file path."; return; } // returns a list of all referenced files StringLookupTable filesLut = filesProp->GetValue(); const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable(); for (auto it : lookUpTableMap) { const char *fileName = (it.second).c_str(); if (readFileFormat->loadFile(fileName, EXS_Unknown).good()) { std::unique_ptr readDCMDataset(readFileFormat->getAndRemoveDataset()); dcmDatasetsSourceImage.push_back(std::move(readDCMDataset)); } } } catch (const std::exception &e) { MITK_ERROR << "An error occurred while getting the dicom information: " << e.what() << endl; return; } mitk::Image *mitkPMImage = const_cast(PMinput); // Cast input PMinput to itk image ImageToItk::Pointer PMimageToItkFilter = ImageToItk::New(); PMimageToItkFilter->SetInput(mitkPMImage); // Cast from original itk type to dcmqi input itk image type typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(PMimageToItkFilter->GetOutput()); castFilter->Update(); PMitkInternalImageType::Pointer itkParamapImage = castFilter->GetOutput(); // Create PM meta information const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFilePM(); // Convert itk PM images to dicom image MITK_INFO << "Writing PM image: " << path << std::endl; try { // convert from unique to raw pointer vector rawVecDataset; for ( const auto& dcmDataSet : dcmDatasetsSourceImage ) { rawVecDataset.push_back( dcmDataSet.get() ); } std::unique_ptr PMconverter(new dcmqi::ParaMapConverter()); std::unique_ptr PMresult (PMconverter->itkimage2paramap(itkParamapImage, rawVecDataset, tmpMetaInfoFile)); // Write dicom file DcmFileFormat dcmFileFormat(PMresult.get()); std::string filePath = path.substr(0, path.find_last_of(".")); filePath = filePath + ".dcm"; dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit); } catch (const std::exception &e) { MITK_ERROR << "An error occurred during writing the DICOM Paramap: " << e.what() << endl; return; } } const std::string mitk::DICOMPMIO::CreateMetaDataJsonFilePM() const { const mitk::Image *PMimage = dynamic_cast(this->GetInput()); dcmqi::JSONParametricMapMetaInformationHandler PMhandler; // Get Metadata from modelFitConstants std::string parameterName; PMimage->GetPropertyList()->GetStringProperty(ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), parameterName); std::string modelName; PMimage->GetPropertyList()->GetStringProperty(ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), modelName); mitk::ParamapPresetsParser* pmPresets = mitk::ParamapPresetsParser::New(); // Here the mitkParamapPresets.xml file containing the Coding Schmeme Designator and Code Value are parsed and the relevant values extracted pmPresets->LoadPreset(); auto pmType_parameterName = pmPresets->GetType(parameterName); auto pmType_modelName = pmPresets->GetType(modelName); // Pass codes to Paramap Converter PMhandler.setDerivedPixelContrast("TCS"); PMhandler.setFrameLaterality("U"); PMhandler.setQuantityValueCode(pmType_parameterName.codeValue, pmType_parameterName.codeScheme, parameterName); PMhandler.setMeasurementMethodCode(pmType_modelName.codeValue, pmType_modelName.codeScheme, modelName); PMhandler.setMeasurementUnitsCode("/min", "UCUM", "/m"); PMhandler.setSeriesNumber("1"); PMhandler.setInstanceNumber("1"); PMhandler.setDerivationCode("129104", "DCM", "Perfusion image analysis"); PMhandler.setRealWorldValueSlope("1"); - - return PMhandler.getJSONOutputAsString(); - } std::vector DICOMPMIO::Read() { mitk::LocaleSwitch localeSwitch("C"); std::vector result; return result; } IFileIO::ConfidenceLevel DICOMPMIO::GetReaderConfidenceLevel() const { return Unsupported; } DICOMPMIO *DICOMPMIO::IOClone() const { return new DICOMPMIO(*this); } } // namespace #endif //__mitkDICOMPMIO__cpp