
/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/
#include "mitkImageStatisticsCalculator.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkPlanarPolygon.h>

#include <mitkIOUtil.h>

#include <mitkPlanarFigureMaskGenerator.h>
#include <mitkImageMaskGenerator.h>
#include <mitkMultiLabelMaskGenerator.h>
#include <mitkImageStatisticsConstants.h>

/**
 * \brief Test class for mitkImageStatisticsCalculator
 *
 * This test covers:
 * - instantiation of an ImageStatisticsCalculator class
 * - correctness of statistics when using PlanarFigures for masking
 */
class mitkImageStatisticsCalculatorTestSuite : public mitk::TestFixture
{
  CPPUNIT_TEST_SUITE(mitkImageStatisticsCalculatorTestSuite);
  MITK_TEST(TestUninitializedImage);
  MITK_TEST(TestCase1);
  MITK_TEST(TestCase2);
  MITK_TEST(TestCase3);
  MITK_TEST(TestCase4);
  MITK_TEST(TestCase5);
  MITK_TEST(TestCase6);
  MITK_TEST(TestCase7);
  MITK_TEST(TestCase8);
  MITK_TEST(TestCase9);
  MITK_TEST(TestCase10);
  MITK_TEST(TestCase11);
  MITK_TEST(TestCase12);
  MITK_TEST(TestPic3DCroppedNoMask);
  MITK_TEST(TestPic3DCroppedBinMask);
  MITK_TEST(TestPic3DCroppedMultilabelMask);
  MITK_TEST(TestPic3DCroppedPlanarFigure);
  MITK_TEST(TestUS4DCroppedNoMaskTimeStep1);
  MITK_TEST(TestUS4DCroppedBinMaskTimeStep1);
  MITK_TEST(TestUS4DCroppedMultilabelMaskTimeStep1);
  MITK_TEST(TestUS4DCroppedPlanarFigureTimeStep1);
  MITK_TEST(TestUS4DCroppedAllTimesteps);
  MITK_TEST(TestUS4DCropped3DMask);
  CPPUNIT_TEST_SUITE_END();

public:

  void TestUninitializedImage();

  void TestCase1();
  void TestCase2();
  void TestCase3();
  void TestCase4();
  void TestCase5();
  void TestCase6();
  void TestCase7();
  void TestCase8();
  void TestCase9();
  void TestCase10();
  void TestCase11();
  void TestCase12();

  void TestPic3DCroppedNoMask();
  void TestPic3DCroppedBinMask();
  void TestPic3DCroppedMultilabelMask();
  void TestPic3DCroppedPlanarFigure();

  void TestUS4DCroppedNoMaskTimeStep1();
  void TestUS4DCroppedBinMaskTimeStep1();
  void TestUS4DCroppedMultilabelMaskTimeStep1();
  void TestUS4DCroppedPlanarFigureTimeStep1();
  void TestUS4DCroppedAllTimesteps();
  void TestUS4DCropped3DMask();
private:
  mitk::Image::ConstPointer m_TestImage;

  mitk::Image::ConstPointer m_Pic3DCroppedImage;
  mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure;

  mitk::Image::ConstPointer m_US4DCroppedImage;
  mitk::PlanarFigure::Pointer m_US4DCroppedPlanarFigure;

  mitk::PlaneGeometry::Pointer m_Geometry;

