1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 
19 
20 #include "itkLevelSetDenseImage.h"
21 #include "itkImageRegionIteratorWithIndex.h"
22 
23 #include "itkLevelSetTestFunction.h"
24 
25 /**
26  * \class ToleranceChecker
27  * \brief Compare values to see if they are within tolerance.
28  */
29 template< typename RealType >
30 class ToleranceChecker
31 {
32 public:
ToleranceChecker()33   ToleranceChecker(): m_Tolerance( 1e-8 )
34   {}
35 
IsOutsideTolerance(const RealType & value,const RealType & theoreticalValue) const36   bool IsOutsideTolerance( const RealType & value, const RealType & theoreticalValue ) const
37     {
38     // ignore if they are both effectively zero
39     if( std::max( itk::Math::abs( value ), itk::Math::abs( theoreticalValue ) ) <  50 * itk::Math::eps )
40       {
41       return false;
42       }
43     if( this->GetFractionalError( value, theoreticalValue ) > m_Tolerance )
44       {
45       return true;
46       }
47     return false;
48     }
49 
GetFractionalError(const RealType & value,const RealType & theoreticalValue) const50   RealType GetFractionalError( const RealType & value, const RealType & theoreticalValue ) const
51     {
52     RealType fractionalError = itk::Math::abs( theoreticalValue - value ) /
53       ( itk::Math::abs( theoreticalValue ) + 20* itk::Math::eps );
54     return fractionalError;
55     }
56 
57   /** Set fractional tolerance. */
SetTolerance(const RealType & tolerance)58   void SetTolerance( const RealType & tolerance )
59     {
60     m_Tolerance = tolerance;
61     }
62 
63 private:
64   RealType m_Tolerance;
65 
66 };
67 
itkLevelSetDenseImageTest(int,char * [])68 int itkLevelSetDenseImageTest( int , char* [] )
69 {
70   constexpr unsigned int Dimension = 2;
71 
72   using PixelType = float;
73 
74   using ImageType = itk::Image< PixelType, Dimension >;
75   using LevelSetType = itk::LevelSetDenseImage< ImageType >;
76 
77   ImageType::IndexType index;
78   index[0] = 0;
79   index[1] = 0;
80 
81   ImageType::SizeType size;
82   size[0] = 10;
83   size[1] = 20;
84 
85   ImageType::RegionType region;
86   region.SetIndex( index );
87   region.SetSize( size );
88 
89   PixelType zeroValue = 0.;
90 
91   ImageType::SpacingType spacing;
92   spacing[0] = 0.02 / size[0];
93   spacing[1] = 0.02 / size[1];
94 
95   ImageType::PointType origin;
96   origin[0] = 3.99;
97   origin[1] = 3.99;
98 
99   ImageType::Pointer input = ImageType::New();
100   input->SetRegions( region );
101   input->SetSpacing( spacing );
102   input->SetOrigin(  origin );
103   input->Allocate();
104   input->FillBuffer( zeroValue );
105 
106   itk::ImageRegionIteratorWithIndex< ImageType > it( input,
107                                               input->GetLargestPossibleRegion() );
108 
109   it.GoToBegin();
110 
111   ImageType::IndexType idx;
112   ImageType::PointType pt;
113 
114   using TestFunctionType = itk::LevelSetTestFunction< PixelType >;
115   TestFunctionType::Pointer testFunction = TestFunctionType::New();
116 
117   while( !it.IsAtEnd() )
118     {
119     idx = it.GetIndex();
120     input->TransformIndexToPhysicalPoint( idx, pt );
121 
122     PixelType tempValue = testFunction->Evaluate( pt );
123     it.Set( tempValue );
124 
125 
126     ++it;
127     }
128 
129   LevelSetType::Pointer level_set = LevelSetType::New();
130   level_set->SetImage( input );
131 
132   idx[0] = 9;
133   idx[1] = 18;
134   input->TransformIndexToPhysicalPoint( idx, pt );
135   LevelSetType::OutputType theoreticalValue = testFunction->Evaluate( pt );
136   LevelSetType::OutputType value = level_set->Evaluate( idx );
137 
138   ToleranceChecker< double > toleranceChecker;
139 
140   toleranceChecker.SetTolerance( 1e-8 );
141   it.GoToBegin();
142   while( !it.IsAtEnd() )
143     {
144     idx = it.GetIndex();
145     input->TransformIndexToPhysicalPoint( idx, pt );
146 
147     theoreticalValue = testFunction->Evaluate( pt );
148     value            = level_set->Evaluate( idx );
149     if( toleranceChecker.IsOutsideTolerance( value, theoreticalValue ) )
150       {
151       std::cout << "Index:" << idx << " *EvaluateTestFail* " << value << " != "
152                 << theoreticalValue << std::endl;
153       return EXIT_FAILURE;
154       }
155 
156     if( level_set->IsInside( idx ) != ( theoreticalValue <= 0. ) )
157       {
158       std::cerr << "if( testFunction->IsInside( pt ) != ( theoreticalValue <= 0. ) )" << std::endl;
159       std::cerr << "pt : " << pt << std::endl;
160       std::cerr << "theoreticalValue: " << theoreticalValue << std::endl;
161       return EXIT_FAILURE;
162       }
163 
164     ++it;
165     }
166 
167   LevelSetType::GradientType gradient;
168   LevelSetType::GradientType theoreticalGradient;
169 
170   toleranceChecker.SetTolerance( 0.1 );
171   it.GoToBegin();
172   while( !it.IsAtEnd() )
173     {
174     idx = it.GetIndex();
175     input->TransformIndexToPhysicalPoint( idx, pt );
176 
177     theoreticalGradient = testFunction->EvaluateGradient( pt );
178     gradient            = level_set->EvaluateGradient( idx );
179     if( toleranceChecker.IsOutsideTolerance( gradient[0], theoreticalGradient[0] ) ||
180         toleranceChecker.IsOutsideTolerance( gradient[1], theoreticalGradient[1] ) )
181       {
182       std::cout << "Index:" << idx << " Point: " << pt
183         << " Error: [" << toleranceChecker.GetFractionalError( gradient[0], theoreticalGradient[0] )
184         << ',' << toleranceChecker.GetFractionalError( gradient[1], theoreticalGradient[1] ) << "] "
185         <<" *EvaluateGradientTestFail* " << gradient << " != " << theoreticalGradient << std::endl;
186       return EXIT_FAILURE;
187       }
188 
189     ++it;
190     }
191 
192   /** \todo more thorough testing as with the gradient above for hessian,
193    * laplacian, gradient norm. */
194   idx[0] = 9;
195   idx[1] = 18;
196   input->TransformIndexToPhysicalPoint( idx, pt );
197   LevelSetType::HessianType hessian = level_set->EvaluateHessian( idx );
198   std::cout << "hessian = " << std::endl << hessian << std::endl;
199 
200   if ( itk::Math::abs( itk::Math::abs( hessian[0][0] ) - 499.998 ) / 499.998 > 5e-2 )
201     {
202     std::cout << idx << " *HessianTestFail* " << itk::Math::abs( hessian[0][0] ) << " != "
203               << itk::Math::abs( hessian[1][1] ) << std::endl;
204     return EXIT_FAILURE;
205     }
206 
207   LevelSetType::OutputRealType laplacian = level_set->EvaluateLaplacian( idx );
208   std::cout << "laplacian = " << laplacian << std::endl;
209 
210   LevelSetType::OutputRealType gradientnorm = level_set->EvaluateGradientNorm( idx );
211   std::cout <<"gradient norm = " << gradientnorm << std::endl;
212 
213   if( itk::Math::abs( 1 - gradientnorm ) > 5e-2 )
214     {
215     std::cout << idx << " *GradientNormFail* " << gradientnorm << " != "
216               << 1 << std::endl;
217     return EXIT_FAILURE;
218     }
219 
220   LevelSetType::OutputRealType meancurvature = level_set->EvaluateMeanCurvature( idx );
221   std::cout <<"mean curvature = " << meancurvature << std::endl;
222 
223   return EXIT_SUCCESS;
224 }
225