diff --git a/Modules/MatchPointRegistration/CMakeLists.txt b/Modules/MatchPointRegistration/CMakeLists.txt index 4be5284265..50eae7426e 100644 --- a/Modules/MatchPointRegistration/CMakeLists.txt +++ b/Modules/MatchPointRegistration/CMakeLists.txt @@ -1,29 +1,30 @@ mitk_create_module( INCLUDE_DIRS PUBLIC algorithms PRIVATE src/Helper src/Rendering DEPENDS MitkCore MitkSceneSerializationBase MitkMultilabel PACKAGE_DEPENDS PUBLIC MatchPoint PRIVATE VTK|ImagingGeneral+ImagingHybrid ) +if(BUILD_TESTING) + add_subdirectory(Testing) +endif() +add_subdirectory(autoload/IO) +add_subdirectory(cmdapps) + if(TARGET ${MODULE_TARGET}) set(ALG_PROFILE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/algorithms) include(${MatchPoint_SOURCE_DIR}/CMake/mapFunctionCreateAlgorithmProfile.cmake) file(GLOB ALG_PROFILE_FILES LIST_DIRECTORIES false RELATIVE ${ALG_PROFILE_DIR} "${ALG_PROFILE_DIR}/*.profile") foreach(profile_file ${ALG_PROFILE_FILES}) get_filename_component(profile_name ${profile_file} NAME_WE) message(STATUS "Generate MDRA profile ${profile_name} (from ${profile_file})") CREATE_ALGORITHM_PROFILE(${profile_name} ${ALG_PROFILE_DIR}/${profile_file}) endforeach() - add_subdirectory(autoload/IO) add_subdirectory(deployment) - if(BUILD_TESTING) - add_subdirectory(Testing) - endif() - add_subdirectory(cmdapps) endif() diff --git a/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidHeadNeckRegistrationAlgorithm.h b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidHeadNeckRegistrationAlgorithm.h new file mode 100644 index 0000000000..64661706e1 --- /dev/null +++ b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidHeadNeckRegistrationAlgorithm.h @@ -0,0 +1,28 @@ +/*============================================================================ + +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 mitkMultiModalRigidHeadNeckRegistrationAlgorithm_h +#define mitkMultiModalRigidHeadNeckRegistrationAlgorithm_h + +#include +#include +#include + +#include + +namespace mitk +{ + template + using MultiModalRigidHeadNeckRegistrationAlgorithm = typename map::algorithm::boxed::MultiModalRigidHeadNeckRegistrationAlgorithm; +} + +#endif diff --git a/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidHeadNeckRegistrationAlgorithm.profile b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidHeadNeckRegistrationAlgorithm.profile new file mode 100644 index 0000000000..eccff83a0d --- /dev/null +++ b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidHeadNeckRegistrationAlgorithm.profile @@ -0,0 +1,20 @@ +SET(ALGORITHM_PROFILE_UID_Namespace "org.mitk") +SET(ALGORITHM_PROFILE_UID_Name "MultiModal.rigid.Head2HeadNeck") +SET(ALGORITHM_PROFILE_UID_Version "1.0.0") + +SET(ALGORITHM_PROFILE_Description "Algorithm is used for special cases where a head image and a head neck image (assumed to be the larger image in z direction) should be registered. It skips the lower part of the head neck image if a preinitialization (via geometric center or centroid) is used, to establish a better initialization. By default initializes via geometric centers.") +SET(ALGORITHM_PROFILE_Contact "Ralf Floca\; mitk-users@lists.sourceforge.net") + +SET(ALGORITHM_PROFILE_DataType "Image") +SET(ALGORITHM_PROFILE_ResolutionStyle "3 (multi res)") +SET(ALGORITHM_PROFILE_DimMoving "3") +SET(ALGORITHM_PROFILE_ModalityMoving "MR" "any") +SET(ALGORITHM_PROFILE_DimTarget "3") +SET(ALGORITHM_PROFILE_ModalityTarget "MR" "any") +SET(ALGORITHM_PROFILE_Subject "any") +SET(ALGORITHM_PROFILE_Object "Head" "Head/Neck") +SET(ALGORITHM_PROFILE_TransformModel "rigid") +SET(ALGORITHM_PROFILE_Metric "Mattes mutual information") +SET(ALGORITHM_PROFILE_TransformDomain "global") +SET(ALGORITHM_PROFILE_Optimization "Regular Step Gradient Descent") +SET(ALGORITHM_PROFILE_Keywords "basic" "pre initialization" "Head/Neck" "HeadNeck" "multimodal" "rigid") diff --git a/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.h b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.h new file mode 100644 index 0000000000..71674fe153 --- /dev/null +++ b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.h @@ -0,0 +1,28 @@ +/*============================================================================ + +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 mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm_h +#define mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm_h + +#include +#include +#include + +#include + +namespace mitk +{ + template + using MultiModalRigidSlabbedHeadRegistrationAlgorithm = typename map::algorithm::boxed::MultiModalRigidSlabbedHeadRegistrationAlgorithm; +} + +#endif diff --git a/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.profile b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.profile new file mode 100644 index 0000000000..4f455fdf68 --- /dev/null +++ b/Modules/MatchPointRegistration/algorithms/mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.profile @@ -0,0 +1,20 @@ +SET(ALGORITHM_PROFILE_UID_Namespace "org.mitk") +SET(ALGORITHM_PROFILE_UID_Name "MultiModal.rigid.slabbedHead") +SET(ALGORITHM_PROFILE_UID_Version "1.0.0") + +SET(ALGORITHM_PROFILE_Description "Algorithm is used to solve a special case of head registration problems. It is configured to register the slabbed MRI head data (thus data where only a part of the head is visible) onto whole Head images (e.g.CT planning data). Differenz to the default rigid algorithm is that this algorithms is very defensive with rotating out of the slice plan (so roll or pitch). Uses 3 Resolution levels. By default initializes via image centers.") +SET(ALGORITHM_PROFILE_Contact "Ralf Floca\; mitk-users@lists.sourceforge.net") + +SET(ALGORITHM_PROFILE_DataType "Image") +SET(ALGORITHM_PROFILE_ResolutionStyle "3 (multi res)") +SET(ALGORITHM_PROFILE_DimMoving "3") +SET(ALGORITHM_PROFILE_ModalityMoving "MR" "any") +SET(ALGORITHM_PROFILE_DimTarget "3") +SET(ALGORITHM_PROFILE_ModalityTarget "MR" "any") +SET(ALGORITHM_PROFILE_Subject "any") +SET(ALGORITHM_PROFILE_Object "Head") +SET(ALGORITHM_PROFILE_TransformModel "rigid") +SET(ALGORITHM_PROFILE_Metric "Mattes mutual information") +SET(ALGORITHM_PROFILE_TransformDomain "global") +SET(ALGORITHM_PROFILE_Optimization "Regular Step Gradient Descent") +SET(ALGORITHM_PROFILE_Keywords "basic" "pre initialization" "Head" "slabbed" "partial" "multimodal" "rigid") diff --git a/Modules/MatchPointRegistration/cmdapps/CMakeLists.txt b/Modules/MatchPointRegistration/cmdapps/CMakeLists.txt index 272923372e..8fbb245098 100644 --- a/Modules/MatchPointRegistration/cmdapps/CMakeLists.txt +++ b/Modules/MatchPointRegistration/cmdapps/CMakeLists.txt @@ -1,32 +1,34 @@ option(BUILD_MatchPointCmdApps "Build commandline tools for the MatchPoint module" OFF) if(BUILD_MatchPointCmdApps OR MITK_BUILD_ALL_APPS) # needed include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of CmdApps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( cmdapps StitchImagesMiniApp^^ + MatchImageMiniApp^^ + MapImageMiniApp^^ ) foreach(cmdapp ${cmdapps}) # extract cmd name and dependencies string(REPLACE "^^" "\\;" cmdapp_info ${cmdapp}) set(cmdapp_info_list ${cmdapp_info}) list(GET cmdapp_info_list 0 appname) list(GET cmdapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitkFunctionCreateCommandLineApp( NAME ${appname} DEPENDS MitkCore MitkMatchPointRegistration ${dependencies_list} ) endforeach() endif(BUILD_MatchPointCmdApps OR MITK_BUILD_ALL_APPS) diff --git a/Modules/MatchPointRegistration/cmdapps/MapImageMiniApp.cpp b/Modules/MatchPointRegistration/cmdapps/MapImageMiniApp.cpp new file mode 100644 index 0000000000..3b57cb5023 --- /dev/null +++ b/Modules/MatchPointRegistration/cmdapps/MapImageMiniApp.cpp @@ -0,0 +1,286 @@ +/*============================================================================ + +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. + +============================================================================*/ + +// std includes +#include +#include + +// itk includes +//#include + +// CTK includes +#include + +// MITK includes +#include +#include +#include +#include +#include + +struct Settings +{ + std::string inFileName = ""; + std::string regFileName = ""; + std::string outFileName = ""; + std::string refGeometryFileName = ""; + + mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear; + double paddingValue = 0; + std::vector superSamplingFactors; +}; + +void setupParser(mitkCommandLineParser& parser) +{ + // set general information about your MiniApp + parser.setCategory("Registration Tools"); + parser.setTitle("Map Image"); + parser.setDescription("MiniApp that allows to map a image into a given output geometry by using a given registration."); + parser.setContributor("MIC, German Cancer Research Center (DKFZ)"); + //! [create parser] + + //! [add arguments] + // how should arguments be prefixed + parser.setArgumentPrefix("--", "-"); + // add each argument, unless specified otherwise each argument is optional + // see mitkCommandLineParser::addArgument for more information + parser.beginGroup("Required I/O parameters"); + parser.addArgument( + "input", "i", mitkCommandLineParser::File, "Input image", "Path to the input images that should be mapped", us::Any(), false, false, false, mitkCommandLineParser::Input); + parser.addArgument("output", + "o", + mitkCommandLineParser::File, + "Output file path", + "Path to the maped image.", + us::Any(), + false, false, false, mitkCommandLineParser::Output); + parser.endGroup(); + + parser.beginGroup("Optional parameters"); + parser.addArgument( + "registration", "r", mitkCommandLineParser::File, "Registration filee", "Path to the registration that should be used. If no registration is specified, an identity transform is assumed.", us::Any(), true, false, false, mitkCommandLineParser::Input); + parser.addArgument("template", + "t", + mitkCommandLineParser::File, + "Output template image.", + "File path to an image that serves as template for the output geometry. If no template is specified, the geometry of the input image will be used.", + us::Any(), + false, false, false, mitkCommandLineParser::Input); + parser.addArgument( + "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input); + parser.addArgument("interpolator", "n", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true); + parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true); + parser.addArgument("super-sampling", "s", mitkCommandLineParser::StringList, "Super sampling factor", "Value used for super sampling of the result. E.g. factor 2 will lead to a doubled resolution compared to the used template. If not specified, no super sampling will be done.", us::Any(), true); + parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.endGroup(); + //! [add arguments] +} + +bool configureApplicationSettings(std::map parsedArgs, Settings& settings) +{ + try + { + if (parsedArgs.size() == 0) + return false; + + settings.inFileName = us::any_cast(parsedArgs["input"]); + settings.outFileName = us::any_cast(parsedArgs["output"]); + + if (parsedArgs.count("template")) + { + settings.refGeometryFileName = us::any_cast(parsedArgs["template"]); + } + + if (parsedArgs.count("registration")) + { + settings.regFileName = us::any_cast(parsedArgs["registration"]); + } + + if (parsedArgs.count("interpolator")) + { + auto interpolator = us::any_cast(parsedArgs["interpolator"]); + settings.interpolatorType = static_cast(interpolator); + } + + if (parsedArgs.count("padding")) + { + settings.paddingValue = us::any_cast(parsedArgs["padding"]); + } + + settings.superSamplingFactors.clear(); + if (parsedArgs.count("super-sampling")) + { + try + { + auto samplingStrings = us::any_cast(parsedArgs["super-sampling"]); + if (samplingStrings.size() != 1 && samplingStrings.size() != 3) + { + std::cerr << "Error. Invalid number of super sampling parameters provided. Either give one (for isometric super sampling) or 3."; + return false; + } + + for (const auto& samplingstr : samplingStrings) + { + settings.superSamplingFactors.push_back(std::stoul(samplingstr)); + } + if (settings.superSamplingFactors.size() == 1) + { + settings.superSamplingFactors.push_back(settings.superSamplingFactors[0]); + settings.superSamplingFactors.push_back(settings.superSamplingFactors[0]); + } + } + catch (...) + { + std::cerr << "Error. Invalid super sampling parameter provided."; + throw; + } + } + } + catch (...) + { + return false; + } + + return true; +} + +int main(int argc, char* argv[]) +{ + mitk::Image::ConstPointer inputImage; + mitk::MAPRegistrationWrapper::ConstPointer registration; + mitk::BaseGeometry::Pointer refGeometry; + + Settings settings; + mitkCommandLineParser parser; + setupParser(parser); + + mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" }); + + const std::map& parsedArgs = parser.parseArguments(argc, argv); + if (!configureApplicationSettings(parsedArgs, settings)) + { + MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information."; + return EXIT_FAILURE; + }; + + // Show a help message + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + std::cout << std::endl << "*******************************************" << std::endl; + std::cout << "Input file: " << settings.inFileName << std::endl; + std::cout << "Output file: " << settings.outFileName << std::endl; + std::cout << "Registration: "; + if (settings.regFileName.empty()) + std::cout << "None (Identity)" << std::endl; + else + std::cout << settings.regFileName << std::endl; + std::cout << "Template: "; + if (settings.refGeometryFileName.empty()) + std::cout << "None (is input geometry)" << std::endl; + else + std::cout << settings.refGeometryFileName << std::endl; + std::cout << "Padding value: " << settings.paddingValue << std::endl; + std::cout << "Interpolation type: " << settings.interpolatorType << std::endl; + //check for super/sub sampling + if (!settings.superSamplingFactors.empty() && (settings.superSamplingFactors.size() != 1 || settings.superSamplingFactors[0] != 1)) + { + std::cout << "Super sampling:"; + for (auto value : settings.superSamplingFactors) + { + std::cout << " " << value; + } + std::cout << std::endl; + } + //! [do processing] + try + { + std::cout << "Load input data..." << std::endl; + inputImage = mitk::IOUtil::Load(settings.inFileName); + + if (inputImage.IsNull()) + { + MITK_ERROR << "Cannot load input image."; + return EXIT_FAILURE; + } + + std::cout << "Load registration..." << std::endl; + if (settings.regFileName.empty()) + { + std::cout << " associated registration: identity" << std::endl; + registration = mitk::GenerateIdentityRegistration3D().GetPointer(); + } + else + { + registration = mitk::IOUtil::Load(settings.regFileName); + } + + if (registration.IsNull()) + { + MITK_ERROR << "Cannot load registration."; + return EXIT_FAILURE; + } + + if (settings.refGeometryFileName != "") + { + std::cout << "Load reference image..." << std::endl; + auto refImage = mitk::IOUtil::Load(settings.refGeometryFileName, &readerFilterFunctor); + if (refImage.IsNotNull()) + { + refGeometry = refImage->GetGeometry(); + } + else + { + MITK_ERROR << "Cannot load reference geometry image."; + return EXIT_FAILURE; + } + } + else + { + refGeometry = inputImage->GetGeometry(); + } + + //check for super/sub sampling + if (!settings.superSamplingFactors.empty() && (settings.superSamplingFactors.size()!=1 || settings.superSamplingFactors[0]!=1)) + { + refGeometry = mitk::ImageMappingHelper::GenerateSuperSampledGeometry(refGeometry, + settings.superSamplingFactors[0], + settings.superSamplingFactors[1], + settings.superSamplingFactors[2]); + } + + std::cout << "Map the images ..." << std::endl; + + auto output = mitk::ImageMappingHelper::map(inputImage, registration, false, settings.paddingValue, refGeometry, true, 0, settings.interpolatorType); + + std::cout << "Save output image: " << settings.outFileName << std::endl; + + mitk::IOUtil::Save(output, settings.outFileName); + + std::cout << "Processing finished." << std::endl; + + return EXIT_SUCCESS; + } + catch (const std::exception& e) + { + MITK_ERROR << e.what(); + return EXIT_FAILURE; + } + catch (...) + { + MITK_ERROR << "Unexpected error encountered."; + return EXIT_FAILURE; + } +} diff --git a/Modules/MatchPointRegistration/cmdapps/MatchImageMiniApp.cpp b/Modules/MatchPointRegistration/cmdapps/MatchImageMiniApp.cpp new file mode 100644 index 0000000000..bbcb447ea4 --- /dev/null +++ b/Modules/MatchPointRegistration/cmdapps/MatchImageMiniApp.cpp @@ -0,0 +1,318 @@ +/*============================================================================ + +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. + +============================================================================*/ + +#include "mitkCommandLineParser.h" +#include +#include +#include +#include +#include +#include + +#include + +// MatchPoint +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Settings +{ + std::string movingFileName = ""; + std::string targetFileName = ""; + std::string outFileName = ""; + std::string algFileName = ""; +}; + +void SetupParser(mitkCommandLineParser& parser) +{ + parser.setTitle("Image Matcher"); + parser.setCategory("Registration Tools"); + parser.setDescription(""); + parser.setContributor("MIC, German Cancer Research Center (DKFZ)"); + + parser.setArgumentPrefix("--", "-"); + // Add command line argument names + parser.beginGroup("Required I/O parameters"); + parser.addArgument( + "moving", "m", + mitkCommandLineParser::File, + "Moving image files", "Path to the data that should be registred into the target space.", + us::Any(), false, false, false, mitkCommandLineParser::Input); + parser.addArgument( + "target", "t", + mitkCommandLineParser::File, + "Tareget image files", "Path to the data that should be the target data on which the moving data should be registered.", + us::Any(), false, false, false, mitkCommandLineParser::Input); + + parser.addArgument( + "algorithm", "a", + mitkCommandLineParser::File, + "Registration algorithm", "Path to the registration algorithm that should be used for registration.", + us::Any(), false, false, false, mitkCommandLineParser::Input); + + parser.addArgument("output", + "o", + mitkCommandLineParser::File, + "Output file path", + "Path to the generated registration.", + us::Any(), + false, false, false, mitkCommandLineParser::Output); + parser.endGroup(); + + parser.beginGroup("Optional parameters"); + parser.addArgument("template", + "t", + mitkCommandLineParser::File, + "Output template image.", + "File path to an image that serves as template for the output geometry.", + us::Any(), + false, false, false, mitkCommandLineParser::Input); + parser.addArgument( + "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input); + parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.endGroup(); +} + +bool ConfigureApplicationSettings(std::map parsedArgs, Settings& settings) +{ + try + { + if (parsedArgs.size() == 0) + return false; + + settings.movingFileName = us::any_cast(parsedArgs["moving"]); + settings.targetFileName = us::any_cast(parsedArgs["target"]); + settings.outFileName = us::any_cast(parsedArgs["output"]); + settings.algFileName = us::any_cast(parsedArgs["algorithm"]); + } + catch (...) + { + return false; + } + + return true; +} + +map::deployment::RegistrationAlgorithmBasePointer loadAlgorithm(const Settings& settings) +{ + map::deployment::RegistrationAlgorithmBasePointer spAlgorithmBase = nullptr; + + std::cout << std::endl << "Load registration algorithm..." << std::endl; + + map::deployment::DLLHandle::Pointer spHandle = nullptr; + + spHandle = map::deployment::openDeploymentDLL(settings.algFileName); + + if (spHandle.IsNull()) + { + mapDefaultExceptionStaticMacro(<< + "Cannot open deployed registration algorithm file."); + } + + std::cout << "... libary opened..." << std::endl; + + std::cout << "Algorithm information: " << std::endl; + spHandle->getAlgorithmUID().Print(std::cout, 2); + std::cout << std::endl; + + //Now load the algorthm from DLL + spAlgorithmBase = map::deployment::getRegistrationAlgorithm(spHandle); + + + if (spAlgorithmBase.IsNotNull()) + { + std::cout << "... done" << std::endl << std::endl; + } + else + { + mapDefaultExceptionStaticMacro(<< "Cannot create algorithm instance"); + } + return spAlgorithmBase; +}; + +mitk::Image::Pointer ExtractFirstFrame(const mitk::Image* dynamicImage) +{ + mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); + imageTimeSelector->SetInput(dynamicImage); + imageTimeSelector->SetTimeNr(0); + imageTimeSelector->UpdateLargestPossibleRegion(); + + return imageTimeSelector->GetOutput(); +} + +void OnMapAlgorithmEvent(::itk::Object*, const itk::EventObject& event, void*) +{ + const map::events::AlgorithmEvent* pAlgEvent = dynamic_cast(&event); + const map::events::AlgorithmWrapperEvent* pWrapEvent = + dynamic_cast(&event); + const map::events::InitializingAlgorithmEvent* pInitEvent = + dynamic_cast(&event); + const map::events::StartingAlgorithmEvent* pStartEvent = + dynamic_cast(&event); + const map::events::StoppingAlgorithmEvent* pStoppingEvent = + dynamic_cast(&event); + const map::events::StoppedAlgorithmEvent* pStoppedEvent = + dynamic_cast(&event); + const map::events::FinalizingAlgorithmEvent* pFinalizingEvent = + dynamic_cast(&event); + const map::events::FinalizedAlgorithmEvent* pFinalizedEvent = + dynamic_cast(&event); + + if (pInitEvent) + { + std::cout <<"Initializing algorithm ..." << std::endl; + } + else if (pStartEvent) + { + std::cout <<"Starting algorithm ..." << std::endl; + } + else if (pStoppingEvent) + { + std::cout <<"Stopping algorithm ..." << std::endl; + } + else if (pStoppedEvent) + { + std::cout <<"Stopped algorithm ..." << std::endl; + + if (!pStoppedEvent->getComment().empty()) + { + std::cout <<"Stopping condition: " << pStoppedEvent->getComment() << std::endl; + } + } + else if (pFinalizingEvent) + { + std::cout <<"Finalizing algorithm and results ..." << std::endl; + } + else if (pFinalizedEvent) + { + std::cout <<"Finalized algorithm ..." << std::endl; + } + else if (pAlgEvent && !pWrapEvent) + { + std::cout << pAlgEvent->getComment() << std::endl; + } +} + +int main(int argc, char* argv[]) +{ + std::cout << "MitkRegistrationMiniApp - Generic light weight image registration tool based on MatchPoint." << std::endl; + + Settings settings; + mitkCommandLineParser parser; + SetupParser(parser); + + const std::map& parsedArgs = parser.parseArguments(argc, argv); + if (!ConfigureApplicationSettings(parsedArgs, settings)) + { + MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information."; + return EXIT_FAILURE; + }; + + // Show a help message + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + std::cout << std::endl << "*******************************************" << std::endl; + std::cout << "Moving file: " << settings.movingFileName << std::endl; + std::cout << "Target file: " << settings.targetFileName << std::endl; + std::cout << "Output file: " << settings.outFileName << std::endl; + std::cout << "Algorithm location: " << settings.algFileName << std::endl; + + //load algorithm + try + { + auto algorithm = loadAlgorithm(settings); + + auto command = ::itk::CStyleCommand::New(); + command->SetCallback(OnMapAlgorithmEvent); + algorithm->AddObserver(::map::events::AlgorithmEvent(), command); + + std::cout << "Load moving data..." << std::endl; + auto movingImage = mitk::IOUtil::Load(settings.movingFileName); + + if (movingImage.IsNull()) + { + MITK_ERROR << "Cannot load moving image."; + return EXIT_FAILURE; + } + + if (movingImage->GetTimeSteps() > 1) + { + movingImage = mitk::SelectImageByTimeStep(movingImage, 0)->Clone(); //we have to clone because SelectImageByTimeStep + //only generates as new view of the data and we + //are overwriting the only smartpointer to the source. + std::cout << "Moving image has multiple time steps. Use first time step for registartion." << std::endl; + } + + std::cout << "Load target data..." << std::endl; + auto targetImage = mitk::IOUtil::Load(settings.targetFileName); + + if (targetImage.IsNull()) + { + MITK_ERROR << "Cannot load target image."; + return EXIT_FAILURE; + } + + if (targetImage->GetTimeSteps() > 1) + { + targetImage = mitk::SelectImageByTimeStep(targetImage, 0)->Clone(); //we have to clone because SelectImageByTimeStep + //only generates as new view of the data and we + //are overwriting the only smartpointer to the source. + std::cout << "Target image has multiple time steps. Use first time step for registartion." << std::endl; + } + + std::cout << "Start registration...." << std::endl; + mitk::MAPAlgorithmHelper helper(algorithm); + helper.SetData(movingImage, targetImage); + + ::itk::StdStreamLogOutput::Pointer spStreamLogOutput = ::itk::StdStreamLogOutput::New(); + spStreamLogOutput->SetStream(std::cout); + map::core::Logbook::addAdditionalLogOutput(spStreamLogOutput); + + auto registration = helper.GetRegistration(); + + // wrap the registration in a data node + if (registration.IsNull()) + { + MITK_ERROR << "No valid registration generated"; + return EXIT_FAILURE; + } + + auto regWrapper = mitk::MAPRegistrationWrapper::New(registration); + std::cout << "Store registration...." << std::endl; + mitk::IOUtil::Save(regWrapper, settings.outFileName); + } + catch (const std::exception& e) + { + MITK_ERROR << e.what(); + return EXIT_FAILURE; + } + catch (...) + { + MITK_ERROR << "Unexpected error encountered."; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp b/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp index ef8fc82bd2..159d96a34f 100644 --- a/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp +++ b/Modules/MatchPointRegistration/cmdapps/StitchImagesMiniApp.cpp @@ -1,226 +1,226 @@ /*============================================================================ 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. ============================================================================*/ // std includes #include #include // itk includes #include "itksys/SystemTools.hxx" // CTK includes #include "mitkCommandLineParser.h" // MITK includes #include #include #include #include #include mitkCommandLineParser::StringContainerType inFilenames; mitkCommandLineParser::StringContainerType regFilenames; std::string outFileName; std::string refGeometryFileName; std::vector images; std::vector registrations; mitk::BaseGeometry::Pointer refGeometry; mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear; double paddingValue = 0; itk::StitchStrategy stitchStratgy = itk::StitchStrategy::Mean; void setupParser(mitkCommandLineParser& parser) { // set general information about your MiniApp - parser.setCategory("Mapping Tools"); + parser.setCategory("Registration Tools"); parser.setTitle("Stitch 3D Images"); parser.setDescription("MiniApp that allows to map and stitch 3D images into a given output geometry."); - parser.setContributor("DKFZ MIC"); + parser.setContributor("MIC, German Cancer Research Center (DKFZ)"); //! [create parser] //! [add arguments] // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Required I/O parameters"); parser.addArgument( "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be mapped and stitched", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file path", "Path to the fused 3D+t image.", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument("template", "t", mitkCommandLineParser::File, "Output template image.", "File path to an image that serves as template for the output geometry.", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument( "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("interpolator", "n", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true); parser.addArgument("strategy", "s", mitkCommandLineParser::Int, "Stitch strategy", "Strategy used for stitching the images. 0: Mean -> computes the mean value of all input images that cover an output pixel (default strategy). 1: BorderDistance -> Uses the input pixel that has the largest minimal distance to its image borders", us::Any(2), true); parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true); parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.endGroup(); //! [add arguments] } bool configureApplicationSettings(std::map parsedArgs) { try { if (parsedArgs.size() == 0) return false; inFilenames = us::any_cast(parsedArgs["inputs"]); outFileName = us::any_cast(parsedArgs["output"]); if (parsedArgs.count("template")) { refGeometryFileName = us::any_cast(parsedArgs["template"]); } if (parsedArgs.count("registrations")) { regFilenames = us::any_cast(parsedArgs["registrations"]); } else { regFilenames.resize(inFilenames.size()); std::fill(regFilenames.begin(), regFilenames.end(), ""); } if (parsedArgs.count("interpolator")) { auto interpolator = us::any_cast(parsedArgs["interpolator"]); interpolatorType = static_cast(interpolator); } if (parsedArgs.count("padding")) { paddingValue = us::any_cast(parsedArgs["padding"]); } if (parsedArgs.count("strategy")) { auto temp = us::any_cast(parsedArgs["strategy"]); stitchStratgy = static_cast(temp); } } catch (...) { return false; } return true; } int main(int argc, char* argv[]) { mitkCommandLineParser parser; setupParser(parser); mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" }); const std::map& parsedArgs = parser.parseArguments(argc, argv); if (!configureApplicationSettings(parsedArgs)) { MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information."; return EXIT_FAILURE; }; // Show a help message if (parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } if(regFilenames.size() != inFilenames.size()) { MITK_ERROR << "Cannot stitch inputs. The number of specified registrations does not match the number of inputs."; return EXIT_FAILURE; } //! [do processing] try { std::cout << "Load images:" << std::endl; unsigned int index = 0; for (auto path : inFilenames) { std::cout << "#"<(path, &readerFilterFunctor); images.push_back(image.GetPointer()); if (regFilenames[index].empty()) { std::cout << " associated registration: identity" << std::endl; registrations.push_back(mitk::GenerateIdentityRegistration3D().GetPointer()); } else { std::cout << " associated registration: " << regFilenames[index] << std::endl; auto reg = mitk::IOUtil::Load(regFilenames[index]); registrations.push_back(reg.GetPointer()); } ++index; } std::cout << "Reference image: " << refGeometryFileName << std::endl << std::endl; auto refImage = mitk::IOUtil::Load(refGeometryFileName, &readerFilterFunctor); if (refImage.IsNotNull()) { refGeometry = refImage->GetGeometry(); } std::cout << "Padding value: " << paddingValue << std::endl; std::cout << "Stitch strategy: "; if (itk::StitchStrategy::Mean == stitchStratgy) { std::cout << "Mean " << std::endl; } else { std::cout << "BorderDistance" << std::endl; } std::cout << "Stitch the images ..." << std::endl; auto output = mitk::StitchImages(images, registrations, refGeometry,paddingValue,stitchStratgy,interpolatorType); std::cout << "Save output image: " << outFileName << std::endl; mitk::IOUtil::Save(output, outFileName); std::cout << "Processing finished." << std::endl; return EXIT_SUCCESS; } catch (const std::exception& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (...) { MITK_ERROR << "Unexpected error encountered."; return EXIT_FAILURE; } } diff --git a/Modules/MatchPointRegistration/deployment/CMakeLists.txt b/Modules/MatchPointRegistration/deployment/CMakeLists.txt index 120526bfa0..b0a51b1ce6 100644 --- a/Modules/MatchPointRegistration/deployment/CMakeLists.txt +++ b/Modules/MatchPointRegistration/deployment/CMakeLists.txt @@ -1,8 +1,10 @@ set(MDRA_INCLUDE_DIRS PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../algorithms) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_MultiModal_affine_default CPP_FILES mitkMultiModalAffine_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_MultiModal_rigid_default CPP_FILES mitkMultiModalRigid_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) +mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_MultiModal_rigid_headNeck CPP_FILES mitkMultiModalRigid_headNeck.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) +mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_MultiModal_rigid_slabbedHead CPP_FILES mitkMultiModalRigid_slabbedHead.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_MultiModal_translation_default CPP_FILES mitkMultiModalTrans_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_Rigid_closedform_points_default CPP_FILES mitkRigidClosedFormPoints_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_Rigid_ICP_default CPP_FILES mitkRigidICP_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_FastSymmetricForcesDemons_MultiRes_default CPP_FILES mitkFastSymmetricForcesDemonsMultiRes_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) mitkFunctionCreateMatchPointDeployedAlgorithm(NAME MITK_LevelSetMotion_MultiRes_default CPP_FILES mitkLevelSetMotionMultiRes_default.cpp NO_PROFILE_GEN INCLUDE_DIRS ${MDRA_INCLUDE_DIRS}) diff --git a/Modules/MatchPointRegistration/deployment/mitkMultiModalRigid_headNeck.cpp b/Modules/MatchPointRegistration/deployment/mitkMultiModalRigid_headNeck.cpp new file mode 100644 index 0000000000..86791c6458 --- /dev/null +++ b/Modules/MatchPointRegistration/deployment/mitkMultiModalRigid_headNeck.cpp @@ -0,0 +1,21 @@ +/*============================================================================ + +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. + +============================================================================*/ + +#include "mapDeploymentDLLHelper.h" +#include "mapDiscreteElements.h" +#include "mapConfigure.h" + +#include "mitkMultiModalRigidHeadNeckRegistrationAlgorithm.h" + +typedef map::core::discrete::Elements<3>::InternalImageType ImageType; + +mapDeployAlgorithmMacro(mitk::MultiModalRigidHeadNeckRegistrationAlgorithm); diff --git a/Modules/MatchPointRegistration/deployment/mitkMultiModalRigid_slabbedHead.cpp b/Modules/MatchPointRegistration/deployment/mitkMultiModalRigid_slabbedHead.cpp new file mode 100644 index 0000000000..eaee90756c --- /dev/null +++ b/Modules/MatchPointRegistration/deployment/mitkMultiModalRigid_slabbedHead.cpp @@ -0,0 +1,21 @@ +/*============================================================================ + +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. + +============================================================================*/ + +#include "mapDeploymentDLLHelper.h" +#include "mapDiscreteElements.h" +#include "mapConfigure.h" + +#include "mitkMultiModalRigidSlabbedHeadRegistrationAlgorithm.h" + +typedef map::core::discrete::Elements<3>::InternalImageType ImageType; + +mapDeployAlgorithmMacro(mitk::MultiModalRigidSlabbedHeadRegistrationAlgorithm); diff --git a/Modules/MatchPointRegistration/include/mitkImageMappingHelper.h b/Modules/MatchPointRegistration/include/mitkImageMappingHelper.h index 82cc725c29..7cceb311f5 100644 --- a/Modules/MatchPointRegistration/include/mitkImageMappingHelper.h +++ b/Modules/MatchPointRegistration/include/mitkImageMappingHelper.h @@ -1,113 +1,116 @@ /*============================================================================ 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 mitkImageMappingHelper_h #define mitkImageMappingHelper_h #include "mapImageMappingTask.h" #include "mapRegistrationBase.h" #include "mitkImage.h" #include "mitkGeometry3D.h" #include "mitkMAPRegistrationWrapper.h" #include "MitkMatchPointRegistrationExports.h" namespace mitk { struct ImageMappingInterpolator { enum Type { UserDefined = 0, //< User may specify the interpolator to use NearestNeighbor = 1, //< use nearest neighbor Linear = 2, //< use linear BSpline_3 = 3, //< use a 3rd order spline WSinc_Hamming = 4, //< use a wsinc with hamming window WSinc_Welch = 5 //< use a wsinc with welch window }; }; namespace ImageMappingHelper { typedef ::map::core::RegistrationBase RegistrationType; typedef ::mitk::MAPRegistrationWrapper MITKRegistrationType; typedef ::mitk::BaseGeometry ResultImageGeometryType; typedef ::mitk::Image InputImageType; typedef ::mitk::Image ResultImageType; /**Helper that maps a given input image * @param input Image that should be mapped. * @param registration Pointer to the registration instance that should be used for mapping * @param throwOnOutOfInputAreaError Indicates if mapping should fail with an exception (true), if the input image does not cover the whole requested region to be mapped into the result image. * @param paddingValue Indicates the value that should be used if an out of input error occurs (and throwOnOutOfInputAreaError is false). * @param resultGeometry Pointer to the Geometry object that specifies the grid of the result image. If not defined the geometry of the input image will be used. * @param throwOnMappingError Indicates if mapping should fail with an exception (true), if the registration does not cover/support the whole requested region for mapping into the result image. * @param errorValue Indicates the value that should be used if an mapping error occurs (and throwOnMappingError is false). * @param interpolatorType Indicates the type of interpolation strategy that should be used. * @pre input must be valid * @pre registration must be valid * @pre Dimensionality of the registration must match with the input imageinput must be valid * @remark Depending in the settings of throwOnOutOfInputAreaError and throwOnMappingError it may also throw * due to inconsistencies in the mapping process. See parameter description. * @result Pointer to the resulting mapped image.h*/ MITKMATCHPOINTREGISTRATION_EXPORT ResultImageType::Pointer map(const InputImageType* input, const RegistrationType* registration, bool throwOnOutOfInputAreaError = false, const double& paddingValue = 0, const ResultImageGeometryType* resultGeometry = nullptr, bool throwOnMappingError = true, const double& errorValue = 0, mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear); /**Helper that maps a given input image. * @overload * @param input Image that should be mapped. * @param registration Pointer to the registration instance that should be used for mapping * @param throwOnOutOfInputAreaError Indicates if mapping should fail with an exception (true), if the input image does not cover the whole requested region to be mapped into the result image. * @param paddingValue Indicates the value that should be used if an out of input error occurs (and throwOnOutOfInputAreaError is false). * @param resultGeometry Pointer to the Geometry object that specifies the grid of the result image. If not defined the geometry of the input image will be used. * @param throwOnMappingError Indicates if mapping should fail with an exception (true), if the registration does not cover/support the whole requested region for mapping into the result image. * @param errorValue Indicates the value that should be used if an mapping error occurs (and throwOnMappingError is false). * @param interpolatorType Indicates the type of interpolation strategy that should be used. * @pre input must be valid * @pre registration must be valid * @pre Dimensionality of the registration must match with the input imageinput must be valid * @remark Depending in the settings of throwOnOutOfInputAreaError and throwOnMappingError it may also throw * due to inconsistencies in the mapping process. See parameter description. * @result Pointer to the resulting mapped image.h*/ MITKMATCHPOINTREGISTRATION_EXPORT ResultImageType::Pointer map(const InputImageType* input, const MITKRegistrationType* registration, bool throwOnOutOfInputAreaError = false, const double& paddingValue = 0, const ResultImageGeometryType* resultGeometry = nullptr, bool throwOnMappingError = true, const double& errorValue = 0, mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear); + MITKMATCHPOINTREGISTRATION_EXPORT ResultImageGeometryType::Pointer GenerateSuperSampledGeometry(const ResultImageGeometryType* inputGeometry, + double xScaling, double yScaling, double zScaling); + /**Method clones the input image and applies the registration by applying it to the Geometry3D of the image. Thus this method only produces a result if the passed registration has an direct mapping kernel that can be converted into an affine matrix transformation. @pre input must point to a valid instance @pre registration must point to a valid instance @pre registration must be decomposable into rotation matrix and offset or throwOnError must be false. @result Pointer to the result image with refined geometry. May be null if the result cannot be generated (e.g. the registration cannot be transformed in a affine matrix transformation)*/ MITKMATCHPOINTREGISTRATION_EXPORT ResultImageType::Pointer refineGeometry(const InputImageType* input, const RegistrationType* registration, bool throwOnError = true); /**@overload*/ MITKMATCHPOINTREGISTRATION_EXPORT ResultImageType::Pointer refineGeometry(const InputImageType* input, const MITKRegistrationType* registration, bool throwOnError = true); /**Method allows to pre checks if a registration could be used with refineGeometry. If this method returns true, * the registration can be used to refine the geometry of images.*/ MITKMATCHPOINTREGISTRATION_EXPORT bool canRefineGeometry(const RegistrationType* registration); /**@overload*/ MITKMATCHPOINTREGISTRATION_EXPORT bool canRefineGeometry(const MITKRegistrationType* registration); } } #endif diff --git a/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h b/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h index d6006eb44e..b3460d25c7 100644 --- a/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h +++ b/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h @@ -1,269 +1,273 @@ /*============================================================================ 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 mitkMAPRegistrationWrapper_h #define mitkMAPRegistrationWrapper_h //MITK #include #include //MatchPoint #include #include #include #include //MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { /*! \brief MAPRegistrationWrapper Wrapper class to allow the handling of MatchPoint registration objects as mitk data (e.g. in the data explorer). */ class MITKMATCHPOINTREGISTRATION_EXPORT MAPRegistrationWrapper: public mitk::BaseData { public: mitkClassMacro( MAPRegistrationWrapper, BaseData ); mitkNewMacro1Param( Self, ::map::core::RegistrationBase*); Identifiable::UIDType GetUID() const override; + bool IsEmptyTimeStep(unsigned int t) const override; + + bool IsEmpty() const override; + /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ void SetRequestedRegionToLargestPossibleRegion() override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ bool RequestedRegionIsOutsideOfTheBufferedRegion() override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ bool VerifyRequestedRegion() override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ void SetRequestedRegion(const itk::DataObject*) override; /*! @brief Gets the number of moving dimensions @pre valid registration instance must be set. */ virtual unsigned int GetMovingDimensions() const; /*! @brief Gets the number of target dimensions @pre valid registration instance must be set. */ virtual unsigned int GetTargetDimensions() const; /*! typedefs used for the TagMap */ typedef ::map::core::RegistrationBase::TagType TagType; typedef ::map::core::RegistrationBase::ValueType ValueType; typedef ::map::core::RegistrationBase::TagMapType TagMapType; /*! @brief returns the tags associated with this registration @pre valid registration instance must be set. @return a TagMapType containing tags */ const TagMapType& GetTags() const; /*! @brief returns the tag value for a specific tag @pre valid registration instance must be set. @return the success of the operation */ bool GetTagValue(const TagType & tag, ValueType & value) const; /*! Indicates @pre valid registration instance must be set. @return is the target representation limited @retval true if target representation is limited. Thus it is not guaranteed that all inverse mapping operations will succeed. Transformation(inverse kernel) covers only a part of the target space). @retval false if target representation is not limited. Thus it is guaranteed that all inverse mapping operations will succeed. */ bool HasLimitedTargetRepresentation() const; /*! @pre valid registration instance must be set. @return is the moving representation limited @retval true if moving representation is limited. Thus it is not guaranteed that all direct mapping operations will succeed. Transformation(direct kernel) covers only a part of the moving space). @retval false if moving representation is not limited. Thus it is guaranteed that all direct mapping operations will succeed. */ bool HasLimitedMovingRepresentation() const; /*! Helper function that maps a mitk point (of arbitrary dimension) from moving space to target space. @remarks The operation might fail, if the moving and target dimension of the registration is not equal to the dimensionality of the passed points. @pre valid registration instance must be set. @param inPoint Reference pointer to a MovingPointType @param outPoint pointer to a TargetPointType @return success of operation. @pre direct mapping kernel must be defined */ template bool MapPoint(const ::itk::Point& inPoint, ::itk::Point& outPoint) const { typedef typename ::map::core::continuous::Elements::PointType MAPMovingPointType; typedef typename ::map::core::continuous::Elements::PointType MAPTargetPointType; if (m_spRegistration.IsNull()) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint); } bool result = false; if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim)) { MAPMovingPointType tempInP; MAPTargetPointType tempOutP; tempInP.CastFrom(inPoint); typedef ::map::core::Registration CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); if (!pCastedReg) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint); } result = pCastedReg->mapPoint(tempInP,tempOutP); if (result) { outPoint.CastFrom(tempOutP); } } return result; }; /*! Helper function that maps a mitk point (of arbitrary dimension) from target space to moving space @remarks The operation might faile, if the moving and target dimension of the registration is not equal to the dimensionalities of the passed points. @pre valid registration instance must be set. @param inPoint pointer to a TargetPointType @param outPoint pointer to a MovingPointType @return success of operation */ template bool MapPointInverse(const ::itk::Point & inPoint, ::itk::Point & outPoint) const { typedef typename ::map::core::continuous::Elements::PointType MAPMovingPointType; typedef typename ::map::core::continuous::Elements::PointType MAPTargetPointType; if (m_spRegistration.IsNull()) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint); } bool result = false; if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim)) { MAPTargetPointType tempInP; MAPMovingPointType tempOutP; tempInP.CastFrom(inPoint); typedef ::map::core::Registration CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); if (!pCastedReg) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint); } result = pCastedReg->mapPointInverse(tempInP,tempOutP); if (result) { outPoint.CastFrom(tempOutP); } } return result; }; /*! returns the direct FieldRepresentationDescriptor which defines the part of the moving space that is guaranteed to be mapped by the direct mapping kernel. This member converts the internal MatchPoint type into a mitk::Geometry3D. @pre valid registration instance must be set. @return smart pointer to a FieldRepresentationDescriptor for the supported registration space in the moving domain. May be null if the direct registration kernel is global and thus not limited. If there is a limitation, the retun value is not nullptr. @retval nullptr no field representation set/requested by the creating registration algorithm. */ mitk::Geometry3D GetDirectFieldRepresentation() const; /*! returns the inverse FieldRepresentationDescriptor which defines the part of the target space that is guaranteed to be mapped by the inverse mapping kernel. This member converts the internal MatchPoint type into a mitk::Geometry3D. @pre valid registration instance must be set. @return a const FieldRepresentationDescriptor for the supported registration space in the target domain. May be null if the inverse registration kernel is global and thus not limited. If there is a limitation, the retun value is not nullptr. @retval nullptr no field representation set/requested by the creating registration algorithm. */ mitk::Geometry3D GetInverseFieldRepresentation() const; /*! forces kernel to precompute, even if it is a LazyFieldKernel @pre valid registration instance must be set. @todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten ist die garantie dieser methode nicht erfllbar. noch berprfen */ void PrecomputeDirectMapping(); /*! forces kernel to precompute, even if it is a LazyFieldKernel @pre valid registration instance must be set. @todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten ist die garantie dieser methode nicht erfllbar. noch berprfen */ void PrecomputeInverseMapping(); ::map::core::RegistrationBase* GetRegistration(); const ::map::core::RegistrationBase* GetRegistration() const; protected: void PrintSelf (std::ostream &os, itk::Indent indent) const override; MAPRegistrationWrapper(::map::core::RegistrationBase* registration); ~MAPRegistrationWrapper() override; void SetUID(const UIDType& uid) override; ::map::core::RegistrationBase::Pointer m_spRegistration; private: MAPRegistrationWrapper& operator = (const MAPRegistrationWrapper&); MAPRegistrationWrapper(const MAPRegistrationWrapper&); }; } #endif diff --git a/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp index 1721cdf683..39a9e5f197 100644 --- a/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp +++ b/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp @@ -1,444 +1,482 @@ /*============================================================================ 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. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "mapRegistration.h" #include "mitkImageMappingHelper.h" #include "mitkRegistrationHelper.h" template typename ::itk::InterpolateImageFunction< TImage >::Pointer generateInterpolator(mitk::ImageMappingInterpolator::Type interpolatorType) { typedef ::itk::InterpolateImageFunction< TImage > BaseInterpolatorType; typename BaseInterpolatorType::Pointer result; switch (interpolatorType) { case mitk::ImageMappingInterpolator::NearestNeighbor: { result = ::itk::NearestNeighborInterpolateImageFunction::New(); break; } case mitk::ImageMappingInterpolator::BSpline_3: { typename ::itk::BSplineInterpolateImageFunction::Pointer spInterpolator = ::itk::BSplineInterpolateImageFunction::New(); spInterpolator->SetSplineOrder(3); result = spInterpolator; break; } case mitk::ImageMappingInterpolator::WSinc_Hamming: { result = ::itk::WindowedSincInterpolateImageFunction::New(); break; } case mitk::ImageMappingInterpolator::WSinc_Welch: { result = ::itk::WindowedSincInterpolateImageFunction >::New(); break; } default: { result = ::itk::LinearInterpolateImageFunction::New(); break; } } return result; }; template void doMITKMap(const ::itk::Image* input, mitk::ImageMappingHelper::ResultImageType::Pointer& result, const mitk::ImageMappingHelper::RegistrationType*& registration, bool throwOnOutOfInputAreaError, const double& paddingValue, const mitk::ImageMappingHelper::ResultImageGeometryType*& resultGeometry, bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type interpolatorType) { typedef ::map::core::Registration ConcreteRegistrationType; typedef ::map::core::ImageMappingTask, ::itk::Image > MappingTaskType; typename MappingTaskType::Pointer spTask = MappingTaskType::New(); typedef typename MappingTaskType::ResultImageDescriptorType ResultImageDescriptorType; typename ResultImageDescriptorType::Pointer resultDescriptor; //check if image and result geometry fits the passed registration ///////////////////////////////////////////////////////////////// if (registration->getMovingDimensions()!=VImageDimension) { map::core::OStringStream str; str << "Dimension of MITK image ("<getMovingDimensions()<<")."; throw mitk::AccessByItkException(str.str()); } if (registration->getTargetDimensions()!=VImageDimension) { map::core::OStringStream str; str << "Dimension of MITK image ("<getTargetDimensions()<<")."; throw mitk::AccessByItkException(str.str()); } const ConcreteRegistrationType* castedReg = dynamic_cast(registration); if (registration->getTargetDimensions()==2 && resultGeometry) { mitk::ImageMappingHelper::ResultImageGeometryType::BoundsArrayType bounds = resultGeometry->GetBounds(); if (bounds[4]!=0 || bounds[5]!=0) { //array "bounds" is constructed as [min Dim1, max Dim1, min Dim2, max Dim2, min Dim3, max Dim3] //therfore [4] and [5] must be 0 map::core::OStringStream str; str << "Dimension of defined result geometry does not equal the target dimension of the registration object ("<getTargetDimensions()<<")."; throw mitk::AccessByItkException(str.str()); } } //check/create resultDescriptor ///////////////////////// if (resultGeometry) { resultDescriptor = ResultImageDescriptorType::New(); typename ResultImageDescriptorType::PointType origin; typename ResultImageDescriptorType::SizeType size; typename ResultImageDescriptorType::SpacingType fieldSpacing; typename ResultImageDescriptorType::DirectionType matrix; mitk::ImageMappingHelper::ResultImageGeometryType::BoundsArrayType geoBounds = resultGeometry->GetBounds(); mitk::Vector3D geoSpacing = resultGeometry->GetSpacing(); mitk::Point3D geoOrigin = resultGeometry->GetOrigin(); mitk::AffineTransform3D::MatrixType geoMatrix = resultGeometry->GetIndexToWorldTransform()->GetMatrix(); for (unsigned int i = 0; i(geoOrigin[i]); fieldSpacing[i] = static_cast(geoSpacing[i]); size[i] = static_cast(geoBounds[(2*i)+1]-geoBounds[2*i])*fieldSpacing[i]; } //Matrix extraction matrix.SetIdentity(); unsigned int i; unsigned int j; /// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix. /// If it is only a rotation around the transversal plane normal, it can be express with a 2x2 matrix. /// In this case, the ITK image conservs this information and is identical to the MITK image! /// If the MITK image contains any other rotation, the ITK image will have no rotation at all. /// Spacing is of course conserved in both cases. // the following loop devides by spacing now to normalize columns. // counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092. // Check if information is lost if ( VImageDimension == 2) { if ( ( geoMatrix[0][2] != 0) || ( geoMatrix[1][2] != 0) || ( geoMatrix[2][0] != 0) || ( geoMatrix[2][1] != 0) || (( geoMatrix[2][2] != 1) && ( geoMatrix[2][2] != -1) )) { // The 2D MITK image contains 3D rotation information. // This cannot be expressed in a 2D ITK image, so the ITK image will have no rotation } else { // The 2D MITK image can be converted to an 2D ITK image without information loss! for ( i=0; i < 2; ++i) { for( j=0; j < 2; ++j ) { matrix[i][j] = geoMatrix[i][j]/fieldSpacing[j]; } } } } else if (VImageDimension == 3) { // Normal 3D image. Conversion possible without problem! for ( i=0; i < 3; ++i) { for( j=0; j < 3; ++j ) { matrix[i][j] = geoMatrix[i][j]/fieldSpacing[j]; } } } else { assert(0); throw mitk::AccessByItkException("Usage of resultGeometry for 2D images is not yet implemented."); /**@TODO Implement extraction of 2D-Rotation-Matrix out of 3D-Rotation-Matrix * to cover this case as well. * matrix = extract2DRotationMatrix(resultGeometry)*/ } resultDescriptor->setOrigin(origin); resultDescriptor->setSize(size); resultDescriptor->setSpacing(fieldSpacing); resultDescriptor->setDirection(matrix); } //do the mapping ///////////////////////// typedef ::itk::InterpolateImageFunction< ::itk::Image > BaseInterpolatorType; typename BaseInterpolatorType::Pointer interpolator = generateInterpolator< ::itk::Image >(interpolatorType); assert(interpolator.IsNotNull()); spTask->setImageInterpolator(interpolator); spTask->setInputImage(input); spTask->setRegistration(castedReg); spTask->setResultImageDescriptor(resultDescriptor); spTask->setThrowOnMappingError(throwOnMappingError); spTask->setErrorValue(errorValue); spTask->setThrowOnPaddingError(throwOnOutOfInputAreaError); spTask->setPaddingValue(paddingValue); spTask->execute(); mitk::CastToMitkImage<>(spTask->getResultImage(),result); } /**Helper function to ensure the mapping of all time steps of an image.*/ void doMapTimesteps(const mitk::ImageMappingHelper::InputImageType* input, mitk::Image* result, const mitk::ImageMappingHelper::RegistrationType* registration, bool throwOnOutOfInputAreaError,double paddingValue, const mitk::ImageMappingHelper::ResultImageGeometryType* resultGeometry, bool throwOnMappingError, double errorValue, mitk::ImageMappingInterpolator::Type interpolatorType) { for (unsigned int i = 0; iGetTimeSteps(); ++i) { mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(input); imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::ImageMappingHelper::InputImageType::Pointer timeStepInput = imageTimeSelector->GetOutput(); mitk::ImageMappingHelper::ResultImageType::Pointer timeStepResult; AccessByItk_n(timeStepInput, doMITKMap, (timeStepResult, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType)); mitk::ImageReadAccessor readAccess(timeStepResult); result->SetVolume(readAccess.GetData(), i); } } mitk::TimeGeometry::Pointer CreateResultTimeGeometry(const mitk::ImageMappingHelper::InputImageType* input, const mitk::ImageMappingHelper::ResultImageGeometryType* resultGeometry) { mitk::TimeGeometry::ConstPointer timeGeometry = input->GetTimeGeometry(); mitk::TimeGeometry::Pointer mappedTimeGeometry = timeGeometry->Clone(); for (unsigned int i = 0; i < input->GetTimeSteps(); ++i) { mitk::ImageMappingHelper::ResultImageGeometryType::Pointer mappedGeometry = resultGeometry->Clone(); mappedTimeGeometry->SetTimeStepGeometry(mappedGeometry, i); } return mappedTimeGeometry; } mitk::ImageMappingHelper::ResultImageType::Pointer mitk::ImageMappingHelper::map(const InputImageType* input, const RegistrationType* registration, bool throwOnOutOfInputAreaError, const double& paddingValue, const ResultImageGeometryType* resultGeometry, bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type interpolatorType) { if (!registration) { mitkThrow() << "Cannot map image. Passed registration wrapper pointer is nullptr."; } if (!input) { mitkThrow() << "Cannot map image. Passed image pointer is nullptr."; } ResultImageType::Pointer result; auto inputLabelSetImage = dynamic_cast(input); if (nullptr == inputLabelSetImage) { if (input->GetTimeSteps() == 1) { //map the image and done AccessByItk_n(input, doMITKMap, (result, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType)); } else { //map every time step and compose auto mappedTimeGeometry = CreateResultTimeGeometry(input, resultGeometry); result = mitk::Image::New(); result->Initialize(input->GetPixelType(), *mappedTimeGeometry, 1, input->GetTimeSteps()); doMapTimesteps(input, result, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType); } } else { auto resultLabelSetImage = LabelSetImage::New(); auto mappedTimeGeometry = CreateResultTimeGeometry(input, resultGeometry); auto resultTemplate = mitk::Image::New(); resultTemplate->Initialize(input->GetPixelType(), *mappedTimeGeometry, 1, input->GetTimeSteps()); resultLabelSetImage->Initialize(resultTemplate); auto cloneInput = inputLabelSetImage->Clone(); //We need to clone the LabelSetImage due to its illposed design. It is state full //and we have to iterate through all layers as active layers to ensure the content //was really stored (directly working with the layer images does not work with the //active layer). The clone wastes rescources but is the easiest and safest way to //ensure 1) correct mapping 2) avoid race conditions with other parts of the //application because we would change the state of the input. //This whole code block should be reworked as soon as T28525 is done. for (unsigned int layerID = 0; layerID < inputLabelSetImage->GetNumberOfLayers(); ++layerID) { if (resultLabelSetImage->GetNumberOfLayers() <= layerID) { resultLabelSetImage->AddLayer(); } resultLabelSetImage->AddLabelSetToLayer(layerID, inputLabelSetImage->GetLabelSet(layerID)->Clone()); cloneInput->SetActiveLayer(layerID); resultLabelSetImage->SetActiveLayer(layerID); doMapTimesteps(cloneInput, resultLabelSetImage, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, mitk::ImageMappingInterpolator::Linear); } resultLabelSetImage->SetActiveLayer(inputLabelSetImage->GetActiveLayer()); resultLabelSetImage->GetLabelSet(inputLabelSetImage->GetActiveLayer())->SetActiveLabel(inputLabelSetImage->GetActiveLabel(inputLabelSetImage->GetActiveLayer())->GetValue()); result = resultLabelSetImage; } return result; } mitk::ImageMappingHelper::ResultImageType::Pointer mitk::ImageMappingHelper::map(const InputImageType* input, const MITKRegistrationType* registration, bool throwOnOutOfInputAreaError, const double& paddingValue, const ResultImageGeometryType* resultGeometry, bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type) { if (!registration) { mitkThrow() << "Cannot map image. Passed registration wrapper pointer is nullptr."; } if (!registration->GetRegistration()) { mitkThrow() << "Cannot map image. Passed registration wrapper containes no registration."; } if (!input) { mitkThrow() << "Cannot map image. Passed image pointer is nullptr."; } ResultImageType::Pointer result = map(input, registration->GetRegistration(), throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue); return result; } +mitk::ImageMappingHelper::ResultImageGeometryType::Pointer +mitk::ImageMappingHelper::GenerateSuperSampledGeometry(const ResultImageGeometryType* inputGeometry, double xScaling, double yScaling, double zScaling) +{ + auto resultGeometry = inputGeometry->Clone(); + + //change the pixel count and spacing of the geometry + mitk::BaseGeometry::BoundsArrayType geoBounds = inputGeometry->GetBounds(); + auto oldSpacing = inputGeometry->GetSpacing(); + mitk::Vector3D geoSpacing; + + geoSpacing[0] = oldSpacing[0] / xScaling; + geoSpacing[1] = oldSpacing[1] / yScaling; + geoSpacing[2] = oldSpacing[2] / zScaling; + + geoBounds[1] = geoBounds[1] * xScaling; + geoBounds[3] = geoBounds[3] * yScaling; + geoBounds[5] = geoBounds[5] * zScaling; + + resultGeometry->SetBounds(geoBounds); + resultGeometry->SetSpacing(geoSpacing); + + auto oldOrigin = inputGeometry->GetOrigin(); + + //if we change the spacing we must also correct the origin to ensure + //that the voxel matrix still covers the same space. This is due the fact + //that the origin is not in the corner of the voxel matrix, but in the center + // of the voxel that is in the corner. + mitk::Point3D newOrigin; + for (mitk::Point3D::SizeType i = 0; i < 3; ++i) + { + newOrigin[i] = 0.5 * (geoSpacing[i] - oldSpacing[i]) + oldOrigin[i]; + } + + return resultGeometry; +} + mitk::ImageMappingHelper::ResultImageType::Pointer mitk::ImageMappingHelper:: - refineGeometry(const InputImageType* input, const RegistrationType* registration, - bool throwOnError) + refineGeometry(const InputImageType * input, const RegistrationType * registration, + bool throwOnError) { mitk::ImageMappingHelper::ResultImageType::Pointer result = nullptr; if (!registration) { mitkThrow() << "Cannot refine image geometry. Passed registration pointer is nullptr."; } if (!input) { mitkThrow() << "Cannot refine image geometry. Passed image pointer is nullptr."; } - mitk::MITKRegistrationHelper::Affine3DTransformType::Pointer spTransform = mitk::MITKRegistrationHelper::getAffineMatrix(registration,false); - if(spTransform.IsNull() && throwOnError) + mitk::MITKRegistrationHelper::Affine3DTransformType::Pointer spTransform = mitk::MITKRegistrationHelper::getAffineMatrix(registration, false); + if (spTransform.IsNull() && throwOnError) { mitkThrow() << "Cannot refine image geometry. Registration does not contain a suitable direct mapping kernel (3D affine transformation or compatible required)."; } - if(spTransform.IsNotNull()) + if (spTransform.IsNotNull()) { //copy input image result = input->Clone(); //refine geometries - for(unsigned int i = 0; i < result->GetTimeSteps(); ++i) + for (unsigned int i = 0; i < result->GetTimeSteps(); ++i) { //refine every time step result->GetGeometry(i)->Compose(spTransform); } result->GetTimeGeometry()->Update(); } return result; } + + mitk::ImageMappingHelper::ResultImageType::Pointer mitk::ImageMappingHelper:: refineGeometry(const InputImageType* input, const MITKRegistrationType* registration, bool throwOnError) { if (!registration) { mitkThrow() << "Cannot refine image geometry. Passed registration wrapper pointer is nullptr."; } if (!registration->GetRegistration()) { mitkThrow() << "Cannot refine image geometry. Passed registration wrapper containes no registration."; } if (!input) { mitkThrow() << "Cannot refine image geometry. Passed image pointer is nullptr."; } ResultImageType::Pointer result = refineGeometry(input, registration->GetRegistration(), throwOnError); return result; } bool mitk::ImageMappingHelper:: canRefineGeometry(const RegistrationType* registration) { bool result = true; if (!registration) { mitkThrow() << "Cannot check refine capability of registration. Passed registration pointer is nullptr."; } //if the helper does not return null, we can refine the geometry. result = mitk::MITKRegistrationHelper::getAffineMatrix(registration,false).IsNotNull(); return result; } bool mitk::ImageMappingHelper:: canRefineGeometry(const MITKRegistrationType* registration) { if (!registration) { mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper pointer is nullptr."; } if (!registration->GetRegistration()) { mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper containes no registration."; } return canRefineGeometry(registration->GetRegistration()); } diff --git a/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp b/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp index 2ad231a33d..b029d62d78 100644 --- a/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp +++ b/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp @@ -1,159 +1,169 @@ /*============================================================================ 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. ============================================================================*/ #include "mitkMAPRegistrationWrapper.h" #include #include mitk::MAPRegistrationWrapper::MAPRegistrationWrapper(map::core::RegistrationBase* registration) : m_spRegistration(registration) { if (registration == nullptr) { mitkThrow() << "Error. Cannot create MAPRegistrationWrapper with invalid registration instance (nullptr)."; } Identifiable::SetUID(registration->getRegistrationUID()); } mitk::MAPRegistrationWrapper::~MAPRegistrationWrapper() { } void mitk::MAPRegistrationWrapper::SetRequestedRegionToLargestPossibleRegion() { //nothing to do } bool mitk::MAPRegistrationWrapper::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::MAPRegistrationWrapper::VerifyRequestedRegion() { return true; } +bool mitk::MAPRegistrationWrapper::IsEmptyTimeStep(unsigned int /*t*/) const +{ + return m_spRegistration.IsNull(); +} + +bool mitk::MAPRegistrationWrapper::IsEmpty() const +{ + return m_spRegistration.IsNull(); +} + void mitk::MAPRegistrationWrapper::SetRequestedRegion(const itk::DataObject*) { //nothing to do } unsigned int mitk::MAPRegistrationWrapper::GetMovingDimensions() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return moving dimension. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getMovingDimensions(); } unsigned int mitk::MAPRegistrationWrapper::GetTargetDimensions() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return target dimension. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getTargetDimensions(); } const mitk::MAPRegistrationWrapper::TagMapType& mitk::MAPRegistrationWrapper::GetTags() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return registration tags. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getTags(); } bool mitk::MAPRegistrationWrapper::GetTagValue(const TagType & tag, ValueType & value) const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return registration tag value. Wrapper points to invalid registration (nullptr). Tag: " << tag; } return m_spRegistration->getTagValue(tag,value); } bool mitk::MAPRegistrationWrapper::HasLimitedTargetRepresentation() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot determin HasLimitedTargetRepresentation(). Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->hasLimitedTargetRepresentation(); } bool mitk::MAPRegistrationWrapper::HasLimitedMovingRepresentation() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot determin HasLimitedMovingRepresentation(). Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->hasLimitedMovingRepresentation(); } map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration() { return m_spRegistration; } const map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration() const { return m_spRegistration; } void mitk::MAPRegistrationWrapper::PrintSelf (std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); if (m_spRegistration.IsNull()) { os<< "Error. Wrapper points to invalid registration (nullptr)."; } else { os<Print(os,indent.GetNextIndent()); typedef map::core::Registration<3,3> CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); os<getDirectMapping().Print(os,indent.GetNextIndent()); os<getInverseMapping().Print(os,indent.GetNextIndent()); } } void mitk::MAPRegistrationWrapper::SetUID(const UIDType& uid) { if (m_spRegistration.IsNull()) { mitkThrow() << "Error. Cannot set UID. Wrapper points to invalid registration (nullptr)."; } Identifiable::SetUID(uid); ::map::core::RegistrationBaseManipulator manip(m_spRegistration); manip.getTagValues()[::map::tags::RegistrationUID] = uid; }; mitk::Identifiable::UIDType mitk::MAPRegistrationWrapper::GetUID() const { if (m_spRegistration.IsNull()) { mitkThrow() << "Error. Cannot return UID. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getRegistrationUID(); }; diff --git a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp index 85044298f9..3026cd148c 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp @@ -1,584 +1,559 @@ /*============================================================================ 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. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_mapper_Activator.h" // Blueberry #include #include // Mitk #include #include #include "mitkImageMappingHelper.h" #include "mitkMAPRegistrationWrapper.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegistrationHelper.h" #include #include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPointMapper.h" // Qt #include #include #include #include const std::string QmitkMatchPointMapper::VIEW_ID = "org.mitk.views.matchpoint.mapper"; QmitkMatchPointMapper::QmitkMatchPointMapper() : m_Parent(nullptr), m_preparedForBinaryInput(false) { } void QmitkMatchPointMapper::SetFocus() { //m_Controls.buttonPerformImageProcessing->setFocus(); } void QmitkMatchPointMapper::CreateConnections() { connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnRegNodeSelectionChanged); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnInputNodeSelectionChanged); connect(m_Controls.referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnReferenceNodeSelectionChanged); connect(m_Controls.m_cbManualRef, SIGNAL(clicked()), this, SLOT(OnManualRefChecked())); connect(m_Controls.m_cbLinkFactors, SIGNAL(clicked()), this, SLOT(OnLinkSampleFactorChecked())); connect(m_Controls.m_sbXFactor, SIGNAL(valueChanged(double)), this, SLOT(OnXFactorChanged(double))); connect(m_Controls.m_pbMap, SIGNAL(clicked()), this, SLOT(OnMapBtnPushed())); connect(m_Controls.m_pbRefine, SIGNAL(clicked()), this, SLOT(OnRefineBtnPushed())); } void QmitkMatchPointMapper::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QStringLiteral("") + msg + QStringLiteral("")); } void QmitkMatchPointMapper::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(true); this->m_Controls.inputNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.inputNodeSelector->SetSelectionIsOptional(false); this->m_Controls.referenceNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.referenceNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration."); this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity mapping. Select registration to change."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used for the mapping of the input data. If no registration is selected, identity will be assumed for the mapping."); this->m_Controls.inputNodeSelector->SetInvalidInfo("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpTitel("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpHint("Select the input data for the mapping. (Images or point sets are supported so far)."); this->m_Controls.referenceNodeSelector->SetInvalidInfo("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpTitel("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpHint("Select the reference image that specifies the target geometrie the input should be mapped into."); this->ConfigureRegNodePredicate(); this->ConfigureNodePredicates(); // show first page m_Controls.m_tabs->setCurrentIndex(0); this->CreateConnections(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureMappingControls(); } /** Method checks if the currently selected reg node has a direct kernel that * can be decomposed in a rotation matrix and a offset. If this is true, true * is returned. In all other cases false is returned.*/ bool QmitkMatchPointMapper::IsAbleToRefineGeometry() const { bool result = false; if (this->m_spSelectedRegNode.IsNotNull()) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (this->m_spSelectedRegNode->GetData()); //if the helper does not return null, we can refine the geometry. result = mitk::MITKRegistrationHelper::getAffineMatrix(wrapper, false).IsNotNull(); } return result; } bool QmitkMatchPointMapper::IsBinaryInput() const { auto maskPredicate = mitk::MITKRegistrationHelper::MaskNodePredicate(); bool result = false; if(this->m_spSelectedInputNode.IsNotNull()) { result = maskPredicate->CheckNode(this->m_spSelectedInputNode); } return result; } bool QmitkMatchPointMapper::IsPointSetInput() const { bool result = false; if (this->m_spSelectedInputNode.IsNotNull()) { result = dynamic_cast(this->m_spSelectedInputNode->GetData()) != nullptr; } return result; } mitk::DataNode::Pointer QmitkMatchPointMapper::GetAutoRefNodeByReg() { mitk::DataNode::Pointer spResult = nullptr; if (this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedRegNode->GetData()) { std::string nodeName; mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); spResult = this->GetDataStorage()->GetNode(predicate); } } if (spResult.IsNull() && this->m_spSelectedInputNode.IsNotNull()) { //no really reference is available -> use the input as reference spResult = this->m_spSelectedInputNode; if (this->m_spSelectedRefNode != spResult) { m_Controls.m_teLog->append( QStringLiteral("Cannot determine reference automatically. Use input image as reference.")); } } return spResult; } void QmitkMatchPointMapper::ConfigureRegNodePredicate(const mitk::DataNode* input) { mitk::NodePredicateBase::ConstPointer nodePredicate = mitk::MITKRegistrationHelper::RegNodePredicate(); if (input != nullptr) { unsigned int dimension = 0; auto inputImage = dynamic_cast(input->GetData()); auto pointset = dynamic_cast(input->GetData()); if (inputImage) { dimension = inputImage->GetDimension(); if (inputImage->GetTimeSteps() > 1) { //images has multiple time steps -> remove one dimension. dimension -= 1; } } else if (pointset) { dimension = 3; } auto dimCheck = [dimension](const mitk::DataNode * node) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast < const mitk::MAPRegistrationWrapper* >(node->GetData()); return wrapper != nullptr && wrapper->GetMovingDimensions() == dimension; }; mitk::NodePredicateFunction::Pointer hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); nodePredicate = mitk::NodePredicateAnd::New(nodePredicate, hasCorrectDim).GetPointer(); } this->m_Controls.registrationNodeSelector->SetNodePredicate(nodePredicate); } std::function GenerateDimCheckLambda(unsigned int dim) { auto dimCheck = [dim](const mitk::DataNode * node) { auto inputImage = dynamic_cast(node->GetData()); return inputImage != nullptr && (inputImage->GetDimension() == dim || (inputImage->GetDimension() == dim + 1 && inputImage->GetTimeSteps()>1)); }; return dimCheck; } void QmitkMatchPointMapper::ConfigureNodePredicates(const mitk::DataNode* reg) { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate(); auto isData = mitk::NodePredicateOr::New(isImage, isPointSet); mitk::NodePredicateBase::ConstPointer inputPredicate = isData.GetPointer(); mitk::NodePredicateBase::ConstPointer refPredicate = isImage.GetPointer(); if (reg != nullptr) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (reg->GetData()); if (wrapper != nullptr) { auto movingDim = wrapper->GetMovingDimensions(); auto dimCheck = GenerateDimCheckLambda(movingDim); auto hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); if (movingDim == 3) { //Remark: Point sets are always 3D auto is3DInput = mitk::NodePredicateOr::New(isPointSet, mitk::NodePredicateAnd::New(isImage, hasCorrectDim)); inputPredicate = is3DInput.GetPointer(); } else { auto is2DInput = mitk::NodePredicateAnd::New(isImage, hasCorrectDim); inputPredicate = is2DInput.GetPointer(); } auto targetDim = wrapper->GetTargetDimensions(); auto targetDimCheck = GenerateDimCheckLambda(targetDim); auto hasCorrectTargetDim = mitk::NodePredicateFunction::New(targetDimCheck); auto isRef = mitk::NodePredicateAnd::New(isImage, hasCorrectTargetDim); refPredicate = isRef; } } this->m_Controls.inputNodeSelector->SetNodePredicate(inputPredicate); this->m_Controls.referenceNodeSelector->SetNodePredicate(refPredicate); } void QmitkMatchPointMapper::CheckInputs() { this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedInputNode = this->m_Controls.inputNodeSelector->GetSelectedNode(); this->m_spSelectedRefNode = this->m_Controls.referenceNodeSelector->GetSelectedNode(); if (!(m_Controls.m_cbManualRef->isChecked())) { auto autoRefNode = this->GetAutoRefNodeByReg(); if (this->m_spSelectedRefNode != autoRefNode) { this->m_spSelectedRefNode = autoRefNode; QmitkSingleNodeSelectionWidget::NodeList selection; if (this->m_spSelectedRefNode.IsNotNull()) { selection.append(this->m_spSelectedRefNode); } this->m_Controls.referenceNodeSelector->SetCurrentSelection(selection); } } if (this->m_spSelectedRefNode.IsNotNull() && this->m_spSelectedRefNode->GetData() && this->m_spSelectedRefNode->GetData()->GetTimeSteps() > 1) { m_Controls.m_teLog->append( QStringLiteral("Selected reference image has multiple time steps. Only geometry of time step 1 is used as reference.")); } } void QmitkMatchPointMapper::ConfigureMappingControls() { bool validInput = m_spSelectedInputNode.IsNotNull(); bool validRef = m_spSelectedRefNode.IsNotNull(); this->m_Controls.referenceNodeSelector->setEnabled(this->m_Controls.m_cbManualRef->isChecked()); this->m_Controls.m_pbMap->setEnabled(validInput && validRef); this->m_Controls.m_pbRefine->setEnabled(validInput && this->IsAbleToRefineGeometry() && !this->IsPointSetInput()); if (validInput) { if (m_spSelectedRegNode.IsNotNull()) { this->m_Controls.m_leMappedName->setText(tr("mapped_") + QString::fromStdString(m_spSelectedInputNode->GetName()) + tr("_by_") + QString::fromStdString(m_spSelectedRegNode->GetName())); } else { this->m_Controls.m_leMappedName->setText(tr("resampled_") + QString::fromStdString(m_spSelectedInputNode->GetName())); } } else { this->m_Controls.m_leMappedName->setText(tr("mappedData")); } if (this->IsBinaryInput() != this->m_preparedForBinaryInput) { if (this->IsBinaryInput()) { m_Controls.m_teLog->append( QStringLiteral("Binary input (mask) detected. Preparing for mask mapping (default interpolation: nearest neigbour; padding value: 0)")); this->m_Controls.m_comboInterpolator->setCurrentIndex(0); this->m_Controls.m_sbErrorValue->setValue(0); this->m_Controls.m_sbPaddingValue->setValue(0); } else { this->m_Controls.m_comboInterpolator->setCurrentIndex(1); } this->m_preparedForBinaryInput = this->IsBinaryInput(); } OnLinkSampleFactorChecked(); } void QmitkMatchPointMapper::ConfigureProgressInfos() { } void QmitkMatchPointMapper::OnRegNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer regNode; if (!nodes.isEmpty()) { regNode = nodes.front(); } this->ConfigureNodePredicates(regNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnInputNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer inputNode; if (!nodes.isEmpty()) { inputNode = nodes.front(); } this->ConfigureRegNodePredicate(inputNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnReferenceNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnManualRefChecked() { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnLinkSampleFactorChecked() { this->m_Controls.m_sbYFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); this->m_Controls.m_sbZFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(this->m_Controls.m_sbXFactor->value()); this->m_Controls.m_sbZFactor->setValue(this->m_Controls.m_sbXFactor->value()); } } void QmitkMatchPointMapper::OnMapBtnPushed() { SpawnMappingJob(); } void QmitkMatchPointMapper::OnRefineBtnPushed() { SpawnMappingJob(true); } void QmitkMatchPointMapper::SpawnMappingJob(bool doGeometryRefinement) { if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkMappingJob* pJob = new QmitkMappingJob(); pJob->setAutoDelete(true); pJob->m_spInputData = this->m_spSelectedInputNode->GetData(); pJob->m_InputDataUID = mitk::EnsureUID(this->m_spSelectedInputNode->GetData()); pJob->m_doGeometryRefinement = doGeometryRefinement; pJob->m_spRegNode = m_spSelectedRegNode; if (m_spSelectedRegNode.IsNull()) { pJob->m_spRegNode = mitk::DataNode::New(); pJob->m_spRegNode->SetData(mitk::GenerateIdentityRegistration3D().GetPointer()); pJob->m_spRegNode->SetName("Auto_Generated_Identity_Transform"); m_Controls.m_teLog->append( QStringLiteral("No registration selected. Preforming mapping with identity transform")); } if (!doGeometryRefinement) { pJob->m_spRefGeometry = m_spSelectedRefNode->GetData()->GetGeometry()->Clone().GetPointer(); //check for super/sub sampling if (m_Controls.m_groupActivateSampling->isChecked()) { - //change the pixel count and spacing of the geometry - mitk::BaseGeometry::BoundsArrayType geoBounds = pJob->m_spRefGeometry->GetBounds(); - auto oldSpacing = pJob->m_spRefGeometry->GetSpacing(); - mitk::Vector3D geoSpacing; - - geoSpacing[0] = oldSpacing[0] / m_Controls.m_sbXFactor->value(); - geoSpacing[1] = oldSpacing[1] / m_Controls.m_sbYFactor->value(); - geoSpacing[2] = oldSpacing[2] / m_Controls.m_sbZFactor->value(); - - geoBounds[1] = geoBounds[1] * m_Controls.m_sbXFactor->value(); - geoBounds[3] = geoBounds[3] * m_Controls.m_sbYFactor->value(); - geoBounds[5] = geoBounds[5] * m_Controls.m_sbZFactor->value(); - - pJob->m_spRefGeometry->SetBounds(geoBounds); - pJob->m_spRefGeometry->SetSpacing(geoSpacing); - - auto oldOrigin = pJob->m_spRefGeometry->GetOrigin(); - - //if we change the spacing we must also correct the origin to ensure - //that the voxel matrix still covers the same space. This is due the fact - //that the origin is not in the corner of the voxel matrix, but in the center - // of the voxel that is in the corner. - mitk::Point3D newOrigin; - for (mitk::Point3D::SizeType i = 0; i < 3; ++i) - { - newOrigin[i] = 0.5* (geoSpacing[i] - oldSpacing[i]) + oldOrigin[i]; - } - - pJob->m_spRefGeometry->SetOrigin(newOrigin); + pJob->m_spRefGeometry = mitk::ImageMappingHelper::GenerateSuperSampledGeometry(pJob->m_spRefGeometry, + m_Controls.m_sbXFactor->value(), + m_Controls.m_sbYFactor->value(), + m_Controls.m_sbZFactor->value()); } } pJob->m_MappedName = m_Controls.m_leMappedName->text().toStdString(); pJob->m_allowUndefPixels = m_Controls.m_groupAllowUndefPixels->isChecked(); pJob->m_paddingValue = m_Controls.m_sbPaddingValue->value(); pJob->m_allowUnregPixels = m_Controls.m_groupAllowUnregPixels->isChecked(); pJob->m_errorValue = m_Controls.m_sbErrorValue->value(); pJob->m_InterpolatorLabel = m_Controls.m_comboInterpolator->currentText().toStdString(); switch (m_Controls.m_comboInterpolator->currentIndex()) { case 0: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::NearestNeighbor; break; case 1: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; break; case 2: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::BSpline_3; break; case 3: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Hamming; break; case 4: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Welch; break; } connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString))); connect(pJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnMappingInfo(QString))); m_Controls.m_teLog->append(QStringLiteral("Started mapping job. Name: ") + m_Controls.m_leMappedName->text() + QStringLiteral("")); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPointMapper::OnMapJobError(QString err) { Error(err); } void QmitkMatchPointMapper::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { m_Controls.m_teLog->append(QStringLiteral("Mapped entity stored. Name: ") + QString::fromStdString(job->m_MappedName) + QStringLiteral("")); mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); auto* renderWindowPart = this->GetRenderWindowPart(); if (nullptr != renderWindowPart) renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnMappingInfo(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPointMapper::OnXFactorChanged(double d) { if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(d); this->m_Controls.m_sbZFactor->setValue(d); } }