diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp index 7a3b8642b4..7a01ef5cfd 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp @@ -1,1133 +1,1133 @@ /*============================================================================ 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 #include #include #include #include #include #include /** * \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::Image::Pointer m_Pic3DCroppedBinMask; mitk::Image::Pointer m_Pic3DCroppedMultilabelMask; mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure; mitk::Image::ConstPointer m_US4DCroppedImage; mitk::Image::Pointer m_US4DCroppedBinMask; mitk::Image::Pointer m_US4DCroppedMultilabelMask; mitk::Image::Pointer m_US4DCropped3DBinMask; 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 points); // universal function to calculate statistics const mitk::ImageStatisticsContainer::Pointer - mitkImageStatisticsCalculatorTestSuite::ComputeStatistics(mitk::Image::ConstPointer image, + 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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(Pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd"); m_Pic3DCroppedBinMask = mitk::IOUtil::Load(Pic3DCroppedBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_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(m_Pic3DCroppedBinMask); 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(Pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd"); m_Pic3DCroppedMultilabelMask = mitk::IOUtil::Load(Pic3DCroppedMultilabelMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multi-label mask", m_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::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); imgMaskGen->SetImageMask(m_Pic3DCroppedMultilabelMask); 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(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(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(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(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(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd"); m_US4DCroppedBinMask = mitk::IOUtil::Load(US4DCroppedBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", m_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(m_US4DCroppedBinMask); 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(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd"); m_US4DCroppedMultilabelMask = mitk::IOUtil::Load(US4DCroppedMultilabelMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multi-label mask", m_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::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); imgMask1->SetInputImage(m_US4DCroppedImage); imgMask1->SetImageMask(m_US4DCroppedMultilabelMask); mitk::ImageStatisticsContainer::Pointer statisticsContainer; 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::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(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(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(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(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd"); m_US4DCropped3DBinMask = mitk::IOUtil::Load(US4DCropped3DBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_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(m_US4DCropped3DBinMask); 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 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::ImageStatisticsConstants::MEAN())); CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted(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::ImageStatisticsConstants::NUMBEROFVOXELS())); CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MPP())); CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::SKEWNESS())); CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::KURTOSIS())); CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::VARIANCE())); CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUM())); CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUM())); CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::RMS())); CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUMPOSITION())); CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted(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); } CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) < mitk::eps); 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) diff --git a/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.cpp b/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.cpp index 22f94650d0..b13421bf9b 100644 --- a/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.cpp @@ -1,120 +1,120 @@ /*============================================================================ 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 #include #include #include #include #include namespace mitk { void IgnorePixelMaskGenerator::SetIgnoredPixelValue(RealType pixelValue) { if (pixelValue != m_IgnoredPixelValue) { m_IgnoredPixelValue = pixelValue; this->Modified(); } } unsigned int IgnorePixelMaskGenerator::GetNumberOfMasks() const { return 1; } -mitk::Image::ConstPointer IgnorePixelMaskGenerator::DoGetMask(unsigned int maskID) +mitk::Image::ConstPointer IgnorePixelMaskGenerator::DoGetMask(unsigned int) { if (IsUpdateRequired()) { if (m_InputImage.IsNull()) { mitkThrow() << "Image not set!"; } if (m_IgnoredPixelValue == std::numeric_limits::min()) { mitkThrow() << "IgnotePixelValue not set!"; } auto timeSliceImage = mitk::SelectImageByTimePoint(m_InputImage, m_TimePoint); if (timeSliceImage.IsNull()) mitkThrow() << "Cannot generate mask. Passed time point is not supported by input image. Invalid time point: "<< m_TimePoint; // update m_InternalMask AccessByItk(timeSliceImage, InternalCalculateMask); m_InternalMask->SetGeometry(timeSliceImage->GetGeometry()); this->Modified(); } m_InternalMaskUpdateTime = m_InternalMask->GetMTime(); return m_InternalMask; } template -void IgnorePixelMaskGenerator::InternalCalculateMask(typename const itk::Image* image) +void IgnorePixelMaskGenerator::InternalCalculateMask(const itk::Image* image) { typedef itk::Image ImageType; typedef itk::Image MaskType; typename MaskType::Pointer mask = MaskType::New(); mask->SetOrigin(image->GetOrigin()); mask->SetSpacing(image->GetSpacing()); mask->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); mask->SetBufferedRegion(image->GetBufferedRegion()); mask->SetDirection(image->GetDirection()); mask->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); mask->Allocate(); mask->FillBuffer(1); // iterate over image and mask and set mask=1 if image=m_IgnorePixelValue itk::ImageRegionConstIterator imageIterator(image, image->GetLargestPossibleRegion()); itk::ImageRegionIterator maskIterator(mask, mask->GetLargestPossibleRegion()); for (imageIterator.GoToBegin(); !imageIterator.IsAtEnd(); ++imageIterator, ++maskIterator) { if (imageIterator.Value() == static_cast(m_IgnoredPixelValue)) { maskIterator.Set(0); } } m_InternalMask = GrabItkImageMemory(mask); } bool IgnorePixelMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask.IsNull() ? 0 : m_InternalMask->GetMTime(); unsigned long inputImageTimeStamp = m_InputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } // end namespace diff --git a/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.h b/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.h index 478c443d40..c7f108af48 100644 --- a/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.h +++ b/Modules/ImageStatistics/mitkIgnorePixelMaskGenerator.h @@ -1,74 +1,74 @@ /*============================================================================ 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. ============================================================================*/ #ifndef mitkIgnorePixelMaskGenerator_h #define mitkIgnorePixelMaskGenerator_h #include #include #include #include #include namespace mitk { /** * @brief The IgnorePixelMaskGenerator class is used to generate a mask that is zero for specific pixel values in the input image. This class requires an input image. */ class MITKIMAGESTATISTICS_EXPORT IgnorePixelMaskGenerator: public MaskGenerator { public: /** Standard Self typedef */ typedef IgnorePixelMaskGenerator Self; typedef MaskGenerator Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; typedef double RealType; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(IgnorePixelMaskGenerator, MaskGenerator); /** * @brief The mask will be 0 there inputImage==pixelValue and 1 otherwise */ void SetIgnoredPixelValue(RealType pixelValue); unsigned int GetNumberOfMasks() const override; protected: IgnorePixelMaskGenerator(): m_IgnoredPixelValue(std::numeric_limits::min()), m_InternalMaskUpdateTime(0) { } ~IgnorePixelMaskGenerator() = default; mitk::Image::ConstPointer DoGetMask(unsigned int maskID) override; template - void InternalCalculateMask(typename const itk::Image* image); + void InternalCalculateMask(const itk::Image* image); private: bool IsUpdateRequired() const; mitk::Image::Pointer m_InternalMask; RealType m_IgnoredPixelValue; unsigned long m_InternalMaskUpdateTime; }; } #endif diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index 7b61e7ca7e..ac202b47f2 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,524 +1,523 @@ /*============================================================================ 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 #include #include #include #include #include #include #include #include #include #include #include namespace mitk { void ImageStatisticsCalculator::SetInputImage(const mitk::Image *image) { if (image != m_Image) { m_Image = image; this->Modified(); } } void ImageStatisticsCalculator::SetMask(mitk::MaskGenerator *mask) { if (mask != m_MaskGenerator) { m_MaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetSecondaryMask(mitk::MaskGenerator *mask) { if (mask != m_SecondaryMaskGenerator) { m_SecondaryMaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetNBinsForHistogramStatistics(unsigned int nBins) { if (nBins != m_nBinsForHistogramStatistics) { m_nBinsForHistogramStatistics = nBins; this->Modified(); this->m_UseBinSizeOverNBins = false; } if (m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = false; } } unsigned int ImageStatisticsCalculator::GetNBinsForHistogramStatistics() const { return m_nBinsForHistogramStatistics; } void ImageStatisticsCalculator::SetBinSizeForHistogramStatistics(double binSize) { if (binSize != m_binSizeForHistogramStatistics) { m_binSizeForHistogramStatistics = binSize; this->Modified(); this->m_UseBinSizeOverNBins = true; } if (!m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = true; } } double ImageStatisticsCalculator::GetBinSizeForHistogramStatistics() const { return m_binSizeForHistogramStatistics; } mitk::ImageStatisticsContainer* ImageStatisticsCalculator::GetStatistics() { if (m_Image.IsNull()) { mitkThrow() << "no image"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if (IsUpdateRequired()) { auto timeGeometry = m_Image->GetTimeGeometry(); m_StatisticContainer = ImageStatisticsContainer::New(); m_StatisticContainer->SetTimeGeometry(timeGeometry->Clone()); // always compute statistics on all timesteps for (TimeStepType timeStep = 0; timeStep < m_Image->GetTimeSteps(); timeStep++) { const unsigned int numbersOfMasks = m_MaskGenerator.IsNotNull() ? m_MaskGenerator->GetNumberOfMasks() : 1; - auto timePoint = m_Image->GetTimeGeometry()->TimeStepToTimePoint(timeStep); + auto timePoint = timeGeometry->TimeStepToTimePoint(timeStep); //hard coded 0 is //a workaround , open up a task refer to it that it should be removed // as soon as the secondary mask is removed anyways and solved by a generator chain. if (m_SecondaryMaskGenerator.IsNotNull()) { if (m_SecondaryMaskGenerator->GetNumberOfMasks() != 1) mitkThrow() << "Cannot generate secondary mask. ImageStatisticsCalculator does only support secondary mask generators with on mask. Number of masks provided: " << m_SecondaryMaskGenerator->GetNumberOfMasks(); m_SecondaryMaskGenerator->SetTimePoint(timePoint); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(0); } for (unsigned int maskID = 0; maskID < numbersOfMasks; ++maskID) { if (m_MaskGenerator.IsNotNull()) { m_MaskGenerator->SetTimePoint(timePoint); m_InternalMask = m_MaskGenerator->GetMask(maskID); if (m_MaskGenerator->GetReferenceImage().IsNotNull()) { m_InternalImageForStatistics = m_MaskGenerator->GetReferenceImage(); } else { m_InternalImageForStatistics = m_Image; } } else { m_InternalImageForStatistics = m_Image; } m_ImageTimeSlice = SelectImageByTimeStep(m_InternalImageForStatistics, timeStep); // Calculate statistics with/without mask if (m_MaskGenerator.IsNull() && m_SecondaryMaskGenerator.IsNull()) { // 1) calculate statistics unmasked: - AccessByItk_2(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeGeometry, timeStep) + AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeStep) } else { // 2) calculate statistics masked - AccessByItk_2(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeGeometry, timeStep) + AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeStep) } } } } return m_StatisticContainer; } template void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( - typename const itk::Image *image, const TimeGeometry *timeGeometry, TimeStepType timeStep) + const itk::Image *image, TimeStepType timeStep) { typedef typename itk::Image ImageType; typedef typename mitk::StatisticsImageFilter ImageStatisticsFilterType; typedef typename itk::MinMaxImageFilterWithIndex MinMaxFilterType; auto statObj = ImageStatisticsContainer::ImageStatisticsObject(); typename ImageStatisticsFilterType::Pointer statisticsFilter = ImageStatisticsFilterType::New(); statisticsFilter->SetInput(image); statisticsFilter->SetCoordinateTolerance(0.001); statisticsFilter->SetDirectionTolerance(0.001); // TODO: this is single threaded. Implement our own image filter that does this multi threaded // typename itk::MinimumMaximumImageCalculator::Pointer imgMinMaxFilter = // itk::MinimumMaximumImageCalculator::New(); imgMinMaxFilter->SetImage(image); // imgMinMaxFilter->Compute(); vnl_vector minIndex, maxIndex; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput(image); minMaxFilter->UpdateLargestPossibleRegion(); typename ImageType::PixelType minval = minMaxFilter->GetMin(); typename ImageType::PixelType maxval = minMaxFilter->GetMax(); typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex(); typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex(); // typename ImageType::IndexType tmpMinIndex = imgMinMaxFilter->GetIndexOfMinimum(); // typename ImageType::IndexType tmpMaxIndex = imgMinMaxFilter->GetIndexOfMaximum(); minIndex.set_size(tmpMaxIndex.GetIndexDimension()); maxIndex.set_size(tmpMaxIndex.GetIndexDimension()); for (unsigned int i = 0; i < tmpMaxIndex.GetIndexDimension(); i++) { minIndex[i] = tmpMinIndex[i]; maxIndex[i] = tmpMaxIndex[i]; } statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUMPOSITION(), minIndex); statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUMPOSITION(), maxIndex); // convert m_binSize in m_nBins if necessary unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(maxval - minval)) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } statisticsFilter->SetHistogramParameters(nBinsForHistogram, minval, maxval); try { statisticsFilter->Update(); } catch (const itk::ExceptionObject &e) { mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what(); } auto voxelVolume = GetVoxelVolume(image); auto numberOfPixels = image->GetLargestPossibleRegion().GetNumberOfPixels(); auto volume = static_cast(numberOfPixels) * voxelVolume; auto variance = statisticsFilter->GetSigma() * statisticsFilter->GetSigma(); auto rms = std::sqrt(std::pow(statisticsFilter->GetMean(), 2.) + statisticsFilter->GetVariance()); // variance = sigma^2 statObj.AddStatistic(ImageStatisticsConstants::NUMBEROFVOXELS(), static_cast(numberOfPixels)); statObj.AddStatistic(ImageStatisticsConstants::VOLUME(), volume); statObj.AddStatistic(ImageStatisticsConstants::MEAN(), statisticsFilter->GetMean()); statObj.AddStatistic(ImageStatisticsConstants::MINIMUM(), static_cast(statisticsFilter->GetMinimum())); statObj.AddStatistic(ImageStatisticsConstants::MAXIMUM(), static_cast(statisticsFilter->GetMaximum())); statObj.AddStatistic(ImageStatisticsConstants::STANDARDDEVIATION(), statisticsFilter->GetSigma()); statObj.AddStatistic(ImageStatisticsConstants::VARIANCE(), variance); statObj.AddStatistic(ImageStatisticsConstants::SKEWNESS(), statisticsFilter->GetSkewness()); statObj.AddStatistic(ImageStatisticsConstants::KURTOSIS(), statisticsFilter->GetKurtosis()); statObj.AddStatistic(ImageStatisticsConstants::RMS(), rms); statObj.AddStatistic(ImageStatisticsConstants::MPP(), statisticsFilter->GetMPP()); statObj.AddStatistic(ImageStatisticsConstants::ENTROPY(), statisticsFilter->GetEntropy()); statObj.AddStatistic(ImageStatisticsConstants::MEDIAN(), statisticsFilter->GetMedian()); statObj.AddStatistic(ImageStatisticsConstants::UNIFORMITY(), statisticsFilter->GetUniformity()); statObj.AddStatistic(ImageStatisticsConstants::UPP(), statisticsFilter->GetUPP()); statObj.m_Histogram = statisticsFilter->GetHistogram(); m_StatisticContainer->SetStatistics(ImageStatisticsContainer::NO_MASK_LABEL_VALUE, timeStep, statObj); } template - double ImageStatisticsCalculator::GetVoxelVolume(typename const itk::Image *image) const + double ImageStatisticsCalculator::GetVoxelVolume(const itk::Image *image) const { auto spacing = image->GetSpacing(); double voxelVolume = 1.; for (unsigned int i = 0; i < image->GetImageDimension(); i++) { voxelVolume *= spacing[i]; } return voxelVolume; } template - void ImageStatisticsCalculator::InternalCalculateStatisticsMasked(typename const itk::Image *image, - const TimeGeometry *timeGeometry, - unsigned int timeStep) + void ImageStatisticsCalculator::InternalCalculateStatisticsMasked(const itk::Image *image, + TimeStepType timeStep) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef typename MaskType::PixelType LabelPixelType; typedef LabelStatisticsImageFilter ImageStatisticsFilterType; typedef MaskUtilities MaskUtilType; typedef typename itk::MinMaxLabelImageFilterWithIndex MinMaxLabelFilterType; // workaround: if m_SecondaryMaskGenerator is not null but m_MaskGenerator is! (this is the case if we request a // 'ignore zero valued pixels' mask in the gui but do not define a primary mask) bool swapMasks = false; if (m_SecondaryMask.IsNotNull() && m_InternalMask.IsNull()) { m_InternalMask = m_SecondaryMask; m_SecondaryMask = nullptr; swapMasks = true; } // maskImage has to have the same dimension as image typename MaskType::ConstPointer maskImage = MaskType::New(); try { // try to access the pixel values directly (no copying or casting). Only works if mask pixels are of pixelType // unsigned short maskImage = ImageToItkImage(m_InternalMask); } catch (const itk::ExceptionObject &) { typename MaskType::Pointer noneConstMaskImage; //needed to work around the fact that CastToItkImage currently does not support const itk images. // if the pixel type of the mask is not short, then we have to make a copy of m_InternalMask (and cast the values) CastToItkImage(m_InternalMask, noneConstMaskImage); maskImage = noneConstMaskImage; } // if we have a secondary mask (say a ignoreZeroPixelMask) we need to combine the masks (corresponds to AND) if (m_SecondaryMask.IsNotNull()) { // dirty workaround for a bug when pf mask + any other mask is used in conjunction. We need a proper fix for this // (Fabian Isensee is responsible and probably working on it!) if (m_InternalMask->GetDimension() == 2 && (m_SecondaryMask->GetDimension() == 3 || m_SecondaryMask->GetDimension() == 4)) { Image::ConstPointer old_img = m_SecondaryMaskGenerator->GetReferenceImage(); m_SecondaryMaskGenerator->SetInputImage(m_MaskGenerator->GetReferenceImage()); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(0); m_SecondaryMaskGenerator->SetInputImage(old_img); } typename MaskType::ConstPointer secondaryMaskImage = MaskType::New(); secondaryMaskImage = ImageToItkImage(m_SecondaryMask); // secondary mask should be a ignore zero value pixel mask derived from image. it has to be cropped to the mask // region (which may be planar or simply smaller) typename MaskUtilities::Pointer secondaryMaskMaskUtil = MaskUtilities::New(); secondaryMaskMaskUtil->SetImage(secondaryMaskImage.GetPointer()); secondaryMaskMaskUtil->SetMask(maskImage.GetPointer()); typename MaskType::ConstPointer adaptedSecondaryMaskImage = secondaryMaskMaskUtil->ExtractMaskImageRegion(); typename itk::MaskImageFilter2::Pointer maskFilter = itk::MaskImageFilter2::New(); maskFilter->SetInput1(maskImage); maskFilter->SetInput2(adaptedSecondaryMaskImage); maskFilter->SetMaskingValue( 1); // all pixels of maskImage where secondaryMaskImage==1 will be kept, all the others are set to 0 maskFilter->UpdateLargestPossibleRegion(); maskImage = maskFilter->GetOutput(); } typename MaskUtilType::Pointer maskUtil = MaskUtilType::New(); maskUtil->SetImage(image); maskUtil->SetMask(maskImage.GetPointer()); // if mask is smaller than image, extract the image region where the mask is typename ImageType::ConstPointer adaptedImage = ImageType::New(); adaptedImage = maskUtil->ExtractMaskImageRegion(); // this also checks mask sanity // find min, max, minindex and maxindex typename MinMaxLabelFilterType::Pointer minMaxFilter = MinMaxLabelFilterType::New(); minMaxFilter->SetInput(adaptedImage); minMaxFilter->SetLabelInput(maskImage); minMaxFilter->UpdateLargestPossibleRegion(); // set histogram parameters for each label individually (min/max may be different for each label) typedef typename std::unordered_map MapType; std::vector relevantLabels = minMaxFilter->GetRelevantLabels(); MapType minVals; MapType maxVals; std::unordered_map nBins; for (LabelPixelType label : relevantLabels) { minVals[label] = static_cast(minMaxFilter->GetMin(label)); maxVals[label] = static_cast(minMaxFilter->GetMax(label)); unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(minMaxFilter->GetMax(label) - minMaxFilter->GetMin(label))) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } nBins[label] = nBinsForHistogram; } typename ImageStatisticsFilterType::Pointer imageStatisticsFilter = ImageStatisticsFilterType::New(); imageStatisticsFilter->SetDirectionTolerance(0.001); imageStatisticsFilter->SetCoordinateTolerance(0.001); imageStatisticsFilter->SetInput(adaptedImage); imageStatisticsFilter->SetLabelInput(maskImage); imageStatisticsFilter->SetHistogramParameters(nBins, minVals, maxVals); imageStatisticsFilter->Update(); const auto labels = imageStatisticsFilter->GetValidLabelValues(); for (auto labelValue : labels) { if (labelValue == ImageStatisticsContainer::NO_MASK_LABEL_VALUE) { //we ignore the background of a mask if we compute mask statistics. continue; } ImageStatisticsContainer::ImageStatisticsObject statObj; // find min, max, minindex and maxindex // make sure to only look in the masked region, use a masker for this vnl_vector minIndex, maxIndex; Point3D worldCoordinateMin; Point3D worldCoordinateMax; Point3D indexCoordinateMin; Point3D indexCoordinateMax; m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMinIndex(labelValue), worldCoordinateMin); m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMaxIndex(labelValue), worldCoordinateMax); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMin, indexCoordinateMin); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMax, indexCoordinateMax); minIndex.set_size(3); maxIndex.set_size(3); // for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) for (unsigned int i = 0; i < 3; i++) { minIndex[i] = indexCoordinateMin[i]; maxIndex[i] = indexCoordinateMax[i]; } statObj.AddStatistic(ImageStatisticsConstants::MINIMUMPOSITION(), minIndex); statObj.AddStatistic(ImageStatisticsConstants::MAXIMUMPOSITION(), maxIndex); auto voxelVolume = GetVoxelVolume(image); auto numberOfVoxels = static_cast(imageStatisticsFilter->GetCount(labelValue)); auto volume = static_cast(numberOfVoxels) * voxelVolume; auto rms = std::sqrt(std::pow(imageStatisticsFilter->GetMean(labelValue), 2.) + imageStatisticsFilter->GetVariance(labelValue)); // variance = sigma^2 auto variance = imageStatisticsFilter->GetSigma(labelValue) * imageStatisticsFilter->GetSigma(labelValue); statObj.AddStatistic(ImageStatisticsConstants::NUMBEROFVOXELS(), numberOfVoxels); statObj.AddStatistic(ImageStatisticsConstants::VOLUME(), volume); statObj.AddStatistic(ImageStatisticsConstants::MEAN(), imageStatisticsFilter->GetMean(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::MINIMUM(), static_cast(imageStatisticsFilter->GetMinimum(labelValue))); statObj.AddStatistic(ImageStatisticsConstants::MAXIMUM(), static_cast(imageStatisticsFilter->GetMaximum(labelValue))); statObj.AddStatistic(ImageStatisticsConstants::STANDARDDEVIATION(), imageStatisticsFilter->GetSigma(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::VARIANCE(), variance); statObj.AddStatistic(ImageStatisticsConstants::SKEWNESS(), imageStatisticsFilter->GetSkewness(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::KURTOSIS(), imageStatisticsFilter->GetKurtosis(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::RMS(), rms); statObj.AddStatistic(ImageStatisticsConstants::MPP(), imageStatisticsFilter->GetMPP(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::ENTROPY(), imageStatisticsFilter->GetEntropy(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::MEDIAN(), imageStatisticsFilter->GetMedian(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::UNIFORMITY(), imageStatisticsFilter->GetUniformity(labelValue)); statObj.AddStatistic(ImageStatisticsConstants::UPP(), imageStatisticsFilter->GetUPP(labelValue)); statObj.m_Histogram = imageStatisticsFilter->GetHistogram(labelValue); if (m_StatisticContainer->StatisticsExist(labelValue, timeStep)) mitkThrow() << "Invalid state/input data. Statistic for a specific label/time step pair was computed more then once. Conflicting label ID: " << labelValue << " ; conflicting time step: " << timeStep; m_StatisticContainer->SetStatistics(labelValue, timeStep, statObj); } // swap maskGenerators back if (swapMasks) { m_SecondaryMask = m_InternalMask; m_InternalMask = nullptr; } } bool ImageStatisticsCalculator::IsUpdateRequired() const { const auto thisClassTimeStamp = this->GetMTime(); const auto inputImageTimeStamp = m_Image->GetMTime(); if (m_StatisticContainer.IsNull()) { return true; } const auto statisticsTimeStamp = m_StatisticContainer->GetMTime(); if (thisClassTimeStamp > statisticsTimeStamp) // inputs have changed { return true; } if (inputImageTimeStamp > statisticsTimeStamp) // image has changed { return true; } if (m_MaskGenerator.IsNotNull()) { const auto maskGeneratorTimeStamp = m_MaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a mask generator and it has changed { return true; } } if (m_SecondaryMaskGenerator.IsNotNull()) { const auto maskGeneratorTimeStamp = m_SecondaryMaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a secondary mask generator and it has changed { return true; } } return false; } } // namespace mitk diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.h b/Modules/ImageStatistics/mitkImageStatisticsCalculator.h index 89c55746df..50ab83f181 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.h +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.h @@ -1,118 +1,117 @@ /*============================================================================ 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. ============================================================================*/ #ifndef mitkImageStatisticsCalculator_h #define mitkImageStatisticsCalculator_h #include #include #include #include namespace mitk { class MITKIMAGESTATISTICS_EXPORT ImageStatisticsCalculator: public itk::Object { public: /** Standard Self typedef */ typedef ImageStatisticsCalculator Self; typedef itk::Object Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(ImageStatisticsCalculator_v2, itk::Object); typedef double statisticsValueType; typedef std::map statisticsMapType; typedef itk::Statistics::Histogram HistogramType; typedef unsigned short MaskPixelType; using LabelIndex = ImageStatisticsContainer::LabelValueType; /**Documentation @brief Set the image for which the statistics are to be computed.*/ void SetInputImage(const mitk::Image* image); /**Documentation @brief Set the mask generator that creates the mask which is to be used to calculate statistics. If no more mask is desired simply set @param mask to nullptr*/ void SetMask(mitk::MaskGenerator* mask); /**Documentation @brief Set this if more than one mask should be applied (for instance if a IgnorePixelValueMask were to be used alongside with a segmentation). Both masks are combined using pixel wise AND operation. The secondary mask does not have to be the same size than the primary but they need to have some overlap*/ void SetSecondaryMask(mitk::MaskGenerator* mask); /**Documentation @brief Set number of bins to be used for histogram statistics. If Bin size is set after number of bins, bin size will be used instead!*/ void SetNBinsForHistogramStatistics(unsigned int nBins); /**Documentation @brief Retrieve the number of bins used for histogram statistics. Careful: The return value does not indicate whether NBins or BinSize is used. That solely depends on which parameter has been set last.*/ unsigned int GetNBinsForHistogramStatistics() const; /**Documentation @brief Set bin size to be used for histogram statistics. If nbins is set after bin size, nbins will be used instead!*/ void SetBinSizeForHistogramStatistics(double binSize); /**Documentation @brief Retrieve the bin size for histogram statistics. Careful: The return value does not indicate whether NBins or BinSize is used. That solely depends on which parameter has been set last.*/ double GetBinSizeForHistogramStatistics() const; /**Documentation @brief Returns the statistics. If these requested statistics are not computed yet the computation is done as well. */ ImageStatisticsContainer* GetStatistics(); protected: ImageStatisticsCalculator(){ m_nBinsForHistogramStatistics = 100; m_binSizeForHistogramStatistics = 10; m_UseBinSizeOverNBins = false; }; private: //Calculates statistics for each timestep for image - template < typename TPixel, unsigned int VImageDimension > void InternalCalculateStatisticsUnmasked( - typename const itk::Image< TPixel, VImageDimension >* image, const TimeGeometry* timeGeometry, TimeStepType timeStep); + template < typename TPixel, unsigned int VImageDimension > + void InternalCalculateStatisticsUnmasked(const itk::Image< TPixel, VImageDimension >* image, TimeStepType timeStep); - template < typename TPixel, unsigned int VImageDimension > void InternalCalculateStatisticsMasked( - typename const itk::Image< TPixel, VImageDimension >* image, const TimeGeometry* timeGeometry, - unsigned int timeStep); + template < typename TPixel, unsigned int VImageDimension > + void InternalCalculateStatisticsMasked(const itk::Image< TPixel, VImageDimension >* image, TimeStepType timeStep); template < typename TPixel, unsigned int VImageDimension > - double GetVoxelVolume(typename const itk::Image* image) const; + double GetVoxelVolume(const itk::Image* image) const; bool IsUpdateRequired() const; mitk::Image::ConstPointer m_Image; mitk::Image::ConstPointer m_ImageTimeSlice; mitk::Image::ConstPointer m_InternalImageForStatistics; mitk::MaskGenerator::Pointer m_MaskGenerator; mitk::Image::ConstPointer m_InternalMask; mitk::MaskGenerator::Pointer m_SecondaryMaskGenerator; mitk::Image::ConstPointer m_SecondaryMask; unsigned int m_nBinsForHistogramStatistics; double m_binSizeForHistogramStatistics; bool m_UseBinSizeOverNBins; ImageStatisticsContainer::Pointer m_StatisticContainer; }; } #endif diff --git a/Modules/ImageStatistics/mitkImageStatisticsContainer.cpp b/Modules/ImageStatistics/mitkImageStatisticsContainer.cpp index 17d91eadf7..727321b282 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsContainer.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsContainer.cpp @@ -1,303 +1,301 @@ /*============================================================================ 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 #include #include #include namespace mitk { ImageStatisticsContainer::ImageStatisticsContainer() { this->Reset(); } // The order is derived from the old (<2018) image statistics plugin. const ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector ImageStatisticsContainer::ImageStatisticsObject::m_DefaultNames = {ImageStatisticsConstants::MEAN(), ImageStatisticsConstants::MEDIAN(), ImageStatisticsConstants::STANDARDDEVIATION(), ImageStatisticsConstants::RMS(), ImageStatisticsConstants::MAXIMUM(), ImageStatisticsConstants::MAXIMUMPOSITION(), ImageStatisticsConstants::MINIMUM(), ImageStatisticsConstants::MINIMUMPOSITION(), ImageStatisticsConstants::NUMBEROFVOXELS(), ImageStatisticsConstants::VOLUME(), ImageStatisticsConstants::SKEWNESS(), ImageStatisticsConstants::KURTOSIS(), ImageStatisticsConstants::UNIFORMITY(), ImageStatisticsConstants::ENTROPY(), ImageStatisticsConstants::MPP(), ImageStatisticsConstants::UPP()}; ImageStatisticsContainer::ImageStatisticsObject::ImageStatisticsObject() { Reset(); } void ImageStatisticsContainer::ImageStatisticsObject::AddStatistic(const std::string_view key, StatisticsVariantType value) { m_Statistics.emplace(key, value); if (std::find(m_DefaultNames.cbegin(), m_DefaultNames.cend(), key) == m_DefaultNames.cend()) { if (std::find(m_CustomNames.cbegin(), m_CustomNames.cend(), key) == m_CustomNames.cend()) { m_CustomNames.emplace_back(key); } } } const ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector & ImageStatisticsContainer::ImageStatisticsObject::GetDefaultStatisticNames() { return m_DefaultNames; } const ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector & ImageStatisticsContainer::ImageStatisticsObject::GetCustomStatisticNames() const { return m_CustomNames; } ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector ImageStatisticsContainer::ImageStatisticsObject::GetAllStatisticNames() const { StatisticNameVector names = GetDefaultStatisticNames(); names.insert(names.cend(), m_CustomNames.cbegin(), m_CustomNames.cend()); return names; } ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector ImageStatisticsContainer::ImageStatisticsObject::GetExistingStatisticNames() const { StatisticNameVector names; std::transform(m_Statistics.begin(), m_Statistics.end(), std::back_inserter(names), [](const auto &pair) { return pair.first; }); return names; } bool ImageStatisticsContainer::ImageStatisticsObject::HasStatistic(const std::string_view name) const { return m_Statistics.find(name) != m_Statistics.cend(); } ImageStatisticsContainer::StatisticsVariantType ImageStatisticsContainer::ImageStatisticsObject::GetValueNonConverted( const std::string_view name) const { if (HasStatistic(name)) { return m_Statistics.find(name)->second; } else { mitkThrow() << "invalid statistic key, could not find"; } } void ImageStatisticsContainer::ImageStatisticsObject::Reset() { m_Statistics.clear(); m_CustomNames.clear(); } - const static ImageStatisticsContainer::LabelValueType NO_MASK_LABEL_VALUE = 1; - bool ImageStatisticsContainer::StatisticsExist(LabelValueType labelValue, TimeStepType timeStep) const { auto labelFinding = m_LabelTimeStep2StatisticsMap.find(labelValue); if (labelFinding == m_LabelTimeStep2StatisticsMap.end()) return false; auto timeFinding = labelFinding->second.find(timeStep); return timeFinding != labelFinding->second.end(); } const ImageStatisticsContainer::HistogramType* ImageStatisticsContainer::GetHistogram(LabelValueType labelValue, TimeStepType timeStep) const { return this->GetStatistics(labelValue, timeStep).m_Histogram; } bool ImageStatisticsContainer::IgnoresZeroVoxel() const { const auto prop = this->GetProperty(mitk::STATS_IGNORE_ZERO_VOXEL_PROPERTY_NAME.c_str()); const auto boolProp = dynamic_cast(prop.GetPointer()); if (nullptr != boolProp) { return boolProp->GetValue(); } return false; } bool ImageStatisticsContainer::IsWIP() const { return this->GetProperty(mitk::STATS_GENERATION_STATUS_PROPERTY_NAME.c_str()).IsNotNull(); } const ImageStatisticsContainer::ImageStatisticsObject &ImageStatisticsContainer::GetStatistics(LabelValueType labelValue, TimeStepType timeStep) const { auto labelFinding = m_LabelTimeStep2StatisticsMap.find(labelValue); if (labelFinding == m_LabelTimeStep2StatisticsMap.end()) mitkThrow() << "Cannot get statistics. Requested label value does not exist. Invalid label:" <second.find(timeStep); if (timeFinding == labelFinding->second.end()) mitkThrow() << "Cannot get statistics. Requested time step does not exist. Invalid time step:" << timeStep; return timeFinding->second; } void ImageStatisticsContainer::SetStatistics(LabelValueType labelValue, TimeStepType timeStep, const ImageStatisticsObject& statistics) { if (!this->GetTimeGeometry()->IsValidTimeStep(timeStep)) mitkThrow() << "Given timeStep " << timeStep << " out of TimeGeometry bounds of the object. TimeSteps in geometry: " << this->GetTimeSteps(); m_LabelTimeStep2StatisticsMap[labelValue][timeStep] = statistics; this->Modified(); } void ImageStatisticsContainer::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); for (const auto& [labelValue, timeMap] : m_LabelTimeStep2StatisticsMap) { for (const auto& [timeStep, container] : timeMap) { os << std::endl << indent << "Statistics instance (Label "<< labelValue << ", TimeStep "<< timeStep << "):"; auto statisticsValues = GetStatistics(labelValue,timeStep); auto statisticKeys = statisticsValues.GetExistingStatisticNames(); os << std::endl << indent << "Number of entries: " << statisticKeys.size(); for (const auto& aKey : statisticKeys) { os << std::endl << indent.GetNextIndent() << aKey << ": " << statisticsValues.GetValueNonConverted(aKey); } } } } ImageStatisticsContainer::LabelValueVectorType ImageStatisticsContainer::GetExistingLabelValues() const { LabelValueVectorType result; for (const auto& [labelValue, timeMap] : m_LabelTimeStep2StatisticsMap) { result.push_back(labelValue); } return result; } ImageStatisticsContainer::TimeStepVectorType ImageStatisticsContainer::GetExistingTimeSteps(LabelValueType labelValue) const { TimeStepVectorType result; auto labelFinding = m_LabelTimeStep2StatisticsMap.find(labelValue); if (labelFinding == m_LabelTimeStep2StatisticsMap.end()) mitkThrow() << "Cannot get existing time steps. Requested label value does not exist. Invalid label:" << labelValue; for (const auto& [timestep, stats] : labelFinding->second) { result.push_back(timestep); } return result; } void ImageStatisticsContainer::Reset() { m_LabelTimeStep2StatisticsMap.clear(); this->Modified(); } itk::LightObject::Pointer ImageStatisticsContainer::InternalClone() const { itk::LightObject::Pointer ioPtr = Superclass::InternalClone(); Self::Pointer rval = dynamic_cast(ioPtr.GetPointer()); if (rval.IsNull()) { itkExceptionMacro(<< "downcast to type " << "StatisticsContainer" << " failed."); } rval->m_LabelTimeStep2StatisticsMap = m_LabelTimeStep2StatisticsMap; rval->SetTimeGeometry(this->GetTimeGeometry()->Clone()); return ioPtr; } ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector GetAllStatisticNames( const ImageStatisticsContainer *container) { ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector names = ImageStatisticsContainer::ImageStatisticsObject::GetDefaultStatisticNames(); if (container) { std::set customKeys; auto labelValues = container->GetExistingLabelValues(); for (const auto labelValue : labelValues) { auto timeSteps = container->GetExistingTimeSteps(labelValue); for (const auto timeStep : timeSteps) { auto statisticKeys = container->GetStatistics(labelValue, timeStep).GetCustomStatisticNames(); customKeys.insert(statisticKeys.cbegin(), statisticKeys.cend()); } } names.insert(names.cend(), customKeys.cbegin(), customKeys.cend()); } return names; } ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector GetAllStatisticNames( std::vector containers) { ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector names = ImageStatisticsContainer::ImageStatisticsObject::GetDefaultStatisticNames(); std::set customKeys; for (const auto &container : containers) { std::set customKeys; auto labelValues = container->GetExistingLabelValues(); for (const auto labelValue : labelValues) { auto timeSteps = container->GetExistingTimeSteps(labelValue); for (const auto timeStep : timeSteps) { auto statisticKeys = container->GetStatistics(labelValue, timeStep).GetCustomStatisticNames(); customKeys.insert(statisticKeys.cbegin(), statisticKeys.cend()); } } names.insert(names.cend(), customKeys.cbegin(), customKeys.cend()); } names.insert(names.end(), customKeys.begin(), customKeys.end()); return names; }; } // namespace mitk diff --git a/Modules/ImageStatistics/mitkImageStatisticsContainer.h b/Modules/ImageStatistics/mitkImageStatisticsContainer.h index 5d6bc73292..e6ca236c32 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsContainer.h +++ b/Modules/ImageStatistics/mitkImageStatisticsContainer.h @@ -1,182 +1,182 @@ /*============================================================================ 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. ============================================================================*/ #ifndef mitkImageStatisticsContainer_h #define mitkImageStatisticsContainer_h #include #include #include #include #include #include namespace mitk { /** @brief Container class for storing a StatisticsObject for each time step. Stored statistics are: - for the defined statistics, see GetAllStatisticNames - Histogram of Pixel Values */ class MITKIMAGESTATISTICS_EXPORT ImageStatisticsContainer : public mitk::BaseData { public: mitkClassMacro(ImageStatisticsContainer, mitk::BaseData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using HistogramType = itk::Statistics::Histogram; using LabelValueType = LabelSetImage::LabelValueType; void SetRequestedRegionToLargestPossibleRegion() override {} bool RequestedRegionIsOutsideOfTheBufferedRegion() override { return false; } bool VerifyRequestedRegion() override { return true; } void SetRequestedRegion(const itk::DataObject*) override {} /** @brief Container class for storing the computed image statistics. @details The statistics are stored in a map with value as boost::variant. The type used to create the boost::variant is important as only this type can be recovered later on. */ class MITKIMAGESTATISTICS_EXPORT ImageStatisticsObject { public: ImageStatisticsObject(); using RealType = double; using IndexType = vnl_vector; using VoxelCountType = unsigned long; using StatisticsVariantType = boost::variant; /** @brief Adds a statistic to the statistics object @details if already a statistic with that name is included, it is overwritten */ void AddStatistic(const std::string_view key, StatisticsVariantType value); using StatisticNameVector = std::vector; /** @brief Returns the names of the default statistics @details The order is derived from the image statistics plugin. */ static const StatisticNameVector& GetDefaultStatisticNames(); /** @brief Returns the names of all custom statistics (defined at runtime and no default names). */ const StatisticNameVector& GetCustomStatisticNames() const; /** @brief Returns the names of all statistics (default and custom defined) Additional custom keys are added at the end in a sorted order. */ StatisticNameVector GetAllStatisticNames() const; StatisticNameVector GetExistingStatisticNames() const; bool HasStatistic(const std::string_view name) const; /** @brief Converts the requested value to the defined type @param name defined string on creation (AddStatistic) @exception if no statistics with key name was found. */ template TType GetValueConverted(const std::string_view name) const { auto value = GetValueNonConverted(name); return boost::get(value); } /** @brief Returns the requested value @exception if no statistics with key name was found. */ StatisticsVariantType GetValueNonConverted(const std::string_view name) const; void Reset(); HistogramType::ConstPointer m_Histogram=nullptr; private: using StatisticsMapType = std::map < std::string, StatisticsVariantType, std::less<>>; StatisticsMapType m_Statistics; StatisticNameVector m_CustomNames; static const StatisticNameVector m_DefaultNames; }; using StatisticsVariantType = ImageStatisticsObject::StatisticsVariantType; using RealType = ImageStatisticsObject::RealType; using IndexType = ImageStatisticsObject::IndexType; using VoxelCountType = ImageStatisticsObject::VoxelCountType; using TimeStepVectorType = std::vector; TimeStepVectorType GetExistingTimeSteps(LabelValueType labelValue) const; /** Value that can be used to query for the statistic if no mask was provided.*/ - const static LabelValueType NO_MASK_LABEL_VALUE = Label::UNLABELED_VALUE; + static constexpr LabelValueType NO_MASK_LABEL_VALUE = Label::UNLABELED_VALUE; using LabelValueVectorType = LabelSetImage::LabelValueVectorType; LabelValueVectorType GetExistingLabelValues() const; /** @brief Deletes all stored values*/ void Reset(); const ImageStatisticsObject& GetStatistics(LabelValueType labelValue, TimeStepType timeStep) const; /** @brief Sets the statisticObject for the given Timestep @pre timeStep must be valid */ void SetStatistics(LabelValueType labelValue, TimeStepType timeStep, const ImageStatisticsObject& statistics); /** @brief Checks if the Time step exists @pre timeStep must be valid */ bool StatisticsExist(LabelValueType labelValue, TimeStepType timeStep) const; /** /brief Returns the histogram of the passed time step. @pre timeStep must be valid*/ const HistogramType* GetHistogram(LabelValueType labelValue, TimeStepType timeStep) const; bool IgnoresZeroVoxel() const; bool IsWIP() const; protected: ImageStatisticsContainer(); void PrintSelf(std::ostream &os, itk::Indent indent) const override; private: itk::LightObject::Pointer InternalClone() const override; using TimeStepMapType = std::map; using LabelMapType = std::map; LabelMapType m_LabelTimeStep2StatisticsMap; }; MITKIMAGESTATISTICS_EXPORT ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector GetAllStatisticNames(const ImageStatisticsContainer* container); MITKIMAGESTATISTICS_EXPORT ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector GetAllStatisticNames(std::vector containers); } #endif diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp index 1433ab4c50..13b831a616 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp @@ -1,520 +1,521 @@ /*============================================================================ 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 "QmitkImageStatisticsTreeModel.h" #include "QmitkImageStatisticsTreeItem.h" #include "mitkImageStatisticsContainerManager.h" #include "mitkProportionalTimeGeometry.h" #include "mitkStatisticsToImageRelationRule.h" #include "mitkStatisticsToMaskRelationRule.h" #include "QmitkStyleManager.h" QmitkImageStatisticsTreeModel::QmitkImageStatisticsTreeModel(QObject *parent) : QmitkAbstractDataStorageModel(parent) { m_RootItem = std::make_unique(); } QmitkImageStatisticsTreeModel ::~QmitkImageStatisticsTreeModel() { // set data storage to nullptr so that the event listener gets removed this->SetDataStorage(nullptr); }; void QmitkImageStatisticsTreeModel::DataStorageChanged() { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::NodePredicateChanged() { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } int QmitkImageStatisticsTreeModel::columnCount(const QModelIndex& /*parent*/) const { int columns = m_StatisticNames.size() + 1; return columns; } int QmitkImageStatisticsTreeModel::rowCount(const QModelIndex &parent) const { QmitkImageStatisticsTreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = m_RootItem.get(); else parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); } QVariant QmitkImageStatisticsTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QmitkImageStatisticsTreeItem* item = static_cast(index.internalPointer()); if (role == Qt::DisplayRole) { return item->data(index.column()); } else if (role == Qt::DecorationRole && index.column() == 0) { if (item->isWIP() && item->childCount() == 0) return QVariant(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/hourglass-half-solid.svg"))); else if (!item->isWIP()) { auto label = item->GetLabelInstance(); if (label.IsNotNull()) { QPixmap pixmap(QSize(20,20)); QColor color(label->GetColor().GetRed() * 255, label->GetColor().GetGreen() * 255, label->GetColor().GetBlue() * 255); pixmap.fill(color); return QVariant(QIcon(pixmap)); } } } return QVariant(); } QModelIndex QmitkImageStatisticsTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); QmitkImageStatisticsTreeItem *parentItem; if (!parent.isValid()) parentItem = m_RootItem.get(); else parentItem = static_cast(parent.internalPointer()); QmitkImageStatisticsTreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex QmitkImageStatisticsTreeModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); QmitkImageStatisticsTreeItem *childItem = static_cast(child.internalPointer()); QmitkImageStatisticsTreeItem *parentItem = childItem->parentItem(); if (parentItem == m_RootItem.get()) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } Qt::ItemFlags QmitkImageStatisticsTreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return {}; return QAbstractItemModel::flags(index); } QVariant QmitkImageStatisticsTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation)) { if (section == 0) { return m_HeaderFirstColumn; } else { return QVariant(m_StatisticNames.at(section - 1).c_str()); } } return QVariant(); } void QmitkImageStatisticsTreeModel::SetImageNodes(const std::vector &nodes) { std::vector> tempNodes; for (const auto &node : nodes) { auto data = node->GetData(); if (data) { auto timeSteps = data->GetTimeSteps(); for (unsigned int i = 0; i < timeSteps; i++) { tempNodes.push_back(std::make_pair(node, i)); } } } emit beginResetModel(); m_TimeStepResolvedImageNodes = std::move(tempNodes); m_ImageNodes = nodes; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::SetMaskNodes(const std::vector &nodes) { std::vector> tempNodes; for (const auto &node : nodes) { auto data = node->GetData(); if (data) { auto timeSteps = data->GetTimeSteps(); // special case: apply one mask to each time step of an 4D image if (timeSteps == 1 && m_TimeStepResolvedImageNodes.size() > 1) { timeSteps = m_TimeStepResolvedImageNodes.size(); } for (unsigned int i = 0; i < timeSteps; i++) { tempNodes.push_back(std::make_pair(node, i)); } } } emit beginResetModel(); m_TimeStepResolvedMaskNodes = std::move(tempNodes); m_MaskNodes = nodes; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::Clear() { emit beginResetModel(); m_Statistics.clear(); m_ImageNodes.clear(); m_TimeStepResolvedImageNodes.clear(); m_MaskNodes.clear(); m_StatisticNames.clear(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::SetIgnoreZeroValueVoxel(bool _arg) { if (m_IgnoreZeroValueVoxel != _arg) { emit beginResetModel(); m_IgnoreZeroValueVoxel = _arg; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } bool QmitkImageStatisticsTreeModel::GetIgnoreZeroValueVoxel() const { return this->m_IgnoreZeroValueVoxel; } void QmitkImageStatisticsTreeModel::SetHistogramNBins(unsigned int nbins) { if (m_HistogramNBins != nbins) { emit beginResetModel(); m_HistogramNBins = nbins; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } unsigned int QmitkImageStatisticsTreeModel::GetHistogramNBins() const { return this->m_HistogramNBins; } void QmitkImageStatisticsTreeModel::UpdateByDataStorage() { StatisticsContainerVector newStatistics; auto datamanager = m_DataStorage.Lock(); if (datamanager.IsNotNull()) { for (const auto &image : m_ImageNodes) { if (m_MaskNodes.empty()) { auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), nullptr, m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false); if (stats.IsNotNull()) { newStatistics.emplace_back(stats); } } else { for (const auto &mask : m_MaskNodes) { auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData(), m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false); if (stats.IsNotNull()) { newStatistics.emplace_back(stats); } } } } if (!newStatistics.empty()) { emit dataAvailable(); } } { std::lock_guard locked(m_Mutex); m_Statistics = newStatistics; m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics); BuildHierarchicalModel(); m_BuildTime.Modified(); } } void AddTimeStepTreeItems(const mitk::ImageStatisticsContainer* statistic, const mitk::DataNode* imageNode, const mitk::DataNode* maskNode, mitk::ImageStatisticsContainer::LabelValueType labelValue, const std::vector& statisticNames, bool isWIP, QmitkImageStatisticsTreeItem* parentItem, bool& hasMultipleTimesteps) { // 4. hierarchy level: time steps (optional, only if >1 time step) if (statistic->GetTimeSteps() > 1) { for (unsigned int i = 0; i < statistic->GetTimeSteps(); i++) { QString timeStepLabel = "[" + QString::number(i) + "] " + QString::number(statistic->GetTimeGeometry()->TimeStepToTimePoint(i)) + " ms"; if (statistic->StatisticsExist(labelValue, i)) { auto statisticsItem = new QmitkImageStatisticsTreeItem( statistic->GetStatistics(labelValue,i), statisticNames, timeStepLabel, isWIP, parentItem, imageNode, maskNode); parentItem->appendChild(statisticsItem); } else { - auto statisticsItem = new QmitkImageStatisticsTreeItem(statisticNames, QStringLiteral("N/A"), isWIP, parentItem, imageNode, maskNode); + auto statisticsItem = new QmitkImageStatisticsTreeItem(statisticNames, timeStepLabel, parentItem, imageNode, maskNode); + parentItem->appendChild(statisticsItem); } } } hasMultipleTimesteps = hasMultipleTimesteps || (statistic->GetTimeSteps() > 1); } void AddLabelTreeItems(const mitk::ImageStatisticsContainer* statistic, const mitk::DataNode* imageNode, const mitk::DataNode* maskNode, mitk::ImageStatisticsContainer::LabelValueVectorType labelValues, const std::vector& statisticNames, bool isWIP, QmitkImageStatisticsTreeItem* parentItem, bool& hasMultipleTimesteps) { // 3. hierarchy level: labels (optional, only if labels >1) for (const auto labelValue : labelValues) { if (labelValue != mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE) { //currently we only show statistics of the labeled pixel if a mask is provided QString labelLabel = QStringLiteral("unnamed label"); const auto multiLabelSeg = dynamic_cast(maskNode->GetData()); const mitk::Label* labelInstance = nullptr; if (nullptr != multiLabelSeg) { labelInstance = multiLabelSeg->GetLabel(labelValue); labelLabel = QString::fromStdString(labelInstance->GetName() + " [" + labelInstance->GetTrackingID() + "]"); } QmitkImageStatisticsTreeItem* labelItem = nullptr; if (statistic->GetTimeSteps() == 1) { // add statistical values directly in this hierarchy level auto statisticsObject = statistic->GetStatistics(labelValue, 0); labelItem = new QmitkImageStatisticsTreeItem(statisticsObject, statisticNames, labelLabel, isWIP, parentItem, imageNode, maskNode, labelInstance); } else { labelItem = new QmitkImageStatisticsTreeItem(statisticNames, labelLabel, isWIP, parentItem, imageNode, maskNode, labelInstance); AddTimeStepTreeItems(statistic, imageNode, maskNode, labelValue, statisticNames, isWIP, labelItem, hasMultipleTimesteps); } parentItem->appendChild(labelItem); } } } void QmitkImageStatisticsTreeModel::BuildHierarchicalModel() { // reset old model m_RootItem.reset(new QmitkImageStatisticsTreeItem()); bool hasMask = false; bool hasMultipleTimesteps = false; std::map dataNodeToTreeItem; for (const auto &statistic : m_Statistics) { bool isWIP = statistic->IsWIP(); // get the connected image data node/mask data node auto imageRule = mitk::StatisticsToImageRelationRule::New(); auto imageOfStatisticsPredicate = imageRule->GetDestinationsDetector(statistic); auto imageFinding = std::find_if(m_ImageNodes.begin(), m_ImageNodes.end(), [&imageOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return imageOfStatisticsPredicate->CheckNode(testNode); }); auto maskRule = mitk::StatisticsToMaskRelationRule::New(); auto maskOfStatisticsPredicate = maskRule->GetDestinationsDetector(statistic); auto maskFinding = std::find_if(m_MaskNodes.begin(), m_MaskNodes.end(), [&maskOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return maskOfStatisticsPredicate->CheckNode(testNode); }); if (imageFinding == m_ImageNodes.end()) { mitkThrow() << "no image found connected to statistic" << statistic << " Aborting."; } auto& image = *imageFinding; // image: 1. hierarchy level QmitkImageStatisticsTreeItem *imageItem = nullptr; auto search = dataNodeToTreeItem.find(image); if (search != dataNodeToTreeItem.end()) { // the tree item was created previously imageItem = search->second; } else { QString imageLabel = QString::fromStdString(image->GetName()); if (statistic->GetTimeSteps() == 1 && maskFinding == m_MaskNodes.end()) { auto labelValue = isWIP ? mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE : statistic->GetExistingLabelValues().front(); auto statisticsObject = isWIP ? mitk::ImageStatisticsContainer::ImageStatisticsObject() : statistic->GetStatistics(labelValue, 0); // create the final statistics tree item imageItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, imageLabel, isWIP, m_RootItem.get(), image); } else { imageItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, imageLabel, isWIP, m_RootItem.get(), image); } m_RootItem->appendChild(imageItem); dataNodeToTreeItem.emplace(image, imageItem); } if (maskFinding != m_MaskNodes.end()) { const auto labelValues = statistic->GetExistingLabelValues(); //currently we not support showing the statistics for unlabeled pixels if a mask exist // mask: 2. hierarchy level exists auto& mask = *maskFinding; QString maskLabel = QString::fromStdString(mask->GetName()); QmitkImageStatisticsTreeItem* maskItem; if (statistic->GetTimeSteps() == 1 && labelValues.size() == 1) { // add statistical values directly in this hierarchy level auto statisticsObject = isWIP ? mitk::ImageStatisticsContainer::ImageStatisticsObject() : statistic->GetStatistics(labelValues.front(), 0); maskItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, maskLabel, isWIP, imageItem, image, mask); } else if(labelValues.empty()) { //all labels are empty -> no stats are computed maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, imageItem, image, mask); } else { maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, isWIP, imageItem, image, mask); // 3. hierarchy level: labels (optional, only if more then one label in statistic) if (labelValues.size() > 1) { AddLabelTreeItems(statistic, image, mask, labelValues, m_StatisticNames, isWIP, maskItem, hasMultipleTimesteps); } else if (!labelValues.empty()) { mitk::Label::PixelType labelValue = isWIP ? 0 : labelValues.front(); AddTimeStepTreeItems(statistic, image, mask, labelValue, m_StatisticNames, isWIP, maskItem, hasMultipleTimesteps); } } imageItem->appendChild(maskItem); hasMask = true; } else { //no mask -> but multi time step auto labelValue = isWIP ? mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE : statistic->GetExistingLabelValues().front(); AddTimeStepTreeItems(statistic, image, nullptr, labelValue, m_StatisticNames, isWIP, imageItem, hasMultipleTimesteps); } } QString headerString = "Images"; if (hasMask) { headerString += "/Masks"; } if (hasMultipleTimesteps) { headerString += "/Timesteps"; } m_HeaderFirstColumn = headerString; } void QmitkImageStatisticsTreeModel::NodeRemoved(const mitk::DataNode* changedNode) { bool isRelevantNode = (nullptr != dynamic_cast(changedNode->GetData())); if (isRelevantNode) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } void QmitkImageStatisticsTreeModel::NodeAdded(const mitk::DataNode * changedNode) { bool isRelevantNode = (nullptr != dynamic_cast(changedNode->GetData())); if (isRelevantNode) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } void QmitkImageStatisticsTreeModel::NodeChanged(const mitk::DataNode * changedNode) { bool isRelevantNode = m_ImageNodes.end() != std::find(m_ImageNodes.begin(), m_ImageNodes.end(), changedNode); isRelevantNode = isRelevantNode || (m_MaskNodes.end() != std::find(m_MaskNodes.begin(), m_MaskNodes.end(), changedNode)); isRelevantNode = isRelevantNode || (nullptr != dynamic_cast(changedNode->GetData())); if (isRelevantNode) { if (m_BuildTime.GetMTime() < changedNode->GetData()->GetMTime()) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } } diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h index 8452a9cea3..649affabc6 100644 --- a/Modules/Multilabel/mitkLabel.h +++ b/Modules/Multilabel/mitkLabel.h @@ -1,115 +1,115 @@ /*============================================================================ 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. ============================================================================*/ #ifndef mitkLabel_h #define mitkLabel_h #include "MitkMultilabelExports.h" #include #include #include #include namespace mitk { //## //##Documentation //## @brief A data structure describing a label. //## @ingroup Data //## class MITKMULTILABEL_EXPORT Label : public PropertyList { public: mitkClassMacro(Label, mitk::PropertyList); typedef unsigned short PixelType; itkNewMacro(Self); mitkNewMacro2Param(Self, PixelType, const std::string&); /// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535 static const PixelType MAX_LABEL_VALUE; //** Value indicating pixels that are not labeled at all.*/ - const static PixelType UNLABELED_VALUE = 0; + static constexpr PixelType UNLABELED_VALUE = 0; void SetLocked(bool locked); bool GetLocked() const; void SetVisible(bool visible); bool GetVisible() const; void SetOpacity(float opacity); float GetOpacity() const; void SetName(const std::string &name); std::string GetName() const; std::string GetTrackingID() const; void SetCenterOfMassIndex(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassIndex() const; void SetCenterOfMassCoordinates(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassCoordinates() const; void SetColor(const mitk::Color &); const mitk::Color &GetColor() const; void SetValue(PixelType pixelValue); PixelType GetValue() const; void SetLayer(unsigned int layer); unsigned int GetLayer() const; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; using itk::Object::Modified; void Modified() { Superclass::Modified(); } Label(); Label(PixelType value, const std::string& name); ~Label() override; protected: void PrintSelf(std::ostream &os, itk::Indent indent) const override; Label(const Label &other); private: itk::LightObject::Pointer InternalClone() const override; }; using LabelVector = std::vector; using ConstLabelVector = std::vector; /** * @brief Equal A function comparing two labels for beeing equal in data * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - Lebel equality via Equal-PropetyList * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide, const mitk::Label &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif