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 #include "itkShowDistanceMap.h"
20 #include "itkDanielssonDistanceMapImageFilter.h"
21 #include "itkStdStreamStateSave.h"
22 
itkDanielssonDistanceMapImageFilterTest(int,char * [])23 int itkDanielssonDistanceMapImageFilterTest(int, char* [] )
24 {
25 // Save the format stream variables for std::cout
26 // They will be restored when coutState goes out of scope
27   itk::StdStreamStateSave coutState(std::cout);
28 
29   std::cout << "Test ITK Danielsson Distance Map" << std::endl << std::endl;
30   std::cout << "Compute the distance map of a 9x9 image" << std::endl;
31   std::cout << "with a point at (4,4) (value=1)" << std::endl << std::endl;
32   std::cout << "with a point at (1,6) (value=2)" << std::endl << std::endl;
33 
34 
35   using myImageType2D1 = itk::Image<unsigned char, 2>;
36   using myImageType2D2 = itk::Image<float, 2>;
37 
38   /* Allocate the 2D image */
39   myImageType2D1::SizeType size2D = {{9,9}};
40   myImageType2D1::IndexType index2D = {{0,0}};
41   myImageType2D1::RegionType region2D;
42   region2D.SetSize( size2D );
43   region2D.SetIndex( index2D );
44 
45   myImageType2D1::Pointer inputImage2D = myImageType2D1::New();
46   inputImage2D->SetLargestPossibleRegion( region2D );
47   inputImage2D->SetBufferedRegion( region2D );
48   inputImage2D->SetRequestedRegion( region2D );
49   inputImage2D->Allocate( true );
50 
51   /* Set pixel (4,4) with the value 1
52    * and pixel (1,6) with the value 2
53    * The Danielsson Distance is performed for each pixel with a value > 0
54    * The ClosestPoints computation is based on the value of the pixel.
55    */
56 
57   index2D[0] = 4;
58   index2D[1] = 4;
59   inputImage2D->SetPixel( index2D, 1);
60   index2D[0] = 1;
61   index2D[1] = 6;
62   inputImage2D->SetPixel( index2D, 2);
63 
64   /* Create Danielsson Distance Map filter */
65   using myFilterType2D = itk::DanielssonDistanceMapImageFilter<
66                                             myImageType2D1,
67                                             myImageType2D2 >;
68 
69   myFilterType2D::Pointer filter2D = myFilterType2D::New();
70 
71   filter2D->SetInput( inputImage2D );
72 
73   myImageType2D2::Pointer outputDistance2D = filter2D->GetOutput();
74 
75   using VoronoiImageType = myFilterType2D::VoronoiImageType;
76 
77   VoronoiImageType::Pointer outputVoronoi2D  = filter2D->GetVoronoiMap();
78 
79   myFilterType2D::VectorImagePointer
80                     outputComponents = filter2D->GetVectorDistanceMap();
81 
82   filter2D->Update();
83 
84   ShowDistanceMap(outputDistance2D);
85   std::cout << "Voronoi Map Image 2D" << std::endl << std::endl;
86   ShowDistanceMap(outputVoronoi2D);
87 
88   /* Show VectorsComponents Points map */
89   std::cout << std::endl << std::endl;
90   std::cout << "Components Map Image 2D" << std::endl << std::endl;
91 
92   itk::ImageSliceConstIteratorWithIndex< myFilterType2D::VectorImageType > it2D4(
93                                 outputComponents,
94                                 outputComponents->GetRequestedRegion() );
95 
96   it2D4.SetFirstDirection( 0 );
97   it2D4.SetSecondDirection( 1 );
98 
99   while( !it2D4.IsAtEnd() )
100     {
101     while( !it2D4.IsAtEndOfSlice() )
102       {
103       while( !it2D4.IsAtEndOfLine() )
104         {
105         std::cout << "[";
106         for (unsigned int i=0;i<2;i++)
107           {
108           std::cout << it2D4.Get()[i];
109           if(i==0)
110             {
111             std::cout << ",";
112             }
113           }
114         std::cout << "]";
115         std::cout << "\t";
116         ++it2D4;
117 
118         }
119       std::cout << std::endl;
120       it2D4.NextLine();
121       }
122     it2D4.NextSlice();
123     }
124 
125 
126   /* Test Squared Distance functionality */
127   myImageType2D2::IndexType index;
128   index[0] = 0;
129   index[1] = 0;
130   const double distance1 = outputDistance2D->GetPixel( index );
131 
132   filter2D->SquaredDistanceOn();
133 
134   if( filter2D->GetSquaredDistance() != true )
135     {
136     std::cerr << "filter2D->GetSquaredDistance() != true" <<std::endl;
137     return EXIT_FAILURE;
138     }
139 
140   filter2D->SetSquaredDistance( true );
141   filter2D->Update();
142 
143   const double distance2 = outputDistance2D->GetPixel( index );
144   const myImageType2D2::PixelType epsilon = 1e-5;
145   if( std::fabs( distance2 - distance1 * distance1 ) > epsilon )
146     {
147     std::cerr << "Error in use of the SetSquaredDistance() method" << std::endl;
148     return EXIT_FAILURE;
149     }
150 
151   std::cout << "Squared Distance Map " << std::endl;
152   ShowDistanceMap(outputDistance2D);
153 
154 
155   /* Test for images with anisotropic spacing */
156   myImageType2D1::SpacingType anisotropicSpacing;
157 
158   anisotropicSpacing[0] = 1.0;
159   anisotropicSpacing[1] = 5.0;
160 
161   inputImage2D->SetSpacing( anisotropicSpacing );
162 
163   inputImage2D->FillBuffer( 0 );
164 
165   index2D[0] = 4;
166   index2D[1] = 4;
167   inputImage2D->SetPixel( index2D, 1);
168 
169   filter2D->SetInput( inputImage2D );
170   filter2D->SetInputIsBinary(true);
171 
172   if( filter2D->GetInputIsBinary() != true )
173     {
174     std::cerr << "filter2D->GetInputIsBinary() != true" <<std::endl;
175     return EXIT_FAILURE;
176     }
177 
178 
179   filter2D->UseImageSpacingOn();
180 
181   if( filter2D->GetUseImageSpacing() != true )
182     {
183     std::cerr << "filter2D->GetUseImageSpacing() != true" << std::endl;
184     return EXIT_FAILURE;
185     }
186 
187   filter2D->SetUseImageSpacing(true);
188   filter2D->Update();
189 
190   index2D[1] = 5;
191   auto expectedValue = static_cast<myImageType2D2::PixelType>(anisotropicSpacing[1]);
192   expectedValue *= expectedValue;
193   myImageType2D2::PixelType pixelValue =
194     filter2D->GetOutput()->GetPixel( index2D );
195   if ( std::fabs( expectedValue - pixelValue ) > epsilon )
196     {
197     std::cerr << "Error when image spacing is anisotropic." << std::endl;
198     std::cerr << "Pixel value was " << pixelValue << ", expected " << expectedValue << std::endl;
199     return EXIT_FAILURE;
200     }
201 
202   ShowDistanceMap(outputDistance2D);
203 
204   // Create a large 3D image with a small foreground object.  The foreground is denoted by a pixel value of 0,
205   // and the background by a non-zero pixel value.  This will test speedups to the code that ignore all background
206   // pixels in the computation since those pixels do not influence the result.
207 
208   // Allocate the 3D image
209   using ImageType3D = itk::Image< float, 3>;
210   ImageType3D::SizeType size3D = {{200,200,200}};
211   ImageType3D::IndexType index3D = {{0,0}};
212   ImageType3D::RegionType region3D;
213   region3D.SetSize( size3D );
214   region3D.SetIndex( index3D );
215   ImageType3D::Pointer inputImage3D = ImageType3D::New();
216   inputImage3D->SetRegions( region3D );
217   inputImage3D->Allocate();
218   inputImage3D->FillBuffer(1);
219 
220   // Set a few pixels in the middle of the image to 0.  These are the foreground pixels for which the distance will be solved.
221   ImageType3D::IndexType foregroundIndex;
222   ImageType3D::SizeType foregroundSize;
223   for( unsigned int i = 0; i < 3; i++ )
224   {
225     foregroundSize[i] =  5;
226     foregroundIndex[i] = (size3D[i]/2) - foregroundSize[i]/2;
227   }
228   ImageType3D::RegionType foregroundRegion;
229   foregroundRegion.SetSize( foregroundSize );
230   foregroundRegion.SetIndex( foregroundIndex );
231 
232   itk::ImageRegionIteratorWithIndex<ImageType3D> it3D(inputImage3D,foregroundRegion);
233   for( it3D.GoToBegin(); !it3D.IsAtEnd(); ++it3D )
234   {
235     it3D.Set( 0 );
236   }
237 
238   // Create Danielsson Distance Map filter
239   using myFilterType3D = itk::DanielssonDistanceMapImageFilter< ImageType3D, ImageType3D >;
240 
241   myFilterType3D::Pointer filter3D = myFilterType3D::New();
242 
243   filter3D->SetInput( inputImage3D );
244   filter3D->Update();
245 
246   return EXIT_SUCCESS;
247 }
248