-
Notifications
You must be signed in to change notification settings - Fork 6
/
SmallHoleFiller.hxx
157 lines (136 loc) · 5.05 KB
/
SmallHoleFiller.hxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "itkImageFileWriter.h" // For intermediate debugging output
#include "itkImageRegionConstIterator.h"
#include "itkImageRegionIterator.h"
#include "itkNeighborhoodIterator.h"
#include "itkNumericTraits.h"
#include "rtkMacro.h"
template <typename TImage>
SmallHoleFiller<TImage>::SmallHoleFiller()
{
this->HolePixel = itk::NumericTraits< typename TImage::PixelType >::Zero;
this->Image = NULL;
this->Output = NULL;
}
template <typename TImage>
void SmallHoleFiller<TImage>::SetImage(typename TImage::Pointer image)
{
this->Image = image;
this->Output = TImage::New();
}
template <typename TImage>
void SmallHoleFiller<TImage>::SetHolePixel(typename TImage::PixelType pixel)
{
this->HolePixel = pixel;
}
template <typename TImage>
typename TImage::Pointer SmallHoleFiller<TImage>::GetOutput()
{
return this->Output;
}
template <typename TImage>
void SmallHoleFiller<TImage>::Fill()
{
if(!this->Image)
{
std::cerr << "You must first call SetImage!" << std::endl;
return;
}
// Initialize by setting the output image to the input image.
DeepCopy<TImage>(this->Image, this->Output);
unsigned int numberOfIterations = 0;
while(HasEmptyPixels())
{
std::cout << "Iteration " << numberOfIterations << "..." << std::endl;
Iterate();
numberOfIterations++;
// typedef itk::ImageFileWriter< TImage > WriterType;
// typename WriterType::Pointer writer = WriterType::New();
// std::stringstream ss;
// ss << "/tmp/intermediate_" << numberOfIterations << ".mha";
// writer->SetFileName(ss.str());
// writer->SetInput(this->Output);
// writer->Update();
}
std::cout << "Filling completed in " << numberOfIterations << " iterations." << std::endl;
}
template <typename TImage>
void SmallHoleFiller<TImage>::Iterate()
{
// Make a copy of the current intermediate output. We use this to determine which pixels were holes at the beginning of this iteration.
typename TImage::Pointer temporaryImage = TImage::New();
DeepCopy<TImage>(this->Output, temporaryImage);
// Traverse the image. When a pixel is encountered that is a hole, fill it with the average of its non-hole neighbors.
// Do not mark pixels filled on this iteration as known. This will result in a less accurate filling, as it favors the colors
// of pixels that occur earlier in the raster scan order.
typename TImage::SizeType radius;
radius.Fill(1);
itk::NeighborhoodIterator<TImage> intermediateNeighborhoodIterator(radius, temporaryImage, temporaryImage->GetLargestPossibleRegion());
// This iterator is used to set the output pixels.
itk::ImageRegionIterator<TImage> outputIterator(this->Output, this->Output->GetLargestPossibleRegion());
while(!intermediateNeighborhoodIterator.IsAtEnd())
{
if(intermediateNeighborhoodIterator.GetCenterPixel() == this->HolePixel) // We want to fill this pixel
{
typename TImage::PixelType pixelSum = itk::NumericTraits< typename TImage::PixelType >::Zero;
// Loop over the 8-neighorhood
unsigned int validPixels = 0;
for(unsigned int i = 0; i < 27; i++)
{
if(i==13) // this is the center (current) pixel, so skip it
{
continue;
}
bool isInBounds = false;
typename TImage::PixelType currentPixel = intermediateNeighborhoodIterator.GetPixel(i, isInBounds);
if(isInBounds)
{
if(currentPixel != this->HolePixel)
{
validPixels++;
pixelSum += currentPixel;
}
}
} // end 8-connected neighbor for
if(validPixels > 0)
{
//typename TImage::PixelType pixelAverage = static_cast<typename TImage::PixelType>(pixelSum / validPixels);
typename TImage::PixelType pixelAverage = static_cast<typename TImage::PixelType>(pixelSum * (1.0/ validPixels)); // We multiply by the reciprocal because operator/ is not defined for all types.
outputIterator.Set(pixelAverage);
//std::cout << "Set " << outputIterator.GetIndex() << " to " << pixelAverage << std::endl;
}
} // end "fill this pixel?" conditional
++intermediateNeighborhoodIterator;
++outputIterator;
} // end main traversal loop
}
template <typename TImage>
bool SmallHoleFiller<TImage>::HasEmptyPixels()
{
itk::ImageRegionConstIterator<TImage> imageIterator(this->Output, this->Output->GetLargestPossibleRegion());
while(!imageIterator.IsAtEnd())
{
if(imageIterator.Get() == this->HolePixel)
{
//std::cout << "Pixel " << imageIterator.GetIndex() << " is empty." << std::endl;
return true;
}
++imageIterator;
}
return false;
}
///////// This is not a member function /////////////
/** Copy the input to the output*/
template<typename TImage>
void DeepCopy(typename TImage::Pointer input, typename TImage::Pointer output)
{
output->SetRegions(input->GetLargestPossibleRegion());
output->Allocate();
itk::ImageRegionConstIterator<TImage> inputIterator(input, input->GetLargestPossibleRegion());
itk::ImageRegionIterator<TImage> outputIterator(output, output->GetLargestPossibleRegion());
while(!inputIterator.IsAtEnd())
{
outputIterator.Set(inputIterator.Get());
++inputIterator;
++outputIterator;
}
}