Skip to content

Commit

Permalink
ENH: Allow InitialTransformParameterFileName relative to parameter file
Browse files Browse the repository at this point in the history
Allow the file name specified by the "InitialTransformParameterFileName" parameter in a transform parameter file to be relative to that transform parameter file.
  • Loading branch information
N-Dekker committed Jun 8, 2023
1 parent 09cdfe9 commit a7ae46e
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 4 deletions.
35 changes: 31 additions & 4 deletions Core/ComponentBaseClasses/elxTransformBase.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -388,16 +388,43 @@ TransformBase<TElastix>::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());
}
}
}

Expand Down
56 changes: 56 additions & 0 deletions Core/Main/GTesting/itkElastixRegistrationMethodGTest.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,62 @@ GTEST_TEST(itkElastixRegistrationMethod, InitialTransformParameterFile)
}


GTEST_TEST(itkElastixRegistrationMethod, InitialTransformParameterFileWithInitialTransformParameterFile)
{
using PixelType = float;
enum
{
ImageDimension = 2U
};
using ImageType = itk::Image<PixelType, ImageDimension>;

const auto doDummyRegistration =
[](const std::string & initialTransformParameterFileName) -> itk::SmartPointer<ImageType> {
const ImageType::SizeType imageSize{ { 5, 6 } };

std::mt19937 randomNumberEngine{};

const auto fillImageBufferRandomly = [&randomNumberEngine](ImageType & image) {
const itk::ImageBufferRange<ImageType> imageBufferRange(image);

std::generate(imageBufferRange.begin(), imageBufferRange.end(), [&randomNumberEngine] {
return std::uniform_real_distribution<PixelType>{ PixelType{ 1 }, PixelType{ 2 } }(randomNumberEngine);
});
};

elx::DefaultConstruct<ImageType> fixedImage{};
fixedImage.SetRegions(imageSize);
fixedImage.Allocate(true);
fillImageBufferRandomly(fixedImage);

elx::DefaultConstruct<ImageType> movingImage{};
movingImage.SetRegions(imageSize);
movingImage.Allocate(true);
fillImageBufferRandomly(movingImage);

elx::DefaultConstruct<ElastixRegistrationMethodType<ImageType>> 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;
Expand Down
4 changes: 4 additions & 0 deletions Testing/Data/Translation(1,-2)/InitialTransformParameters.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(InitialTransformParameterFileName "NoInitialTransform")
(NumberOfParameters 2)
(Transform "TranslationTransform")
(TransformParameters 1 0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(InitialTransformParameterFileName "InitialTransformParameters.txt")
(NumberOfParameters 2)
(Transform "TranslationTransform")
(TransformParameters 0 -2)

0 comments on commit a7ae46e

Please sign in to comment.