  // creates a polygon given a geometry and a vector of 2d points
  mitk::PlanarPolygon::Pointer GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector <mitk::Point2D> points);

  // universal function to calculate statistics
  const mitk::ImageStatisticsContainer::Pointer
    ComputeStatistics(mitk::Image::ConstPointer image,
      mitk::MaskGenerator::Pointer maskGen = nullptr,
      mitk::MaskGenerator::Pointer secondardMaskGen = nullptr)
  {
    mitk::ImageStatisticsCalculator::Pointer imgStatCalc = mitk::ImageStatisticsCalculator::New();
    imgStatCalc->SetInputImage(image);

    if (maskGen.IsNotNull())
    {
      imgStatCalc->SetMask(maskGen.GetPointer());
      if (secondardMaskGen.IsNotNull())
      {
        imgStatCalc->SetSecondaryMask(secondardMaskGen.GetPointer());
      }
    }

    return imgStatCalc->GetStatistics();
  }

  void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
    mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian = 0);

  // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
  void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
    mitk::ImageStatisticsContainer::VoxelCountType N,
    mitk::ImageStatisticsContainer::RealType mean,
    mitk::ImageStatisticsContainer::RealType MPP,
    mitk::ImageStatisticsContainer::RealType skewness,
    mitk::ImageStatisticsContainer::RealType kurtosis,
    mitk::ImageStatisticsContainer::RealType variance,
    mitk::ImageStatisticsContainer::RealType stdev,
    mitk::ImageStatisticsContainer::RealType min,
    mitk::ImageStatisticsContainer::RealType max,
    mitk::ImageStatisticsContainer::RealType RMS,
    mitk::ImageStatisticsContainer::IndexType minIndex,
    mitk::ImageStatisticsContainer::IndexType maxIndex);
};

