-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sequential run of Elastix producing different result #338
Comments
Hi again. @N-Dekker, @mstaring maybe you guys have ideas regarding this? I also tried what @Chayan010 indicated in the previous post (also here more details: https://groups.google.com/g/elastix-imageregistration/c/0_36mGQrPv8) I tested in my system and the issue is very weird. The code that I uploaded in the repository https://github.com/rcorredorj/elastix-cpp-test shows exactly an example that reproduces this issue:
Here the differences in the transformation parameters: We'll appreciate your thoughts on this issue and thank you again for your support! Cheers, RaC |
I suspect that the seed we set in the executable is not set in the library. We will put it on our list of todo's ... |
note however that this is not very problematic behaviour, it is simply related to the randomness in the sampler used by the stochastic optimizer. Stochastic optimizers will walk to the same minimum for different seeds, up to some epsilon. You observe the epsilon here. |
@mstaring Thanks again for your answer. |
@mstaring Your suspect is right, that the problem is caused by the use of the RandomSampler in combination with the itk random number generator (MersenneTwisterRandomVariateGenerator). In order to get reproducable registration results in subsequent elastixLib::RegisterImages() usages we would need to:
Maybe you can give advice how to use the MinimumStepSize stop condition? Regarding the reset of the random number generator before every use of RegisterImages(): Your guess that just the initialization of the MersenneTwisterRandomVariateGenerator with specific seed is missing in elastixLib scenario is partly true. In fact that seed initialization is already executed in ealstixLib scenario (within ElastixBase::BeforeAllBase) but it is executed a bit to late because the MersenneTwisterRandomVariateGenerator singleton is already accessed during component instantiation. But that is not the only problem. The real problem is, that elastix uses the MersenneTwisterRandomVariateGenerator singleton in 2 different ways ~ as random number generator and as seed generator ~. |
Great analysis, thank you! Regarding the minimum stepsize, that parameter is most relevant for the non-stochastic optimizers. For stochastic ones there will be noise on the step size and it is perhaps better to look at a moving average. But that is not currently implemented in elastix, so we always use the max number of iterations. Regarding the seed, good to hear that it is actually called in the lib as well. Do I understand correctly that in the lib, because the object itself stays alive unlike the command line version, the seed is the same between two consecutive runs, but this increment you mention is not? |
Thanks for the accolade. Sad to hear that a quality based stop condition is no option. Yes you understood correctly. So in lib scenario with every new run of Elastix registration the Seed value within the MersenneTwisterRandomVariateGenerator Singleton is the same (due the fix in ElastixBase::BeforeAllBase) and hence that Singleton produces same random number sequence as in first run. But all the seed values fetched from that Singleton will be much higher than with the first run. You might not be aware to use that GetNextSeed() function because you just inheritate that usage via the itk::ImageRandomConstIteratorWithIndex. Every new instance of this itk class creates a new instance of the MersenneTwisterRandomVariateGenerator and initializes that new instance with the next seed delivered from the global Singleton instance of that same random generator class. So these random generators (which are used to generate the random sample path through the images) will produce different random numbers in every new run beause the seed value will increase with eyery run. This is how GetNextSeed() is implemented:
As already mentioned m_StaticDiffer is defined as private static. So there is no way to access it and reset it from outside. The best and cleanest solution would be to change the itk code, means to extend the MersenneTwisterRandomVariateGenerator with a method that allows to reset that increment counter. Another but not so clean fix could be to change the Elastix implementation in AdaptiveStochasticGradientDescent and to modify/extend the random seed fix in ElastixBase::BeforeAllBase. The idea behind that possible fix would take longer to explain. You can call me back if interested in details. |
Then indeed I would suggest as a first step to talk to the itk people if the increment counter could be exposed by adding set and get functions to this class, or simply a ResetStaticDiffer() function. Would be an easy addition, if they agree this would be useful and does not break something. In my experience they are very welcoming to new ideas and code enhancements. |
Hi @N-Dekker, Any additional suggestion is very much appreciated. |
Dear @mstaring and Elastix-ers, We have a sort of related question to this thread. When we run elastix from C++, we get the transformation parameters with a high precision number, a float number as expected. Do you have a better understanding of how this is managed for the TransformParameters to get from C++ the same values we get in the txt file ? Thanks in advance! |
@rcorredorj Hi Ricardo, thanks for elaborating on this issue. Did you already try the latest version from the "develop" branch. Because it should ensure that floating point transform parameters now always have the full precision of a 64-bit
HTH, Niels |
@N-Dekker just submitted a PR related to this: Do you think this may be useful for this issue as well? |
@hankst69 @rcorredorj @Chayan010 @mstaring This week I finally proposed a function for ITK's (Please let me know if you would prefer to name it "ResetStaticDiffer()".) Sorry for being so late! The function is now merged into the master branch of ITK (InsightSoftwareConsortium/ITK@ecd7bda), and is intended to be included with the next major ITK release (v5.4). Thanks to you all for your feedback! |
Hi,
I was trying to run Elastix more than once in sequence with two different fixed and moving images. The result of first run is fine but for second run the output is not same, and even the transformation matrix is different
Even I have tried passing same fixed and moving image to check but second result is different.
Kindly suggest.
Below is the model code I have included in my Code:
#include
#include
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkRoundImageFilter.h"
#include "itkImage.h"
#include "itkCastImageFilter.h"
#include "itkParameterMapInterface.h"
#include "itkParameterFileParser.h"
#include "elastixlib.h"
typedef itk::ParameterFileParser ParameterFileParserType;
std::map < std::string, std::vector< std::string > > ParameterFileParser();
void test1();
void test2();
int main(int argc, char* argv[])
{
test1();
test2();
}
void test1()
{
const unsigned int dimension = 3;
typedef float InputPixelType;
typedef float OutputPixelType;
typedef int IntPixelType;
typedef itk::Image<InputPixelType, dimension> InputImageType;
typedef itk::Image<OutputPixelType, dimension> OutputImageType;
typedef itk::Image<IntPixelType, dimension> IntImageType;
typedef itk::ImageFileReader ReaderType;
typedef itk::ImageFileWriter FloatWriterType;
typedef itk::ImageFileWriter UShortWriterType;
typedef itk::RoundImageFilter<OutputImageType, OutputImageType> RoundFilterType;
}
void test2()
{
const unsigned int dimension = 3;
typedef float InputPixelType;
typedef float OutputPixelType;
typedef int IntPixelType;
typedef itk::Image<InputPixelType, dimension> InputImageType;
typedef itk::Image<OutputPixelType, dimension> OutputImageType;
typedef itk::Image<IntPixelType, dimension> IntImageType;
typedef itk::ImageFileReader ReaderType;
typedef itk::ImageFileWriter FloatWriterType;
typedef itk::ImageFileWriter UShortWriterType;
typedef itk::RoundImageFilter<OutputImageType, OutputImageType> RoundFilterType;
}
std::map < std::string, std::vector< std::string > > ParameterFileParser()
{
std::map < std::string, std::vector< std::string > > parameters;
constexpr auto ImageDimension = 3;
}
regards,
Chayan
Below is the link to the issue:
https://groups.google.com/g/elastix-imageregistration/c/0_36mGQrPv8
The text was updated successfully, but these errors were encountered: