/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "itkLevelSetDenseImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLevelSetTestFunction.h" #include "itkTestingMacros.h" /** * \class ToleranceChecker * \brief Compare values to see if they are within tolerance. */ template class ToleranceChecker { public: ToleranceChecker() : m_Tolerance(1e-8) {} bool IsOutsideTolerance(const RealType & value, const RealType & theoreticalValue) const { // ignore if they are both effectively zero if (std::max(itk::Math::abs(value), itk::Math::abs(theoreticalValue)) < 50 * itk::Math::eps) { return false; } if (this->GetFractionalError(value, theoreticalValue) > m_Tolerance) { return true; } return false; } RealType GetFractionalError(const RealType & value, const RealType & theoreticalValue) const { RealType fractionalError = itk::Math::abs(theoreticalValue - value) / (itk::Math::abs(theoreticalValue) + 20 * itk::Math::eps); return fractionalError; } /** Set fractional tolerance. */ void SetTolerance(const RealType & tolerance) { m_Tolerance = tolerance; } private: RealType m_Tolerance; }; int itkLevelSetDenseImageTest(int, char *[]) { constexpr unsigned int Dimension = 2; using PixelType = float; using ImageType = itk::Image; using LevelSetType = itk::LevelSetDenseImage; ImageType::IndexType index; index[0] = 0; index[1] = 0; ImageType::SizeType size; size[0] = 10; size[1] = 20; ImageType::RegionType region; region.SetIndex(index); region.SetSize(size); PixelType zeroValue = 0.; ImageType::SpacingType spacing; spacing[0] = 0.02 / size[0]; spacing[1] = 0.02 / size[1]; ImageType::PointType origin; origin[0] = 3.99; origin[1] = 3.99; auto input = ImageType::New(); input->SetRegions(region); input->SetSpacing(spacing); input->SetOrigin(origin); input->Allocate(); input->FillBuffer(zeroValue); itk::ImageRegionIteratorWithIndex it(input, input->GetLargestPossibleRegion()); it.GoToBegin(); ImageType::IndexType idx; ImageType::PointType pt; using TestFunctionType = itk::LevelSetTestFunction; auto testFunction = TestFunctionType::New(); while (!it.IsAtEnd()) { idx = it.GetIndex(); input->TransformIndexToPhysicalPoint(idx, pt); PixelType tempValue = testFunction->Evaluate(pt); it.Set(tempValue); ++it; } auto levelSet = LevelSetType::New(); ITK_EXERCISE_BASIC_OBJECT_METHODS(levelSet, LevelSetDenseImage, DiscreteLevelSetImage); levelSet->SetImage(input); ITK_TEST_SET_GET_VALUE(input, levelSet->GetImage()); idx[0] = 9; idx[1] = 18; input->TransformIndexToPhysicalPoint(idx, pt); LevelSetType::OutputType theoreticalValue = testFunction->Evaluate(pt); LevelSetType::OutputType value = levelSet->Evaluate(idx); ToleranceChecker toleranceChecker; toleranceChecker.SetTolerance(1e-8); it.GoToBegin(); while (!it.IsAtEnd()) { idx = it.GetIndex(); input->TransformIndexToPhysicalPoint(idx, pt); theoreticalValue = testFunction->Evaluate(pt); value = levelSet->Evaluate(idx); if (toleranceChecker.IsOutsideTolerance(value, theoreticalValue)) { std::cout << "Index:" << idx << " *EvaluateTestFail* " << value << " != " << theoreticalValue << std::endl; return EXIT_FAILURE; } if (levelSet->IsInside(idx) != (theoreticalValue <= 0.)) { std::cerr << "if( testFunction->IsInside( pt ) != ( theoreticalValue <= 0. ) )" << std::endl; std::cerr << "pt : " << pt << std::endl; std::cerr << "theoreticalValue: " << theoreticalValue << std::endl; return EXIT_FAILURE; } ++it; } LevelSetType::GradientType gradient; LevelSetType::GradientType theoreticalGradient; toleranceChecker.SetTolerance(0.1); it.GoToBegin(); while (!it.IsAtEnd()) { idx = it.GetIndex(); input->TransformIndexToPhysicalPoint(idx, pt); theoreticalGradient = testFunction->EvaluateGradient(pt); gradient = levelSet->EvaluateGradient(idx); if (toleranceChecker.IsOutsideTolerance(gradient[0], theoreticalGradient[0]) || toleranceChecker.IsOutsideTolerance(gradient[1], theoreticalGradient[1])) { std::cout << "Index:" << idx << " Point: " << pt << " Error: [" << toleranceChecker.GetFractionalError(gradient[0], theoreticalGradient[0]) << ',' << toleranceChecker.GetFractionalError(gradient[1], theoreticalGradient[1]) << "] " << " *EvaluateGradientTestFail* " << gradient << " != " << theoreticalGradient << std::endl; return EXIT_FAILURE; } ++it; } /** \todo more thorough testing as with the gradient above for hessian, * laplacian, gradient norm. */ idx[0] = 9; idx[1] = 18; input->TransformIndexToPhysicalPoint(idx, pt); LevelSetType::HessianType hessian = levelSet->EvaluateHessian(idx); std::cout << "hessian = " << std::endl << hessian << std::endl; if (itk::Math::abs(itk::Math::abs(hessian[0][0]) - 499.998) / 499.998 > 5e-2) { std::cout << idx << " *HessianTestFail* " << itk::Math::abs(hessian[0][0]) << " != " << itk::Math::abs(hessian[1][1]) << std::endl; return EXIT_FAILURE; } LevelSetType::OutputRealType laplacian = levelSet->EvaluateLaplacian(idx); std::cout << "laplacian = " << laplacian << std::endl; LevelSetType::OutputRealType gradientnorm = levelSet->EvaluateGradientNorm(idx); std::cout << "gradient norm = " << gradientnorm << std::endl; if (itk::Math::abs(1 - gradientnorm) > 5e-2) { std::cout << idx << " *GradientNormFail* " << gradientnorm << " != " << 1 << std::endl; return EXIT_FAILURE; } LevelSetType::OutputRealType meancurvature = levelSet->EvaluateMeanCurvature(idx); std::cout << "mean curvature = " << meancurvature << std::endl; return EXIT_SUCCESS; }