void mitkImageStatisticsCalculatorTestSuite::TestUninitializedImage()
{
  /*****************************
  * loading uninitialized image to datastorage
  ******************************/
  MITK_INFO << std::endl << "Test uninitialized image: -----------------------------------------------------------------------------------";
  mitk::Image::Pointer image = mitk::Image::New();
  mitk::DataNode::Pointer node = mitk::DataNode::New();
  node->SetData(image);

  mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New();
  CPPUNIT_ASSERT_THROW(is->GetStatistics(), mitk::Exception);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase1()
{
  /*****************************
   * one whole white pixel
   * -> mean of 255 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 1:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  CPPUNIT_ASSERT_MESSAGE("Failed loading an mitk::Image", m_TestImage.IsNotNull());

  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
  CPPUNIT_ASSERT_MESSAGE("Failed getting image geometry", m_Geometry.IsNotNull());

  mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5;
  mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
  mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);
  mitk::ImageStatisticsContainer::Pointer statisticsContainer;

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase2()
{
  /*****************************
   * half pixel in x-direction (white)
   * -> mean of 255 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 2:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 10.0; pnt1[1] = 3.5;
  mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
  mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1,0);

  this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase3()
{
  /*****************************
   * half pixel in diagonal-direction (white)
   * -> mean of 255 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 3:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5;
  mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase4()
{
  /*****************************
   * one pixel (white) + 2 half pixels (white) + 1 half pixel (black)
   * -> mean of 191.25 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 4:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1;
  mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0;
  mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0;
  mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 191.25, 127.5, 253.72499847412109);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase5()
{
  /*****************************
   * whole pixel (white) + half pixel (gray) in x-direction
   * -> mean of 191.5 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 5:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
  mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
  mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 191.50, 89.802561210691536, 128.63499999046327);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase6()
{
  /*****************************
   * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction
   * -> mean of 191.5 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 6:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
  mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5;
  mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 89.802561210691536, 128.63499999046327);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase7()
{
  /*****************************
   * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction
   * -> mean of 127.66 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 7:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
  mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0;
  mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 127.50032679696680, 128.7750015258789);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase8()
{
  /*****************************
   * whole pixel (gray)
   * -> mean of 128 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 8:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
  mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5;
  mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5;
  mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 128.0, 0.0, 128.0);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase9()
{
  /*****************************
   * whole pixel (gray) + half pixel (white) in y-direction
   * -> mean of 191.5 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 9:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
  mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0;
  mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0;
  mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 89.802561210691536, 128.63499999046327);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase10()
{
  /*****************************
   * 2 whole pixel (white) + 2 whole pixel (black) in y-direction
   * -> mean of 127.66 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 10:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
  mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5;
  mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5;
  mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 127.50032679696680, 128.7750015258789);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase11()
{
  /*****************************
   * 9 whole pixels (white) + 3 half pixels (white)
   * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)]
   * -> mean of 204.0 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 11:-----------------------------------------------------------------------------------";
  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5;
  mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5;
  mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5;
  mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 204.0, 105.58003057938019, 253.724998474121083);
}

void mitkImageStatisticsCalculatorTestSuite::TestCase12()
{
  /*****************************
   * half pixel (white) + whole pixel (white) + half pixel (black)
   * -> mean of 212.66 expected
   ******************************/
  MITK_INFO << std::endl << "Test case 12:-----------------------------------------------------------------------------------";

  std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
  m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
  m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);

  mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5;
  mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5;
  mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5;
  std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3 };
        auto figure = GeneratePlanarPolygon(m_Geometry, points);

  mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
  planFigMaskGen->SetInputImage(m_TestImage);
        planFigMaskGen->SetPlanarFigure(figure.GetPointer());

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  this->VerifyStatistics(statisticsObjectTimestep0, 212.666666666666667, 73.323484187082443, 254.36499786376954);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedNoMask()
{
  MITK_INFO << std::endl << "Test Pic3D cropped without mask:-----------------------------------------------------------------------------------";

  std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
  m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27;
  mitk::ImageStatisticsContainer::RealType expected_mean = -564.1481481481481481;
  mitk::ImageStatisticsContainer::RealType expected_MPP = 113.66666666666667;
        //mitk::ImageStatisticsContainer::RealType expected_median = -825;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0.7120461106763573;
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.8794464383714844;
  mitk::ImageStatisticsContainer::RealType expected_variance = 145946.82336182334;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 382.02987234223366;
  mitk::ImageStatisticsContainer::RealType expected_min = -927;
  mitk::ImageStatisticsContainer::RealType expected_max = 147;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 681.32955052662169;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 2;
  expected_minIndex[1] = 1;
  expected_minIndex[2] = 1;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 0;
  expected_maxIndex[1] = 1;
  expected_maxIndex[2] = 2;

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage));
  auto statisticsObject = statisticsContainer->GetStatistics(mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE, 0);

  VerifyStatistics(statisticsObject,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedBinMask()
{
  MITK_INFO << std::endl << "Test Pic3D cropped binary mask:-----------------------------------------------------------------------------------";

  std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
  m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());

  std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd");
  auto pic3DCroppedBinMask = mitk::IOUtil::Load<mitk::MultiLabelSegmentation>(Pic3DCroppedBinMaskFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", pic3DCroppedBinMask.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0765697398089618;
  mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
  mitk::ImageStatisticsContainer::RealType expected_max = -22;
  mitk::ImageStatisticsContainer::RealType expected_mean = -464;
  mitk::ImageStatisticsContainer::RealType expected_min = -846;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 633.20191618998331;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0544059290851858;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 430.86966320067910;
  mitk::ImageStatisticsContainer::RealType expected_variance = 185648.66666666663;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 1;
  expected_minIndex[1] = 0;
  expected_minIndex[2] = 0;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 0;
  expected_maxIndex[1] = 0;
  expected_maxIndex[2] = 1;

  mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
  imgMaskGen->SetImageMask(pic3DCroppedBinMask->GetGroupImage(0));
  imgMaskGen->SetInputImage(m_Pic3DCroppedImage);
  imgMaskGen->SetTimePoint(m_Pic3DCroppedImage->GetTimeGeometry()->TimeStepToTimePoint(0));

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  VerifyStatistics(statisticsObjectTimestep0,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedMultilabelMask()
{
  MITK_INFO << std::endl << "Test Pic3D cropped multi-label mask:-----------------------------------------------------------------------------------";

  std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
  m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());

  std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd");
  auto pic3DCroppedMultilabelMask = mitk::IOUtil::Load<mitk::MultiLabelSegmentation>(Pic3DCroppedMultilabelMaskFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multi-label mask", pic3DCroppedMultilabelMask.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5;
  mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
  mitk::ImageStatisticsContainer::RealType expected_max = -22;
  mitk::ImageStatisticsContainer::RealType expected_mean = -586.33333333333333;
  mitk::ImageStatisticsContainer::RealType expected_min = -916;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 3;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 764.78566351044469;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0.6774469597523700;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 491.02987010296363;
  mitk::ImageStatisticsContainer::RealType expected_variance = 241110.33333333334;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 2;
  expected_minIndex[1] = 0;
  expected_minIndex[2] = 1;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 0;
  expected_maxIndex[1] = 0;
  expected_maxIndex[2] = 1;

  mitk::MultiLabelMaskGenerator::Pointer mlMaskGen = mitk::MultiLabelMaskGenerator::New();
  mlMaskGen->SetMultiLabelSegmentation(pic3DCroppedMultilabelMask);
  mlMaskGen->SetInputImage(m_Pic3DCroppedImage);
  mlMaskGen->SetTimePoint(m_Pic3DCroppedImage->GetTimeGeometry()->TimeStepToTimePoint(0));

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, mlMaskGen.GetPointer(), nullptr));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(2, 0);

  VerifyStatistics(statisticsObjectTimestep0,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedPlanarFigure()
{
  MITK_INFO << std::endl << "Test Pic3D cropped planar figure:-----------------------------------------------------------------------------------";

  std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
  m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());

  std::string Pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf");
  m_Pic3DCroppedPlanarFigure = mitk::IOUtil::Load<mitk::PlanarFigure>(Pic3DCroppedPlanarFigureFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_Pic3DCroppedPlanarFigure.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
  mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
  mitk::ImageStatisticsContainer::RealType expected_max = -67;
  mitk::ImageStatisticsContainer::RealType expected_mean = -446;
  mitk::ImageStatisticsContainer::RealType expected_min = -825;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 697.27899724572228;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 535.98694013940303;
  mitk::ImageStatisticsContainer::RealType expected_variance = 287282.0;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 1;
  expected_minIndex[1] = 1;
  expected_minIndex[2] = 1;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 0;
  expected_maxIndex[1] = 1;
  expected_maxIndex[2] = 1;

  mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New();
  pfMaskGen->SetInputImage(m_Pic3DCroppedImage);
  pfMaskGen->SetPlanarFigure(m_Pic3DCroppedPlanarFigure);

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, pfMaskGen.GetPointer()));
  auto statisticsObjectTimestep0 = statisticsContainer->GetStatistics(1, 0);

  VerifyStatistics(statisticsObjectTimestep0,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedNoMaskTimeStep1()
{
  MITK_INFO << std::endl << "Test US4D cropped without mask time step 1:-----------------------------------------------------------------------------------";

        std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
  m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5398359155908228;
  mitk::ImageStatisticsContainer::RealType expected_MPP = 157.74074074074073;
  mitk::ImageStatisticsContainer::RealType expected_max = 199;
  mitk::ImageStatisticsContainer::RealType expected_mean = 157.74074074074073;
  mitk::ImageStatisticsContainer::RealType expected_min = 101;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 161.11544579426010;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0347280313508018;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 32.803133753432512;
  mitk::ImageStatisticsContainer::RealType expected_variance = 1076.0455840455834;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 0;
  expected_minIndex[1] = 2;
  expected_minIndex[2] = 0;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 0;
  expected_maxIndex[1] = 0;
  expected_maxIndex[2] = 1;

  mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage));
  auto statisticsObjectTimestep1 = statisticsContainer->GetStatistics(mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE, 1);

  VerifyStatistics(statisticsObjectTimestep1,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedBinMaskTimeStep1()
{
  MITK_INFO << std::endl << "Test US4D cropped with binary mask time step 1:-----------------------------------------------------------------------------------";

  std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
  m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());

  std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd");
  auto us4DCroppedBinMask = mitk::IOUtil::Load<mitk::MultiLabelSegmentation>(US4DCroppedBinMaskFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", us4DCroppedBinMask.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5863739712889191;
  mitk::ImageStatisticsContainer::RealType expected_MPP = 166.75;
  mitk::ImageStatisticsContainer::RealType expected_max = 199;
  mitk::ImageStatisticsContainer::RealType expected_mean = 166.75;
  mitk::ImageStatisticsContainer::RealType expected_min = 120;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 170.68043971117487;
  mitk::ImageStatisticsContainer::RealType expected_skewness = -0.4285540263894276;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 36.417715469260287;
  mitk::ImageStatisticsContainer::RealType expected_variance = 1326.25;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 0;
  expected_minIndex[1] = 0;
  expected_minIndex[2] = 2;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 1;
  expected_maxIndex[1] = 1;
  expected_maxIndex[2] = 1;

  mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
  imgMask1->SetInputImage(m_US4DCroppedImage);
  imgMask1->SetImageMask(us4DCroppedBinMask->GetGroupImage(0));

  mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr));
  auto statisticsObjectTimestep1 = statisticsContainer->GetStatistics(1, 1);

  VerifyStatistics(statisticsObjectTimestep1,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedMultilabelMaskTimeStep1()
{
  MITK_INFO << std::endl << "Test US4D cropped with multi-label mask time step 1:-----------------------------------------------------------------------------------";

  std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
  m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());

  std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd");
  auto us4DCroppedMultilabelMask = mitk::IOUtil::Load<mitk::MultiLabelSegmentation>(US4DCroppedMultilabelMaskFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multi-label mask", us4DCroppedMultilabelMask.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0432484564918287;
  mitk::ImageStatisticsContainer::RealType expected_MPP = 159.75;
  mitk::ImageStatisticsContainer::RealType expected_max = 199;
  mitk::ImageStatisticsContainer::RealType expected_mean = 159.75;
  mitk::ImageStatisticsContainer::RealType expected_min = 120;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 165.05447333128134;
  mitk::ImageStatisticsContainer::RealType expected_skewness = -0.004329226115093;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 41.508031351374242;
  mitk::ImageStatisticsContainer::RealType expected_variance = 1722.9166666666670;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 0;
  expected_minIndex[1] = 0;
  expected_minIndex[2] = 2;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 0;
  expected_maxIndex[1] = 0;
  expected_maxIndex[2] = 1;

  mitk::MultiLabelMaskGenerator::Pointer mlMask = mitk::MultiLabelMaskGenerator::New();
  mlMask->SetInputImage(m_US4DCroppedImage);
  mlMask->SetMultiLabelSegmentation(us4DCroppedMultilabelMask);

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, mlMask.GetPointer(), nullptr));
  auto statisticsObjectTimestep1 = statisticsContainer->GetStatistics(1, 1);

  VerifyStatistics(statisticsObjectTimestep1,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedPlanarFigureTimeStep1()
{
  MITK_INFO << std::endl << "Test US4D cropped planar figure time step 1:-----------------------------------------------------------------------------------";

  std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
  m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());

  std::string US4DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedPF.pf");
  m_US4DCroppedPlanarFigure = mitk::IOUtil::Load<mitk::PlanarFigure>(US4DCroppedPlanarFigureFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D planar figure", m_US4DCroppedPlanarFigure.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
  mitk::ImageStatisticsContainer::RealType expected_MPP = 172.5;
  mitk::ImageStatisticsContainer::RealType expected_max = 197;
  mitk::ImageStatisticsContainer::RealType expected_mean = 172.5;
  mitk::ImageStatisticsContainer::RealType expected_min = 148;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 175.94530400098776;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 34.648232278140831;
  mitk::ImageStatisticsContainer::RealType expected_variance = 1200.5000000000002;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 2;
  expected_minIndex[1] = 2;
  expected_minIndex[2] = 2;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 2;
  expected_maxIndex[1] = 2;
  expected_maxIndex[2] = 1;

  mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New();
  pfMaskGen->SetInputImage(m_US4DCroppedImage);
  pfMaskGen->SetPlanarFigure(m_US4DCroppedPlanarFigure);

  mitk::ImageStatisticsContainer::Pointer statisticsContainer;
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, pfMaskGen.GetPointer()));
  auto statisticsObjectTimestep1 = statisticsContainer->GetStatistics(1, 1);

  VerifyStatistics(statisticsObjectTimestep1,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedAllTimesteps()
{
  MITK_INFO << std::endl << "Test US4D cropped all time steps:-----------------------------------------------------------------------------------";

  std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
  m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
  mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage));
  for (int i = 0; i < 4; i++)
  {
    CPPUNIT_ASSERT_MESSAGE("Error computing statistics for multiple time steps", statisticsContainer->StatisticsExist(mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE, i));
  }
}

void mitkImageStatisticsCalculatorTestSuite::TestUS4DCropped3DMask()
{
  MITK_INFO << std::endl << "Test US4D cropped with 3D binary Mask:-----------------------------------------------------------------------------------";

  std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
  m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());

  std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd");
  auto us4DCropped3DBinMask = mitk::IOUtil::Load<mitk::MultiLabelSegmentation>(US4DCropped3DBinMaskFile);
  CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", us4DCropped3DBinMask.IsNotNull());
  //calculated ground truth via script
  mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
  mitk::ImageStatisticsContainer::RealType expected_MPP = 198;
  mitk::ImageStatisticsContainer::RealType expected_max = 199;
  mitk::ImageStatisticsContainer::RealType expected_mean = 198;
  mitk::ImageStatisticsContainer::RealType expected_min = 197;
  mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
  mitk::ImageStatisticsContainer::RealType expected_RMS = 198.00505044063902;
  mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
  mitk::ImageStatisticsContainer::RealType expected_standarddev = 1.4142135623730951;
  mitk::ImageStatisticsContainer::RealType expected_variance = 2;
  mitk::ImageStatisticsContainer::IndexType expected_minIndex;
  expected_minIndex.set_size(3);
  expected_minIndex[0] = 1;
  expected_minIndex[1] = 2;
  expected_minIndex[2] = 1;

  mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
  expected_maxIndex.set_size(3);
  expected_maxIndex[0] = 1;
  expected_maxIndex[1] = 1;
  expected_maxIndex[2] = 1;

  mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
  imgMask1->SetInputImage(m_US4DCroppedImage);
  imgMask1->SetImageMask(us4DCropped3DBinMask->GetGroupImage(0));

  mitk::ImageStatisticsContainer::Pointer statisticsContainer = mitk::ImageStatisticsContainer::New();
  CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr));
  auto statisticsObjectTimestep1 = statisticsContainer->GetStatistics(1, 1);

  VerifyStatistics(statisticsObjectTimestep1,
    expected_N,
    expected_mean,
    expected_MPP,
    expected_skewness,
    expected_kurtosis,
    expected_variance,
    expected_standarddev,
    expected_min,
    expected_max,
    expected_RMS,
    expected_minIndex,
    expected_maxIndex);
}

