diff --git a/Core/ComponentBaseClasses/elxTransformBase.hxx b/Core/ComponentBaseClasses/elxTransformBase.hxx index 7bedd5dae..0532acd01 100644 --- a/Core/ComponentBaseClasses/elxTransformBase.hxx +++ b/Core/ComponentBaseClasses/elxTransformBase.hxx @@ -388,16 +388,43 @@ TransformBase::ReadFromFile() * is not the same as this transform parameter file. Otherwise, * we will have an infinite loop. */ - std::string fullFileName1 = itksys::SystemTools::CollapseFullPath(fileName); - std::string fullFileName2 = itksys::SystemTools::CollapseFullPath(configuration.GetParameterFileName()); - if (fullFileName1 == fullFileName2) + + const std::string configurationParameterFileName = configuration.GetParameterFileName(); + + if (itksys::SystemTools::CollapseFullPath(fileName) == + itksys::SystemTools::CollapseFullPath(configurationParameterFileName)) { itkExceptionMacro("ERROR: The InitialTransformParameterFileName is identical to the current " "TransformParameters filename! An infinite loop is not allowed."); } /** We can safely read the initial transform. */ - this->ReadInitialTransformFromFile(fileName.c_str()); + + // Find the last separator (slash or backslash) in the current transform parameter file path. + const auto lastConfigurationParameterFilePathSeparator = configurationParameterFileName.find_last_of("\\/"); + const char firstFileNameLetter = fileName.front(); + + if (const bool isAbsoluteFilePath{ firstFileNameLetter == '\\' || firstFileNameLetter == '/' || + (firstFileNameLetter > 0 && std::isalpha(firstFileNameLetter) && + fileName.size() > 1 && fileName[1] == ':') }; + isAbsoluteFilePath || (lastConfigurationParameterFilePathSeparator == std::string::npos) || + itksys::SystemTools::FileExists(fileName)) + { + // The file name is an absolute path, or the current transform parameter file name does not have any separator, + // or the file exists in the current working directory. So use it! + this->ReadInitialTransformFromFile(fileName.c_str()); + } + else + { + // The file name of the initial transform is a relative path, so now assume that it is relative to the current + // transform parameter file (the current configuration). Try to read the initial transform from the same + // directory as the current transform, by concatenating the current configuration file path up to that last + // separator with the string specified by "InitialTransformParameterFileName". + this->ReadInitialTransformFromFile( + configurationParameterFileName.substr(0, lastConfigurationParameterFilePathSeparator + 1) + .append(fileName) + .c_str()); + } } } diff --git a/Core/Main/GTesting/itkElastixRegistrationMethodGTest.cxx b/Core/Main/GTesting/itkElastixRegistrationMethodGTest.cxx index fda506f6e..44d03f483 100644 --- a/Core/Main/GTesting/itkElastixRegistrationMethodGTest.cxx +++ b/Core/Main/GTesting/itkElastixRegistrationMethodGTest.cxx @@ -976,6 +976,62 @@ GTEST_TEST(itkElastixRegistrationMethod, InitialTransformParameterFile) } +GTEST_TEST(itkElastixRegistrationMethod, InitialTransformParameterFileWithInitialTransformParameterFile) +{ + using PixelType = float; + enum + { + ImageDimension = 2U + }; + using ImageType = itk::Image; + + const auto doDummyRegistration = + [](const std::string & initialTransformParameterFileName) -> itk::SmartPointer { + const ImageType::SizeType imageSize{ { 5, 6 } }; + + std::mt19937 randomNumberEngine{}; + + const auto fillImageBufferRandomly = [&randomNumberEngine](ImageType & image) { + const itk::ImageBufferRange imageBufferRange(image); + + std::generate(imageBufferRange.begin(), imageBufferRange.end(), [&randomNumberEngine] { + return std::uniform_real_distribution{ PixelType{ 1 }, PixelType{ 2 } }(randomNumberEngine); + }); + }; + + elx::DefaultConstruct fixedImage{}; + fixedImage.SetRegions(imageSize); + fixedImage.Allocate(true); + fillImageBufferRandomly(fixedImage); + + elx::DefaultConstruct movingImage{}; + movingImage.SetRegions(imageSize); + movingImage.Allocate(true); + fillImageBufferRandomly(movingImage); + + elx::DefaultConstruct> registration{}; + registration.SetFixedImage(&fixedImage); + registration.SetMovingImage(&movingImage); + registration.SetInitialTransformParameterFileName(initialTransformParameterFileName); + + registration.SetParameterObject(CreateParameterObject({ // Parameters in alphabetic order: + { "AutomaticTransformInitialization", "false" }, + { "ImageSampler", "Full" }, + { "MaximumNumberOfIterations", "0" }, + { "Metric", "AdvancedNormalizedCorrelation" }, + { "Optimizer", "AdaptiveStochasticGradientDescent" }, + { "Transform", "TranslationTransform" } })); + registration.Update(); + return registration.GetOutput(); + }; + + EXPECT_EQ( + DerefSmartPointer(doDummyRegistration( + GetDataDirectoryPath() + "/Translation(1,-2)/TransformParametersWithInitialTransformParameterFile.txt")), + DerefSmartPointer(doDummyRegistration(GetDataDirectoryPath() + "/Translation(1,-2)/TransformParameters.txt"))); +} + + GTEST_TEST(itkElastixRegistrationMethod, SetInitialTransformParameterObject) { using PixelType = float; diff --git a/Testing/Data/Translation(1,-2)/InitialTransformParameters.txt b/Testing/Data/Translation(1,-2)/InitialTransformParameters.txt new file mode 100644 index 000000000..246cb2114 --- /dev/null +++ b/Testing/Data/Translation(1,-2)/InitialTransformParameters.txt @@ -0,0 +1,4 @@ +(InitialTransformParameterFileName "NoInitialTransform") +(NumberOfParameters 2) +(Transform "TranslationTransform") +(TransformParameters 1 0) \ No newline at end of file diff --git a/Testing/Data/Translation(1,-2)/TransformParametersWithInitialTransformParameterFile.txt b/Testing/Data/Translation(1,-2)/TransformParametersWithInitialTransformParameterFile.txt new file mode 100644 index 000000000..86185b509 --- /dev/null +++ b/Testing/Data/Translation(1,-2)/TransformParametersWithInitialTransformParameterFile.txt @@ -0,0 +1,4 @@ +(InitialTransformParameterFileName "InitialTransformParameters.txt") +(NumberOfParameters 2) +(Transform "TranslationTransform") +(TransformParameters 0 -2) \ No newline at end of file