Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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("\\/");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does itksys not have a robust function for what you are trying to do here?

Copy link
Member Author

@N-Dekker N-Dekker Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does itksys not have a robust function for what you are trying to do here?

Good question 😃 I found some itksys functions that do similar things, by splitting a path to its components, and then joining them again:

  static std::vector<std::string> SplitString(const std::string& s,
                                              char separator = '/',
                                              bool isPath = false);
  static std::string Join(const std::vector<std::string>& list,
                          const std::string& separator);

But as far as I can see, it involves extra manual programming, it doesn't get much easier than what it does now: Just find the last separator (slash or backslash) in the current transform file path (by std::string::find_last_of), and then concatenate the current path up to that last separator with the string specified by "InitialTransformParameterFileName".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, just at least add a comment explaining what you do here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, thanks! I just added some more comment, and also made the code clearer by introducing a local variable, isAbsoluteFilePath. Hope it's clear enough now!

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)