# Similarity measure

**URL:** <https://discourse.itk.org/t/similarity-measure/4680>\
**Category:** Uncategorized\
**Created:** [December 17, 2021, 12:34pm UTC](https://discourse.itk.org/t/similarity-measure/4680 "2021-12-17T12:34:25Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![wangjun456](https://discourse.itk.org/user_avatar/discourse.itk.org/wangjun456/32/2409_2.png) [@wangjun456](https://discourse.itk.org/u/wangjun456)\
**Post date:** [December 17, 2021, 12:34pm UTC](https://discourse.itk.org/t/similarity-measure/4680/1 "2021-12-17T12:34:25Z")

</div>

I want to combine two similarity measures in ITK. What should I do?

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [December 17, 2021, 4:44pm UTC](https://discourse.itk.org/t/similarity-measure/4680/2 "2021-12-17T16:44:09Z")

</div>

Evaluate both separately, then combine them using your formula. E.g.

```auto
auto metric1 = ...
auto metric2 = ...
double combinedMetric = metric1*0.2 + metric2*0.8;

```

---

<div class="post-metadata">

**Author:** ![wangjun456](https://discourse.itk.org/user_avatar/discourse.itk.org/wangjun456/32/2409_2.png) [@wangjun456](https://discourse.itk.org/u/wangjun456)\
**Post date:** [December 21, 2021, 7:47am UTC](https://discourse.itk.org/t/similarity-measure/4680/3 "2021-12-21T07:47:28Z")

</div>

I tried unsuccessfully. Do I have to write mymetric Mymetric.h and Mymetric.hxx integrates the two similarity measures?

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [December 21, 2021, 7:06pm UTC](https://discourse.itk.org/t/similarity-measure/4680/4 "2021-12-21T19:06:18Z")

</div>

I don’t think you have to write a new metric class. There is [ObjectToObjectMultiMetricv4](https://itk.org/Doxygen/html/classitk_1_1ObjectToObjectMultiMetricv4.html) which is worth trying if a simple combination didn’t work.

Can you share your attempt?

---

<div class="post-metadata">

**Author:** ![wangjun456](https://discourse.itk.org/user_avatar/discourse.itk.org/wangjun456/32/2409_2.png) [@wangjun456](https://discourse.itk.org/u/wangjun456)\
**Post date:** [December 22, 2021, 2:31pm UTC](https://discourse.itk.org/t/similarity-measure/4680/5 "2021-12-22T14:31:09Z")

</div>

```auto
class CommandIterationUpdate : public itk::Command
{
public:
    using Self = CommandIterationUpdate;
    using Superclass = itk::Command;
    using Pointer = itk::SmartPointer<Self>;
    itkNewMacro(Self);

protected:
    CommandIterationUpdate() = default;

public:
    /*using OptimizerType = itk::PowellOptimizer;*/
    using OptimizerType = itk::GradientDescentOptimizer;
    using OptimizerPointer = const OptimizerType*;

    void
        Execute(itk::Object* caller, const itk::EventObject& event) override
    {
        Execute((const itk::Object*)caller, event);
    }

    void
        Execute(const itk::Object* object, const itk::EventObject& event) override
    {
        auto optimizer = dynamic_cast<OptimizerPointer>(object);
        auto optimizer1 = dynamic_cast<OptimizerPointer>(object);
        if (!itk::IterationEvent().CheckEvent(&event))
        {
            return;
        }
        std::cout << optimizer->GetCurrentIteration() << " ";
        std::cout << optimizer->GetValue()*0.5 + optimizer1->GetValue() * 0.5 << " ";
        std::cout << optimizer->GetCurrentPosition() << std::endl;
    }
};

int
main(int argc, char* argv[])
{
  
    const char* fixedImageFile ="BrainProtonDensitySliceShifted13x17y.png";
    const char* movingImageFile = "BrainT1SliceBorder20.png";
    const char* outputImageFile = "output.png";
    const char* checkerBoardBefore = "before.png";
    const char* checkerBoardAfter = "after.png";

    constexpr unsigned int Dimension = 2;
    using PixelType = float;

    using FixedImageType = itk::Image<PixelType, Dimension>;
    using MovingImageType = itk::Image<PixelType, Dimension>;

    // It is convenient to work with an internal image type because mutual
    // information will perform better on images with a normalized statistical
    // distribution. The fixed and moving images will be normalized and
    // converted to this internal type.
    using InternalPixelType = float;
    using InternalImageType = itk::Image<InternalPixelType, Dimension>;

    using TransformType = itk::TranslationTransform<double, Dimension>;
    using OptimizerType = itk::GradientDescentOptimizer;
   /* using OptimizerType = itk::PowellOptimizer;*/
    using InterpolatorType = itk::LinearInterpolateImageFunction<InternalImageType, double>;
    using RegistrationType = itk::ImageRegistrationMethod<InternalImageType, InternalImageType>;
    using RegistrationType1 = itk::ImageRegistrationMethod<InternalImageType, InternalImageType>;
    using MetricType = itk::MutualInformationImageToImageMetric<InternalImageType, InternalImageType>;
    using MetricType1 = itk::GradientDifferenceImageToImageMetric<InternalImageType, InternalImageType>;
   /* using MetricType = itk::NormalizedMutualInformationImageToImageMetric<InternalImageType, InternalImageType>;*/

    TransformType::Pointer transform = TransformType::New();
    OptimizerType::Pointer optimizer = OptimizerType::New();
    OptimizerType::Pointer optimizer1 = OptimizerType::New();
    InterpolatorType::Pointer interpolator = InterpolatorType::New();
    RegistrationType::Pointer registration = RegistrationType::New();
    RegistrationType1::Pointer registration1 = RegistrationType::New();
    MetricType::Pointer metric = MetricType::New();
    MetricType1::Pointer metric1 = MetricType1::New();

    registration->SetOptimizer(optimizer);
    registration->SetTransform(transform);
    registration->SetInterpolator(interpolator);
    registration->SetMetric(metric);

    registration1->SetOptimizer(optimizer);
    registration1->SetTransform(transform);
    registration1->SetInterpolator(interpolator);
    metric1->SetDerivativeDelta(0.5);
    registration1->SetMetric(metric1);
 

    // The metric requires a number of parameters to be selected, including
    // the standard deviation of the Gaussian kernel for the fixed image
    // density estimate, the standard deviation of the kernel for the moving
    // image density and the number of samples use to compute the densities
    // and entropy values. Experience has
    // shown that a kernel standard deviation of 0.4 works well for images
    // which have been normalized to a mean of zero and unit variance. We
    // will follow this empirical rule in this example.
    metric->SetFixedImageStandardDeviation(0.4);
    metric->SetMovingImageStandardDeviation(0.4);

    using FixedImageReaderType = itk::ImageFileReader<FixedImageType>;
    using MovingImageReaderType = itk::ImageFileReader<MovingImageType>;

    FixedImageReaderType::Pointer fixedImageReader = FixedImageReaderType::New();
    MovingImageReaderType::Pointer movingImageReader = MovingImageReaderType::New();

    fixedImageReader->SetFileName(movingImageFile);
    movingImageReader->SetFileName(movingImageFile);

    registration1->SetFixedImage(fixedImageReader->GetOutput());
    registration1->SetMovingImage(movingImageReader->GetOutput());
    registration1->SetFixedImageRegion(
        fixedImageReader->GetOutput()->GetBufferedRegion());

    using FixedNormalizeFilterType = itk::NormalizeImageFilter<FixedImageType, InternalImageType>;

    using MovingNormalizeFilterType = itk::NormalizeImageFilter<MovingImageType, InternalImageType>;

    FixedNormalizeFilterType::Pointer fixedNormalizer = FixedNormalizeFilterType::New();

    MovingNormalizeFilterType::Pointer movingNormalizer = MovingNormalizeFilterType::New();

    using GaussianFilterType = itk::DiscreteGaussianImageFilter<InternalImageType, InternalImageType>;

    GaussianFilterType::Pointer fixedSmoother = GaussianFilterType::New();
    GaussianFilterType::Pointer movingSmoother = GaussianFilterType::New();

    fixedSmoother->SetVariance(2.0);
    movingSmoother->SetVariance(2.0);

    fixedNormalizer->SetInput(fixedImageReader->GetOutput());
    movingNormalizer->SetInput(movingImageReader->GetOutput());

    fixedSmoother->SetInput(fixedNormalizer->GetOutput());
    movingSmoother->SetInput(movingNormalizer->GetOutput());

    registration->SetFixedImage(fixedSmoother->GetOutput());
    registration->SetMovingImage(movingSmoother->GetOutput());

    fixedNormalizer->Update();
    FixedImageType::RegionType fixedImageRegion = fixedNormalizer->GetOutput()->GetBufferedRegion();
    registration->SetFixedImageRegion(fixedImageRegion);

    using ParametersType = RegistrationType::ParametersType;
    ParametersType initialParameters(transform->GetNumberOfParameters());

    initialParameters[0] = 1.0; // Initial offset in mm along X
    initialParameters[1] = 1.0; // Initial offset in mm along Y

    registration->SetInitialTransformParameters(initialParameters);
    registration1->SetInitialTransformParameters(initialParameters);

    // We should now define the number of spatial samples to be considered in
    // the metric computation. Note that we were forced to postpone this setting
    // until we had done the preprocessing of the images because the number of
    // samples is usually defined as a fraction of the total number of pixels in
    // the fixed image.
    //
    // The number of spatial samples can usually be as low as $1\%$ of the total
    // number of pixels in the fixed image. Increasing the number of samples
    // improves the smoothness of the metric from one iteration to another and
    // therefore helps when this metric is used in conjunction with optimizers
    // that rely of the continuity of the metric values. The trade-off, of
    // course, is that a larger number of samples result in longer computation
    // times per every evaluation of the metric.
    //
    // It has been demonstrated empirically that the number of samples is not a
    // critical parameter for the registration process. When you start fine
    // tuning your own registration process, you should start using high values
    // of number of samples, for example in the range of 20% to 50% of the
    // number of pixels in the fixed image. Once you have succeeded to register
    // your images you can then reduce the number of samples progressively until
    // you find a good compromise on the time it takes to compute one evaluation
    // of the Metric. Note that it is not useful to have very fast evaluations
    // of the Metric if the noise in their values results in more iterations
    // being required by the optimizer to converge.
    // behavior of the metric values as the iterations progress.
    const unsigned int numberOfPixels = fixedImageRegion.GetNumberOfPixels();

    const auto numberOfSamples = static_cast<unsigned int>(numberOfPixels * 0.01);

    metric->SetNumberOfSpatialSamples(numberOfSamples);

    // For consistent results when regression testing.
    metric->ReinitializeSeed(121212);

    // Since larger values of mutual information indicate better matches than
    // smaller values, we need to maximize the cost function in this example.
    // By default the GradientDescentOptimizer class is set to minimize the
    // value of the cost-function. It is therefore necessary to modify its
    // default behavior by invoking the MaximizeOn() method.
    // Additionally, we need to define the optimizer's step size using the
    // SetLearningRate() method.
    optimizer->SetLearningRate(15.0);
    optimizer->SetNumberOfIterations(200);
    optimizer->MaximizeOn();

    
    optimizer1->SetLearningRate(15.0);
    optimizer1->SetNumberOfIterations(200);
    optimizer1->MaximizeOn();
   

    // Note that large values of the learning rate will make the optimizer
    // unstable. Small values, on the other hand, may result in the optimizer
    // needing too many iterations in order to walk to the extrema of the cost
    // function. The easy way of fine tuning this parameter is to start with
    // small values, probably in the range of {5.0, 10.0}. Once the other
    // registration parameters have been tuned for producing convergence, you
    // may want to revisit the learning rate and start increasing its value until
    // you observe that the optimization becomes unstable. The ideal value for
    // this parameter is the one that results in a minimum number of iterations
    // while still keeping a stable path on the parametric space of the
    // optimization. Keep in mind that this parameter is a multiplicative factor
    // applied on the gradient of the Metric. Therefore, its effect on the
    // optimizer step length is proportional to the Metric values themselves.
    // Metrics with large values will require you to use smaller values for the
    // learning rate in order to maintain a similar optimizer behavior.
   /* optimizer->SetLearningRate(15.0);*/
   /* optimizer->SetMaximize(true);*/ // for NCC
  /* optimizer->MaximizeOn();*/

    //optimizer->SetMaximumIteration(100);
    //optimizer->SetMaximumLineIteration(4); // for Powell's method
    //optimizer->SetStepLength(0.5);
    //optimizer->SetStepTolerance(0.02);
    //optimizer->SetValueTolerance(0.001);

   /* itk::Optimizer::ScalesType weightings(transform->GetNumberOfParameters());
    weightings[0] = 1.;
    weightings[1] = 1.;
    optimizer->SetScales(weightings);*/

    CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New();
    optimizer->AddObserver(itk::IterationEvent(), observer);
    optimizer1->AddObserver(itk::IterationEvent(), observer);

    try
    {
        registration->Update();
        std::cout << "Optimizer stop condition: " << registration->GetOptimizer()->GetStopConditionDescription()
            << std::endl;
    }
    catch (itk::ExceptionObject& err)
    {
        std::cout << "ExceptionObject caught !" << std::endl;
        std::cout << err << std::endl;
        return EXIT_FAILURE;
    }

    ParametersType finalParameters = registration->GetLastTransformParameters();

    double TranslationAlongX = finalParameters[0];
    double TranslationAlongY = finalParameters[1];

    unsigned int numberOfIterations = optimizer->GetCurrentIteration();

    double bestValue = (optimizer->GetValue()*0.5+optimizer1->GetValue()*0.5);

    // Print out results
    std::cout << std::endl;
    std::cout << "Result = " << std::endl;
    std::cout << " Translation X = " << TranslationAlongX << std::endl;
    std::cout << " Translation Y = " << TranslationAlongY << std::endl;
    std::cout << " Iterations = " << numberOfIterations << std::endl;
    std::cout << " Metric value = " << bestValue << std::endl;
    std::cout << " Numb. Samples = " << numberOfSamples << std::endl;

    using ResampleFilterType = itk::ResampleImageFilter<MovingImageType, FixedImageType>;

    TransformType::Pointer finalTransform = TransformType::New();

    finalTransform->SetParameters(finalParameters);
    finalTransform->SetFixedParameters(transform->GetFixedParameters());

    ResampleFilterType::Pointer resample = ResampleFilterType::New();

    resample->SetTransform(finalTransform);
    resample->SetInput(movingImageReader->GetOutput());

    FixedImageType::Pointer fixedImage = fixedImageReader->GetOutput();

    resample->SetSize(fixedImage->GetLargestPossibleRegion().GetSize());
    resample->SetOutputOrigin(fixedImage->GetOrigin());
    resample->SetOutputSpacing(fixedImage->GetSpacing());
    resample->SetOutputDirection(fixedImage->GetDirection());
    resample->SetDefaultPixelValue(100);

    using OutputPixelType = unsigned char;

    using OutputImageType = itk::Image<OutputPixelType, Dimension>;

    using CastFilterType = itk::CastImageFilter<FixedImageType, OutputImageType>;

    using WriterType = itk::ImageFileWriter<OutputImageType>;

    WriterType::Pointer writer = WriterType::New();
    CastFilterType::Pointer caster = CastFilterType::New();

    writer->SetFileName(outputImageFile);
    caster->SetInput(resample->GetOutput());
    writer->SetInput(caster->GetOutput());
    writer->Update();

    // Generate checkerboards before and after registration
    using CheckerBoardFilterType = itk::CheckerBoardImageFilter<FixedImageType>;

    CheckerBoardFilterType::Pointer checker = CheckerBoardFilterType::New();

    checker->SetInput1(fixedImage);
    checker->SetInput2(resample->GetOutput());

    caster->SetInput(checker->GetOutput());
    writer->SetInput(caster->GetOutput());

    // Before registration
    TransformType::Pointer identityTransform = TransformType::New();
    identityTransform->SetIdentity();
    resample->SetTransform(identityTransform);

  /* if (argc > 4)*/
    
     writer->SetFileName(checkerBoardBefore);
     writer->Update();

    // After registration
    resample->SetTransform(finalTransform);
    /*if (argc > 5)*/
    
     writer->SetFileName(checkerBoardAfter);
    

    try
    {
        writer->Update();
    }
    catch (itk::ExceptionObject& error)
    {
        std::cerr << "Error: " << error << std::endl;
        return EXIT_FAILURE;
    }
    system("pause");
    return EXIT_SUCCESS;
}

```

I did this, and the result seems wrong

 ![image](https://discourse.itk.org/uploads/default/original/2X/3/328e0ee9024f9f8ca22effc8bf7c85bc78299321.png)

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [December 22, 2021, 5:06pm UTC](https://discourse.itk.org/t/similarity-measure/4680/6 "2021-12-22T17:06:29Z")

</div>

Well, you failed to mention that you want to do registration using a combination of 2 metrics. For that, you indeed need a single metric, even if it is internally computed using two metrics. Using `ObjectToObjectMultiMetricv4` is probably meant for this case.

---

<div class="post-metadata">

**Author:** ![wangjun456](https://discourse.itk.org/user_avatar/discourse.itk.org/wangjun456/32/2409_2.png) [@wangjun456](https://discourse.itk.org/u/wangjun456)\
**Post date:** [December 28, 2021, 3:34am UTC](https://discourse.itk.org/t/similarity-measure/4680/7 "2021-12-28T03:34:04Z")

</div>

I used it, but the following error occurred

 ![image](https://discourse.itk.org/uploads/default/original/2X/7/755a92d6428b499f410b9ce77e3814352828e63e.png)  
My code:

```auto
template <typename TFilter>
class itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate : public itk::Command
{
public:
    using Self = itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate;

    using Superclass = itk::Command;
    using Pointer = itk::SmartPointer<Self>;
    itkNewMacro(Self);

protected:
    itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate() = default;

public:
    void
        Execute(itk::Object* caller, const itk::EventObject& event) override
    {
        Execute((const itk::Object*)caller, event);
    }

    void
        Execute(const itk::Object* object, const itk::EventObject& event) override
    {
        if (typeid(event) != typeid(itk::IterationEvent))
        {
            return;
        }
        const auto* optimizer = dynamic_cast<const TFilter*>(object);

        if (!optimizer)
        {
            itkGenericExceptionMacro("Error dynamic_cast failed");
        }
        std::cout << "It- " << optimizer->GetCurrentIteration() << " gradient: " << optimizer->GetGradient()
            << " metric value: " << optimizer->GetCurrentMetricValue()
            << " Params: " << const_cast<TFilter*>(optimizer)->GetCurrentPosition() << std::endl;
    }
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
main()
{
    constexpr int Dimension = 2;
    using ImageType = itk::Image<float, Dimension>;
    using PixelType = float;

    using FixedImageType = itk::Image<PixelType, Dimension>;
    using MovingImageType = itk::Image<PixelType, Dimension>;

    using FixedImageReaderType = itk::ImageFileReader<FixedImageType>;
    using MovingImageReaderType = itk::ImageFileReader<MovingImageType>;

    FixedImageReaderType::Pointer fixedImageReader =
        FixedImageReaderType::New();
    MovingImageReaderType::Pointer movingImageReader =
        MovingImageReaderType::New();

    fixedImageReader->SetFileName("BrainProtonDensitySliceShifted13x17y.png");
    movingImageReader->SetFileName("BrainT1SliceBorder20.png");
    
    auto fixedImage = fixedImageReader->GetOutput();
    auto movingImage = movingImageReader->GetOutput();

    int numberOfIterations = 50;
   /* if (argc > 1)
    {
        numberOfIterations = std::stoi(argv[1]);
    }*/

    // create an affine transform
    using TranslationTransformType = itk::TranslationTransform<double, Dimension>;
    auto translationTransform = TranslationTransformType::New();
    translationTransform->SetIdentity();

    using OptimizerType = itk::GradientDescentOptimizerv4;
    auto optimizer = OptimizerType::New();

    using RegistrationType = itk::ImageRegistrationMethodv4<ImageType, ImageType>;
    auto registration = RegistrationType::New();
    registration->SetFixedImage(fixedImage);
    registration->SetMovingImage(movingImage);

  

    using MultiMetricType = itk::ObjectToObjectMultiMetricv4<Dimension, Dimension, ImageType>;
    using MetricType = itk::CorrelationImageToImageMetricv4<ImageType, ImageType>;
    auto correlationMetric = MetricType::New();
    correlationMetric->SetFixedImage(fixedImage);
    correlationMetric->SetMovingImage(movingImage);
    correlationMetric->SetMovingTransform(translationTransform);
    correlationMetric->Initialize();

    /*translationTransform->SetIdentity();*/
    // Test with two different metric types

   /* using MeanSquaresMetricType = itk::MeanSquaresImageToImageMetricv4<ImageType, ImageType>;
    auto meanSquaresMetric = MeanSquaresMetricType::New();
    meanSquaresMetric->SetFixedImage(fixedImage);
    meanSquaresMetric->SetMovingImage(movingImage);
    meanSquaresMetric->SetMovingTransform(translationTransform);*/

    using MattesMutualInformationMetricType = itk::MattesMutualInformationImageToImageMetricv4<ImageType,ImageType>;
    auto MattesMutualInformationMetric = MattesMutualInformationMetricType::New();
    MattesMutualInformationMetric->SetFixedImage(fixedImage);
    MattesMutualInformationMetric->SetMovingImage(movingImage);
    MattesMutualInformationMetric->SetMovingTransform(translationTransform);
    MattesMutualInformationMetric->Initialize();

    auto multiMetric2 = MultiMetricType::New();
    multiMetric2->AddMetric(correlationMetric);
    multiMetric2->AddMetric(MattesMutualInformationMetric);
    multiMetric2->Initialize();

    translationTransform->SetIdentity();
    MultiMetricType::WeightsArrayType oriMetricWeights(2);
    oriMetricWeights[0] = 0.5;
    oriMetricWeights[1] = 0.5;
    multiMetric2->SetMetricWeights(oriMetricWeights);
    std::cout << "*** Multi-metric with different metric types: " << std::endl;

    using ParametersType = TranslationTransformType::ParametersType;
    ParametersType initialParameters(translationTransform->GetNumberOfParameters());
    initialParameters[0] = 1.0; // Initial offset in mm along X
    initialParameters[1] = 1.0; // Initial offset in mm along Y
    
    translationTransform->SetParameters(initialParameters);
    registration->SetInitialTransform(translationTransform);
    registration->SetOptimizer(optimizer);
    registration->SetMetric(multiMetric2);
   
    optimizer->SetMetric(multiMetric2);
    optimizer->SetNumberOfIterations(numberOfIterations);
    optimizer->SetLearningRate(0.01);
    optimizer->SetMaximumStepSizeInPhysicalUnits(1.0);

    using CommandType = itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate<OptimizerType>;
    auto observer = CommandType::New();
    optimizer->AddObserver(itk::IterationEvent(), observer);

    optimizer->StartOptimization();

    try
    {
        registration->Update();
        std::cout << "Optimizer stop condition: " << registration->GetOptimizer()->GetStopConditionDescription()
            << std::endl;
    }
    catch (itk::ExceptionObject& err)
    {
        std::cout << "ExceptionObject caught !" << std::endl;
        std::cout << err << std::endl;
        system("pause");
        return EXIT_FAILURE;
    }

   

    

    using ResampleFilterType = itk::ResampleImageFilter<MovingImageType, FixedImageType>;

    OptimizerType::ParametersType finalParameters = translationTransform->GetParameters();
    double TranslationAlongX = finalParameters[0];
    double TranslationAlongY = finalParameters[1];

    unsigned int numberOfIteration = optimizer->GetCurrentIteration();

    double bestValue = optimizer->GetValue();

    std::cout << std::endl;
    std::cout << "Result = " << std::endl;
    std::cout << " Translation X = " << TranslationAlongX << std::endl;
    std::cout << " Translation Y = " << TranslationAlongY << std::endl;
    std::cout << " Iterations = " << numberOfIteration << std::endl;
    std::cout << " Metric value = " << bestValue << std::endl;
   

    TranslationTransformType::Pointer finalTransform = TranslationTransformType::New();
    finalTransform->SetParameters(finalParameters);
    finalTransform->SetFixedParameters(translationTransform->GetFixedParameters());

    ResampleFilterType::Pointer resample = ResampleFilterType::New();

    resample->SetTransform(finalTransform);
    resample->SetInput(movingImageReader->GetOutput());

   
    resample->SetSize(fixedImage->GetLargestPossibleRegion().GetSize());
    resample->SetOutputOrigin(fixedImage->GetOrigin());
    resample->SetOutputSpacing(fixedImage->GetSpacing());
    resample->SetOutputDirection(fixedImage->GetDirection());
    resample->SetDefaultPixelValue(100);

    using OutputPixelType = unsigned char;

    using OutputImageType = itk::Image<OutputPixelType, Dimension>;

    using CastFilterType = itk::CastImageFilter<FixedImageType, OutputImageType>;

    using WriterType = itk::ImageFileWriter<OutputImageType>;

    WriterType::Pointer writer = WriterType::New();
    CastFilterType::Pointer caster = CastFilterType::New();

    writer->SetFileName("out.png");
    caster->SetInput(resample->GetOutput());
    writer->SetInput(caster->GetOutput());
    writer->Update();

    system("pause");
    return EXIT_SUCCESS;
}

```

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [December 28, 2021, 2:39pm UTC](https://discourse.itk.org/t/similarity-measure/4680/8 "2021-12-28T14:39:32Z")

</div>

You came very close.

```cpp
  registration->SetFixedImage(fixedImage);
  registration->SetMovingImage(movingImage);

```

needed to be changed to

```cpp
  registration->SetFixedImage(fixedImage);
  registration->SetMovingImage(movingImage);
  registration->SetFixedImage(1, fixedImage);
  registration->SetMovingImage(1, movingImage);

```

There needs to be a separate pair of inputs for each metric in the multi-metric, as the different metrics don’t necessarily take the same inputs (e.g. first metric takes images, the second metric takes point sets). This should probably be noted in the [documentation](https://itk.org/Doxygen/html/classitk_1_1ObjectToObjectMultiMetricv4.html). Do you mind [contributing](https://github.com/InsightSoftwareConsortium/ITK/blob/master/CONTRIBUTING.md) that?

For posterity, here is a complete example which runs on my computer:

```cpp
#include "itkImageRegistrationMethodv4.h"
#include "itkTranslationTransform.h"
#include "itkMeanSquaresImageToImageMetricv4.h"
#include "itkRegularStepGradientDescentOptimizerv4.h"

#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

#include "itkResampleImageFilter.h"
#include "itkCastImageFilter.h"
#include "itkCorrelationImageToImageMetricv4.h"

template <typename TFilter>
class itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate : public itk::Command
{
public:
  using Self = itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate;

  using Superclass = itk::Command;
  using Pointer = itk::SmartPointer<Self>;
  itkNewMacro(Self);

protected:
  itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate() = default;

public:
  void
  Execute(itk::Object * caller, const itk::EventObject & event) override
  {
    Execute((const itk::Object *)caller, event);
  }

  void
  Execute(const itk::Object * object, const itk::EventObject & event) override
  {
    if (typeid(event) != typeid(itk::IterationEvent))
    {
      return;
    }
    const auto * optimizer = dynamic_cast<const TFilter *>(object);

    if (!optimizer)
    {
      itkGenericExceptionMacro("Error dynamic_cast failed");
    }
    std::cout << "It- " << optimizer->GetCurrentIteration() << " gradient: " << optimizer->GetGradient()
              << " metric value: " << optimizer->GetCurrentMetricValue()
              << " Params: " << const_cast<TFilter *>(optimizer)->GetCurrentPosition() << std::endl;
  }
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
main()
{
  constexpr int Dimension = 2;
  using ImageType = itk::Image<float, Dimension>;
  using PixelType = float;

  using FixedImageType = itk::Image<PixelType, Dimension>;
  using MovingImageType = itk::Image<PixelType, Dimension>;

  using FixedImageReaderType = itk::ImageFileReader<FixedImageType>;
  using MovingImageReaderType = itk::ImageFileReader<MovingImageType>;

  FixedImageReaderType::Pointer fixedImageReader = FixedImageReaderType::New();
  MovingImageReaderType::Pointer movingImageReader = MovingImageReaderType::New();

  fixedImageReader->SetFileName("C:/Dev/ITK-git/Examples/Data/BrainProtonDensitySliceShifted13x17y.png");
  movingImageReader->SetFileName("C:/Dev/ITK-git/Examples/Data/BrainT1SliceBorder20.png");

  auto fixedImage = fixedImageReader->GetOutput();
  auto movingImage = movingImageReader->GetOutput();

  int numberOfIterations = 50;
  /* if (argc > 1)
   {
       numberOfIterations = std::stoi(argv[1]);
   }*/

  // create an affine transform
  using TranslationTransformType = itk::TranslationTransform<double, Dimension>;
  auto translationTransform = TranslationTransformType::New();
  translationTransform->SetIdentity();

  using OptimizerType = itk::GradientDescentOptimizerv4;
  auto optimizer = OptimizerType::New();

  using RegistrationType = itk::ImageRegistrationMethodv4<ImageType, ImageType>;
  auto registration = RegistrationType::New();
  registration->SetFixedImage(fixedImage);
  registration->SetMovingImage(movingImage);
  registration->SetFixedImage(1, fixedImage);
  registration->SetMovingImage(1, movingImage);

  using MultiMetricType = itk::ObjectToObjectMultiMetricv4<Dimension, Dimension, ImageType>;
  using MetricType = itk::CorrelationImageToImageMetricv4<ImageType, ImageType>;
  auto correlationMetric = MetricType::New();
  correlationMetric->SetFixedImage(fixedImage);
  correlationMetric->SetMovingImage(movingImage);
  correlationMetric->SetMovingTransform(translationTransform);
  correlationMetric->Initialize();

  /*translationTransform->SetIdentity();*/
  // Test with two different metric types

  /* using MeanSquaresMetricType = itk::MeanSquaresImageToImageMetricv4<ImageType, ImageType>;
   auto meanSquaresMetric = MeanSquaresMetricType::New();
   meanSquaresMetric->SetFixedImage(fixedImage);
   meanSquaresMetric->SetMovingImage(movingImage);
   meanSquaresMetric->SetMovingTransform(translationTransform);*/

  using MattesMutualInformationMetricType = itk::MattesMutualInformationImageToImageMetricv4<ImageType, ImageType>;
  auto MattesMutualInformationMetric = MattesMutualInformationMetricType::New();
  MattesMutualInformationMetric->SetFixedImage(fixedImage);
  MattesMutualInformationMetric->SetMovingImage(movingImage);
  MattesMutualInformationMetric->SetMovingTransform(translationTransform);
  MattesMutualInformationMetric->Initialize();

  auto multiMetric2 = MultiMetricType::New();
  multiMetric2->AddMetric(correlationMetric);
  multiMetric2->AddMetric(MattesMutualInformationMetric);
  multiMetric2->Initialize();

  translationTransform->SetIdentity();
  MultiMetricType::WeightsArrayType oriMetricWeights(2);
  oriMetricWeights[0] = 0.5;
  oriMetricWeights[1] = 0.5;
  multiMetric2->SetMetricWeights(oriMetricWeights);
  std::cout << "*** Multi-metric with different metric types: " << std::endl;

  using ParametersType = TranslationTransformType::ParametersType;
  ParametersType initialParameters(translationTransform->GetNumberOfParameters());
  initialParameters[0] = 1.0; // Initial offset in mm along X
  initialParameters[1] = 1.0; // Initial offset in mm along Y

  translationTransform->SetParameters(initialParameters);
  registration->SetInitialTransform(translationTransform);
  registration->SetOptimizer(optimizer);
  registration->SetMetric(multiMetric2);

  optimizer->SetMetric(multiMetric2);
  optimizer->SetNumberOfIterations(numberOfIterations);
  optimizer->SetLearningRate(0.01);
  optimizer->SetMaximumStepSizeInPhysicalUnits(1.0);

  using CommandType = itkObjectToObjectMultiMetricv4RegistrationTestCommandIterationUpdate<OptimizerType>;
  auto observer = CommandType::New();
  optimizer->AddObserver(itk::IterationEvent(), observer);

  //optimizer->StartOptimization();

  try
  {
    registration->Update();
    std::cout << "Optimizer stop condition: " << registration->GetOptimizer()->GetStopConditionDescription()
              << std::endl;
  }
  catch (itk::ExceptionObject & err)
  {
    std::cout << "ExceptionObject caught !" << std::endl;
    std::cout << err << std::endl;
    system("pause");
    return EXIT_FAILURE;
  }

  using ResampleFilterType = itk::ResampleImageFilter<MovingImageType, FixedImageType>;

  OptimizerType::ParametersType finalParameters = translationTransform->GetParameters();
  double TranslationAlongX = finalParameters[0];
  double TranslationAlongY = finalParameters[1];

  unsigned int numberOfIteration = optimizer->GetCurrentIteration();

  double bestValue = optimizer->GetValue();

  std::cout << std::endl;
  std::cout << "Result = " << std::endl;
  std::cout << " Translation X = " << TranslationAlongX << std::endl;
  std::cout << " Translation Y = " << TranslationAlongY << std::endl;
  std::cout << " Iterations = " << numberOfIteration << std::endl;
  std::cout << " Metric value = " << bestValue << std::endl;

  TranslationTransformType::Pointer finalTransform = TranslationTransformType::New();
  finalTransform->SetParameters(finalParameters);
  finalTransform->SetFixedParameters(translationTransform->GetFixedParameters());

  ResampleFilterType::Pointer resample = ResampleFilterType::New();

  resample->SetTransform(finalTransform);
  resample->SetInput(movingImageReader->GetOutput());

  resample->SetSize(fixedImage->GetLargestPossibleRegion().GetSize());
  resample->SetOutputOrigin(fixedImage->GetOrigin());
  resample->SetOutputSpacing(fixedImage->GetSpacing());
  resample->SetOutputDirection(fixedImage->GetDirection());
  resample->SetDefaultPixelValue(100);

  using OutputPixelType = unsigned char;

  using OutputImageType = itk::Image<OutputPixelType, Dimension>;

  using CastFilterType = itk::CastImageFilter<FixedImageType, OutputImageType>;

  using WriterType = itk::ImageFileWriter<OutputImageType>;

  WriterType::Pointer writer = WriterType::New();
  CastFilterType::Pointer caster = CastFilterType::New();

  writer->SetFileName("out.png");
  caster->SetInput(resample->GetOutput());
  writer->SetInput(caster->GetOutput());
  writer->Update();

  system("pause");
  return EXIT_SUCCESS;
}

```

---

<div class="post-metadata">

**Author:** ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)\
**Post date:** [December 29, 2021, 2:33pm UTC](https://discourse.itk.org/t/similarity-measure/4680/9 "2021-12-29T14:33:57Z")

</div>

Split this topic on December 29, 2021, 2:33pm UTC

A post was split to a new topic: [How to tune registration](https://discourse.itk.org/t/how-to-tune-registration/4707)

---

<div class="post-metadata">

**Author:** ![Vidya](https://discourse.itk.org/user_avatar/discourse.itk.org/vidya/32/3986_2.png) [@Vidya](https://discourse.itk.org/u/Vidya)\
**Post date:** [June 13, 2024, 10:22am UTC](https://discourse.itk.org/t/similarity-measure/4680/10 "2024-06-13T10:22:28Z")

</div>

> [@dzenanz](#):
>
> ```auto
> std::cout << "It- " << optimizer->GetCurrentIteration() << " gradient: " << optimizer->GetGradient()
> << " metric value: " << optimizer->GetCurrentMetricValue()
> << " Params: " << const_cast<TFilter *>(optimizer)->GetCurrentPosition() << std::endl;
> 
> ```

This code will only give the values of the first metric during each iteration, right?  
[itk::ObjectToObjectMultiMetricv4](https://itk.org/Doxygen/html/classitk_1_1ObjectToObjectMultiMetricv4.html#a683e6e1a8776a60870dfa3de53dd4fa4), in this documentation its written that i can use [GetWeightedValue()](https://itk.org/Doxygen/html/classitk_1_1ObjectToObjectMultiMetricv4.html#a683e6e1a8776a60870dfa3de53dd4fa4) to get the weighted metric value. But `multiMetric2->GetWeightedValue()` will only give final metric value.

Is there any way to get metric values of both the metrics and weighted metric value during each iteration?
