1 #ifndef _itkBinaryThinningImageFilter3D_txx
\r
2 #define _itkBinaryThinningImageFilter3D_txx
\r
6 #include "itkBinaryThinningImageFilter3D.h"
\r
7 #include "itkImageRegionConstIterator.h"
\r
8 #include "itkImageRegionIterator.h"
\r
9 #include "itkNeighborhoodIterator.h"
\r
18 template <class TInputImage,class TOutputImage>
\r
19 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
20 ::BinaryThinningImageFilter3D()
\r
23 this->SetNumberOfRequiredOutputs( 1 );
\r
25 OutputImagePointer thinImage = OutputImageType::New();
\r
26 this->SetNthOutput( 0, thinImage.GetPointer() );
\r
31 * Return the thinning Image pointer
\r
33 template <class TInputImage,class TOutputImage>
\r
34 typename BinaryThinningImageFilter3D<
\r
35 TInputImage,TOutputImage>::OutputImageType *
\r
36 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
39 return dynamic_cast< OutputImageType * >(
\r
40 this->ProcessObject::GetOutput(0) );
\r
45 * Prepare data for computation
\r
46 * Copy the input image to the output image, changing from the input
\r
47 * type to the output type.
\r
49 template <class TInputImage,class TOutputImage>
\r
51 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
52 ::PrepareData(void)
\r
55 itkDebugMacro(<< "PrepareData Start");
\r
56 OutputImagePointer thinImage = GetThinning();
\r
58 InputImagePointer inputImage =
\r
59 dynamic_cast<const TInputImage *>( ProcessObject::GetInput(0) );
\r
61 thinImage->SetBufferedRegion( thinImage->GetRequestedRegion() );
\r
62 thinImage->Allocate();
\r
64 typename OutputImageType::RegionType region = thinImage->GetRequestedRegion();
\r
67 ImageRegionConstIterator< TInputImage > it( inputImage, region );
\r
68 ImageRegionIterator< TOutputImage > ot( thinImage, region );
\r
73 itkDebugMacro(<< "PrepareData: Copy input to output");
\r
75 // Copy the input to the output, changing all foreground pixels to
\r
76 // have value 1 in the process.
\r
77 while( !ot.IsAtEnd() )
\r
81 ot.Set( NumericTraits<OutputImagePixelType>::One );
\r
85 ot.Set( NumericTraits<OutputImagePixelType>::Zero );
\r
90 itkDebugMacro(<< "PrepareData End");
\r
94 * Post processing for computing thinning
\r
96 template <class TInputImage,class TOutputImage>
\r
98 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
99 ::ComputeThinImage()
\r
101 itkDebugMacro( << "ComputeThinImage Start");
\r
102 OutputImagePointer thinImage = GetThinning();
\r
104 typename OutputImageType::RegionType region = thinImage->GetRequestedRegion();
\r
106 ConstBoundaryConditionType boundaryCondition;
\r
107 boundaryCondition.SetConstant( 0 );
\r
109 typename NeighborhoodIteratorType::RadiusType radius;
\r
111 NeighborhoodIteratorType ot( radius, thinImage, region );
\r
112 ot.SetBoundaryCondition( boundaryCondition );
\r
114 std::vector < IndexType > simpleBorderPoints;
\r
115 typename std::vector < IndexType >::iterator simpleBorderPointsIt;
\r
118 typedef typename NeighborhoodIteratorType::OffsetType OffsetType;
\r
119 OffsetType N = {{ 0,-1, 0}}; // north
\r
120 OffsetType S = {{ 0, 1, 0}}; // south
\r
121 OffsetType E = {{ 1, 0, 0}}; // east
\r
122 OffsetType W = {{-1, 0, 0}}; // west
\r
123 OffsetType U = {{ 0, 0, 1}}; // up
\r
124 OffsetType B = {{ 0, 0,-1}}; // bottom
\r
126 // prepare Euler LUT [Lee94]
\r
127 int eulerLUT[256];
\r
128 fillEulerLUT( eulerLUT );
\r
129 // Loop through the image several times until there is no change.
\r
130 int unchangedBorders = 0;
\r
131 while( unchangedBorders < 6 ) // loop until no change for all the six border types
\r
133 unchangedBorders = 0;
\r
134 for( int currentBorder = 1; currentBorder <= 6; currentBorder++)
\r
136 // Loop through the image.
\r
137 for ( ot.GoToBegin(); !ot.IsAtEnd(); ++ot )
\r
139 // check if point is foreground
\r
140 if ( ot.GetCenterPixel() != 1 )
\r
142 continue; // current point is already background
\r
144 // check 6-neighbors if point is a border point of type currentBorder
\r
145 bool isBorderPoint = false;
\r
146 if( currentBorder == 1 && ot.GetPixel(N)<=0 )
\r
147 isBorderPoint = true;
\r
148 if( currentBorder == 2 && ot.GetPixel(S)<=0 )
\r
149 isBorderPoint = true;
\r
150 if( currentBorder == 3 && ot.GetPixel(E)<=0 )
\r
151 isBorderPoint = true;
\r
152 if( currentBorder == 4 && ot.GetPixel(W)<=0 )
\r
153 isBorderPoint = true;
\r
154 if( currentBorder == 5 && ot.GetPixel(U)<=0 )
\r
155 isBorderPoint = true;
\r
156 if( currentBorder == 6 && ot.GetPixel(B)<=0 )
\r
157 isBorderPoint = true;
\r
158 if( !isBorderPoint )
\r
160 continue; // current point is not deletable
\r
162 // check if point is the end of an arc
\r
163 int numberOfNeighbors = -1; // -1 and not 0 because the center pixel will be counted as well
\r
164 for( int i = 0; i < 27; i++ ) // i = 0..26
\r
165 if( ot.GetPixel(i)==1 )
\r
166 numberOfNeighbors++;
\r
168 if( numberOfNeighbors == 1 )
\r
170 continue; // current point is not deletable
\r
173 // check if point is Euler invariant
\r
174 if( !isEulerInvariant( ot.GetNeighborhood(), eulerLUT ) )
\r
176 continue; // current point is not deletable
\r
179 // check if point is simple (deletion does not change connectivity in the 3x3x3 neighborhood)
\r
180 if( !isSimplePoint( ot.GetNeighborhood() ) )
\r
182 continue; // current point is not deletable
\r
185 // add all simple border points to a list for sequential re-checking
\r
186 simpleBorderPoints.push_back( ot.GetIndex() );
\r
187 } // end image iteration loop
\r
189 // sequential re-checking to preserve connectivity when
\r
190 // deleting in a parallel way
\r
191 bool noChange = true;
\r
192 for( simpleBorderPointsIt=simpleBorderPoints.begin(); simpleBorderPointsIt!=simpleBorderPoints.end(); simpleBorderPointsIt++)
\r
194 // 1. Set simple border point to 0
\r
195 thinImage->SetPixel( *simpleBorderPointsIt, NumericTraits<OutputImagePixelType>::Zero);
\r
196 // 2. Check if neighborhood is still connected
\r
197 ot.SetLocation( *simpleBorderPointsIt );
\r
198 if( !isSimplePoint( ot.GetNeighborhood() ) )
\r
200 // we cannot delete current point, so reset
\r
201 thinImage->SetPixel( *simpleBorderPointsIt, NumericTraits<OutputImagePixelType>::One );
\r
209 unchangedBorders++;
\r
211 simpleBorderPoints.clear();
\r
212 } // end currentBorder for loop
\r
213 } // end unchangedBorders while loop
\r
215 itkDebugMacro( << "ComputeThinImage End");
\r
219 * Generate ThinImage
\r
221 template <class TInputImage,class TOutputImage>
\r
223 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
227 this->PrepareData();
\r
229 itkDebugMacro(<< "GenerateData: Computing Thinning Image");
\r
230 this->ComputeThinImage();
\r
231 } // end GenerateData()
\r
234 * Fill the Euler look-up table (LUT) for later check of the Euler invariance. (see [Lee94])
\r
236 template <class TInputImage,class TOutputImage>
\r
238 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
239 ::fillEulerLUT(int *LUT)
\r
376 * Check for Euler invariance. (see [Lee94])
\r
378 template <class TInputImage,class TOutputImage>
\r
380 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
381 ::isEulerInvariant(NeighborhoodType neighbors, int *LUT)
\r
383 // calculate Euler characteristic for each octant and sum up
\r
388 if( neighbors[24]==1 )
\r
390 if( neighbors[25]==1 )
\r
392 if( neighbors[15]==1 )
\r
394 if( neighbors[16]==1 )
\r
396 if( neighbors[21]==1 )
\r
398 if( neighbors[22]==1 )
\r
400 if( neighbors[12]==1 )
\r
402 EulerChar += LUT[n];
\r
405 if( neighbors[26]==1 )
\r
407 if( neighbors[23]==1 )
\r
409 if( neighbors[17]==1 )
\r
411 if( neighbors[14]==1 )
\r
413 if( neighbors[25]==1 )
\r
415 if( neighbors[22]==1 )
\r
417 if( neighbors[16]==1 )
\r
419 EulerChar += LUT[n];
\r
422 if( neighbors[18]==1 )
\r
424 if( neighbors[21]==1 )
\r
426 if( neighbors[9]==1 )
\r
428 if( neighbors[12]==1 )
\r
430 if( neighbors[19]==1 )
\r
432 if( neighbors[22]==1 )
\r
434 if( neighbors[10]==1 )
\r
436 EulerChar += LUT[n];
\r
439 if( neighbors[20]==1 )
\r
441 if( neighbors[23]==1 )
\r
443 if( neighbors[19]==1 )
\r
445 if( neighbors[22]==1 )
\r
447 if( neighbors[11]==1 )
\r
449 if( neighbors[14]==1 )
\r
451 if( neighbors[10]==1 )
\r
453 EulerChar += LUT[n];
\r
456 if( neighbors[6]==1 )
\r
458 if( neighbors[15]==1 )
\r
460 if( neighbors[7]==1 )
\r
462 if( neighbors[16]==1 )
\r
464 if( neighbors[3]==1 )
\r
466 if( neighbors[12]==1 )
\r
468 if( neighbors[4]==1 )
\r
470 EulerChar += LUT[n];
\r
473 if( neighbors[8]==1 )
\r
475 if( neighbors[7]==1 )
\r
477 if( neighbors[17]==1 )
\r
479 if( neighbors[16]==1 )
\r
481 if( neighbors[5]==1 )
\r
483 if( neighbors[4]==1 )
\r
485 if( neighbors[14]==1 )
\r
487 EulerChar += LUT[n];
\r
490 if( neighbors[0]==1 )
\r
492 if( neighbors[9]==1 )
\r
494 if( neighbors[3]==1 )
\r
496 if( neighbors[12]==1 )
\r
498 if( neighbors[1]==1 )
\r
500 if( neighbors[10]==1 )
\r
502 if( neighbors[4]==1 )
\r
504 EulerChar += LUT[n];
\r
507 if( neighbors[2]==1 )
\r
509 if( neighbors[1]==1 )
\r
511 if( neighbors[11]==1 )
\r
513 if( neighbors[10]==1 )
\r
515 if( neighbors[5]==1 )
\r
517 if( neighbors[4]==1 )
\r
519 if( neighbors[14]==1 )
\r
521 EulerChar += LUT[n];
\r
522 if( EulerChar == 0 )
\r
529 * Check if current point is a Simple Point.
\r
530 * This method is named 'N(v)_labeling' in [Lee94].
\r
531 * Outputs the number of connected objects in a neighborhood of a point
\r
532 * after this point would have been removed.
\r
534 template <class TInputImage,class TOutputImage>
\r
536 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
537 ::isSimplePoint(NeighborhoodType neighbors)
\r
539 // copy neighbors for labeling
\r
542 for( i = 0; i < 13; i++ ) // i = 0..12 -> cube[0..12]
\r
543 cube[i] = neighbors[i];
\r
544 // i != 13 : ignore center pixel when counting (see [Lee94])
\r
545 for( i = 14; i < 27; i++ ) // i = 14..26 -> cube[13..25]
\r
546 cube[i-1] = neighbors[i];
\r
547 // set initial label
\r
549 // for all points in the neighborhood
\r
550 for( int i = 0; i < 26; i++ )
\r
552 if( cube[i]==1 ) // voxel has not been labelled yet
\r
554 // start recursion with any octant that contains the point i
\r
564 Octree_labeling(1, label, cube );
\r
570 Octree_labeling(2, label, cube );
\r
576 Octree_labeling(3, label, cube );
\r
580 Octree_labeling(4, label, cube );
\r
586 Octree_labeling(5, label, cube );
\r
590 Octree_labeling(6, label, cube );
\r
594 Octree_labeling(7, label, cube );
\r
597 Octree_labeling(8, label, cube );
\r
607 //return label-2; in [Lee94] if the number of connected compontents would be needed
\r
612 * Octree_labeling [Lee94]
\r
613 * This is a recursive method that calulates the number of connected
\r
614 * components in the 3D neighbourhood after the center pixel would
\r
615 * have been removed.
\r
617 template <class TInputImage,class TOutputImage>
\r
619 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
620 ::Octree_labeling(int octant, int label, int *cube)
\r
622 // check if there are points in the octant with value 1
\r
625 // set points in this octant to current label
\r
626 // and recurseive labeling of adjacent octants
\r
632 Octree_labeling( 2, label, cube);
\r
637 Octree_labeling( 3, label, cube);
\r
642 Octree_labeling( 2, label, cube);
\r
643 Octree_labeling( 3, label, cube);
\r
644 Octree_labeling( 4, label, cube);
\r
649 Octree_labeling( 5, label, cube);
\r
651 if( cube[10] == 1 )
\r
654 Octree_labeling( 2, label, cube);
\r
655 Octree_labeling( 5, label, cube);
\r
656 Octree_labeling( 6, label, cube);
\r
658 if( cube[12] == 1 )
\r
661 Octree_labeling( 3, label, cube);
\r
662 Octree_labeling( 5, label, cube);
\r
663 Octree_labeling( 7, label, cube);
\r
671 Octree_labeling( 1, label, cube);
\r
676 Octree_labeling( 1, label, cube);
\r
677 Octree_labeling( 3, label, cube);
\r
678 Octree_labeling( 4, label, cube);
\r
680 if( cube[10] == 1 )
\r
683 Octree_labeling( 1, label, cube);
\r
684 Octree_labeling( 5, label, cube);
\r
685 Octree_labeling( 6, label, cube);
\r
692 Octree_labeling( 4, label, cube);
\r
694 if( cube[11] == 1 )
\r
697 Octree_labeling( 6, label, cube);
\r
699 if( cube[13] == 1 )
\r
702 Octree_labeling( 4, label, cube);
\r
703 Octree_labeling( 6, label, cube);
\r
704 Octree_labeling( 8, label, cube);
\r
712 Octree_labeling( 1, label, cube);
\r
717 Octree_labeling( 1, label, cube);
\r
718 Octree_labeling( 2, label, cube);
\r
719 Octree_labeling( 4, label, cube);
\r
721 if( cube[12] == 1 )
\r
724 Octree_labeling( 1, label, cube);
\r
725 Octree_labeling( 5, label, cube);
\r
726 Octree_labeling( 7, label, cube);
\r
733 Octree_labeling( 4, label, cube);
\r
735 if( cube[14] == 1 )
\r
738 Octree_labeling( 7, label, cube);
\r
740 if( cube[15] == 1 )
\r
743 Octree_labeling( 4, label, cube);
\r
744 Octree_labeling( 7, label, cube);
\r
745 Octree_labeling( 8, label, cube);
\r
753 Octree_labeling( 1, label, cube);
\r
754 Octree_labeling( 2, label, cube);
\r
755 Octree_labeling( 3, label, cube);
\r
760 Octree_labeling( 2, label, cube);
\r
762 if( cube[13] == 1 )
\r
765 Octree_labeling( 2, label, cube);
\r
766 Octree_labeling( 6, label, cube);
\r
767 Octree_labeling( 8, label, cube);
\r
772 Octree_labeling( 3, label, cube);
\r
774 if( cube[15] == 1 )
\r
777 Octree_labeling( 3, label, cube);
\r
778 Octree_labeling( 7, label, cube);
\r
779 Octree_labeling( 8, label, cube);
\r
783 if( cube[16] == 1 )
\r
786 Octree_labeling( 8, label, cube);
\r
794 Octree_labeling( 1, label, cube);
\r
796 if( cube[10] == 1 )
\r
799 Octree_labeling( 1, label, cube);
\r
800 Octree_labeling( 2, label, cube);
\r
801 Octree_labeling( 6, label, cube);
\r
803 if( cube[12] == 1 )
\r
806 Octree_labeling( 1, label, cube);
\r
807 Octree_labeling( 3, label, cube);
\r
808 Octree_labeling( 7, label, cube);
\r
810 if( cube[17] == 1 )
\r
812 if( cube[18] == 1 )
\r
815 Octree_labeling( 6, label, cube);
\r
817 if( cube[20] == 1 )
\r
820 Octree_labeling( 7, label, cube);
\r
822 if( cube[21] == 1 )
\r
825 Octree_labeling( 6, label, cube);
\r
826 Octree_labeling( 7, label, cube);
\r
827 Octree_labeling( 8, label, cube);
\r
832 if( cube[10] == 1 )
\r
835 Octree_labeling( 1, label, cube);
\r
836 Octree_labeling( 2, label, cube);
\r
837 Octree_labeling( 5, label, cube);
\r
839 if( cube[11] == 1 )
\r
842 Octree_labeling( 2, label, cube);
\r
844 if( cube[13] == 1 )
\r
847 Octree_labeling( 2, label, cube);
\r
848 Octree_labeling( 4, label, cube);
\r
849 Octree_labeling( 8, label, cube);
\r
851 if( cube[18] == 1 )
\r
854 Octree_labeling( 5, label, cube);
\r
856 if( cube[21] == 1 )
\r
859 Octree_labeling( 5, label, cube);
\r
860 Octree_labeling( 7, label, cube);
\r
861 Octree_labeling( 8, label, cube);
\r
863 if( cube[19] == 1 )
\r
865 if( cube[22] == 1 )
\r
868 Octree_labeling( 8, label, cube);
\r
873 if( cube[12] == 1 )
\r
876 Octree_labeling( 1, label, cube);
\r
877 Octree_labeling( 3, label, cube);
\r
878 Octree_labeling( 5, label, cube);
\r
880 if( cube[14] == 1 )
\r
883 Octree_labeling( 3, label, cube);
\r
885 if( cube[15] == 1 )
\r
888 Octree_labeling( 3, label, cube);
\r
889 Octree_labeling( 4, label, cube);
\r
890 Octree_labeling( 8, label, cube);
\r
892 if( cube[20] == 1 )
\r
895 Octree_labeling( 5, label, cube);
\r
897 if( cube[21] == 1 )
\r
900 Octree_labeling( 5, label, cube);
\r
901 Octree_labeling( 6, label, cube);
\r
902 Octree_labeling( 8, label, cube);
\r
904 if( cube[23] == 1 )
\r
906 if( cube[24] == 1 )
\r
909 Octree_labeling( 8, label, cube);
\r
914 if( cube[13] == 1 )
\r
917 Octree_labeling( 2, label, cube);
\r
918 Octree_labeling( 4, label, cube);
\r
919 Octree_labeling( 6, label, cube);
\r
921 if( cube[15] == 1 )
\r
924 Octree_labeling( 3, label, cube);
\r
925 Octree_labeling( 4, label, cube);
\r
926 Octree_labeling( 7, label, cube);
\r
928 if( cube[16] == 1 )
\r
931 Octree_labeling( 4, label, cube);
\r
933 if( cube[21] == 1 )
\r
936 Octree_labeling( 5, label, cube);
\r
937 Octree_labeling( 6, label, cube);
\r
938 Octree_labeling( 7, label, cube);
\r
940 if( cube[22] == 1 )
\r
943 Octree_labeling( 6, label, cube);
\r
945 if( cube[24] == 1 )
\r
948 Octree_labeling( 7, label, cube);
\r
950 if( cube[25] == 1 )
\r
959 template <class TInputImage,class TOutputImage>
\r
961 BinaryThinningImageFilter3D<TInputImage,TOutputImage>
\r
962 ::PrintSelf(std::ostream& os, Indent indent) const
\r
964 Superclass::PrintSelf(os,indent);
\r
966 os << indent << "Thinning image: " << std::endl;
\r
970 } // end namespace itk
\r