mitk::PlanarPolygon::Pointer mitkImageStatisticsCalculatorTestSuite::GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector <mitk::Point2D> points)
{
  mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New();
  figure->SetPlaneGeometry(geometry);
  figure->PlaceFigure(points[0]);
        for (unsigned int i = 1; i < points.size(); i++)
  {
    figure->SetControlPoint(i, points[i], true);
  }
  return figure;
}

void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
  mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian)
{
    mitk::ImageStatisticsContainer::RealType meanObject = 0;
    mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0;
    mitk::ImageStatisticsContainer::RealType medianObject = 0;
  CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN()));
  CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::STANDARDDEVIATION()));
  CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEDIAN()));
  CPPUNIT_ASSERT_MESSAGE("Calculated mean gray value is not equal to the desired value.", std::abs(meanObject - testMean) < mitk::eps);
  CPPUNIT_ASSERT_MESSAGE("Calculated gray value sd is not equal to the desired value.", std::abs(standardDeviationObject - testSD) < mitk::eps);
  CPPUNIT_ASSERT_MESSAGE("Calculated median gray value is not equal to the desired value.", std::abs(medianObject - testMedian) < mitk::eps);
}

// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
  mitk::ImageStatisticsContainer::VoxelCountType N,
  mitk::ImageStatisticsContainer::RealType mean,
  mitk::ImageStatisticsContainer::RealType MPP,
  mitk::ImageStatisticsContainer::RealType skewness,
  mitk::ImageStatisticsContainer::RealType kurtosis,
  mitk::ImageStatisticsContainer::RealType variance,
  mitk::ImageStatisticsContainer::RealType stdev,
  mitk::ImageStatisticsContainer::RealType min,
  mitk::ImageStatisticsContainer::RealType max,
  mitk::ImageStatisticsContainer::RealType RMS,
  mitk::ImageStatisticsContainer::IndexType minIndex,
  mitk::ImageStatisticsContainer::IndexType maxIndex)
{
  mitk::ImageStatisticsContainer::VoxelCountType numberOfVoxelsObject;
  mitk::ImageStatisticsContainer::RealType meanObject = 0;
  mitk::ImageStatisticsContainer::RealType mppObject = 0;
  mitk::ImageStatisticsContainer::RealType skewnessObject = 0;
  mitk::ImageStatisticsContainer::RealType kurtosisObject = 0;
  mitk::ImageStatisticsContainer::RealType varianceObject = 0;
  mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0;
  mitk::ImageStatisticsContainer::RealType minObject = 0;
  mitk::ImageStatisticsContainer::RealType maxObject = 0;
  mitk::ImageStatisticsContainer::RealType rmsObject = 0;
  mitk::ImageStatisticsContainer::IndexType minIndexObject(3,0);
  mitk::ImageStatisticsContainer::IndexType maxIndexObject(3,0);
  CPPUNIT_ASSERT_NO_THROW(numberOfVoxelsObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::VoxelCountType>(mitk::ImageStatisticsConstants::NUMBEROFVOXELS()));
  CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN()));
  CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MPP()));
  CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::SKEWNESS()));
  CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::KURTOSIS()));
  CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::VARIANCE()));
  CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::STANDARDDEVIATION()));
  CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MINIMUM()));
  CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MAXIMUM()));
  CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::RMS()));
  CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::IndexType>(mitk::ImageStatisticsConstants::MINIMUMPOSITION()));
  CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::IndexType>(mitk::ImageStatisticsConstants::MAXIMUMPOSITION()));

  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", numberOfVoxelsObject - N == 0);
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(meanObject - mean) < mitk::eps);
  // in three test cases MPP is None because the ROI has no positive pixels
  if (!std::isnan(mppObject))
  {
    CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(mppObject - MPP) < mitk::eps);
  }
  // Skewness and kurtosis use the high-order moment formula (Σx³/n - 3μs² - μ³)/s³
  // which is sensitive to FP evaluation order (FMA contractions differ between
  // x86_64 and ARM64). Use a relative tolerance with mitk::eps as the absolute floor.
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) <= std::max(mitk::eps, std::abs(skewness) * 1e-9));
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) <= std::max(mitk::eps, std::abs(kurtosis) * 1e-9));
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(varianceObject - variance) < mitk::eps);
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(standardDeviationObject - stdev) < mitk::eps);
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minObject - min) < mitk::eps);
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxObject - max) < mitk::eps);
  CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(rmsObject - RMS) < mitk::eps);
  for (unsigned int i = 0; i < minIndex.size(); ++i)
  {
    CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minIndexObject[i] - minIndex[i]) < mitk::eps);
  }
  for (unsigned int i = 0; i < maxIndex.size(); ++i)
  {
    CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxIndexObject[i] - maxIndex[i]) < mitk::eps);
  }
}
MITK_TEST_SUITE_REGISTRATION(mitkImageStatisticsCalculator)
