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