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 #ifndef itkVectorConfidenceConnectedImageFilter_hxx
19 #define itkVectorConfidenceConnectedImageFilter_hxx
20
21 #include "itkVectorConfidenceConnectedImageFilter.h"
22 #include "itkMacro.h"
23 #include "itkImageRegionIterator.h"
24 #include "itkVectorMeanImageFunction.h"
25 #include "itkCovarianceImageFunction.h"
26 #include "itkBinaryThresholdImageFunction.h"
27 #include "itkFloodFilledImageFunctionConditionalIterator.h"
28 #include "itkNumericTraitsRGBPixel.h"
29 #include "itkProgressReporter.h"
30
31 namespace itk
32 {
33 /**
34 * Constructor
35 */
36 template< typename TInputImage, typename TOutputImage >
37 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
VectorConfidenceConnectedImageFilter()38 ::VectorConfidenceConnectedImageFilter()
39 {
40 m_Multiplier = 2.5;
41 m_NumberOfIterations = 4;
42 m_Seeds.clear();
43 m_InitialNeighborhoodRadius = 1;
44 m_ReplaceValue = NumericTraits< OutputImagePixelType >::OneValue();
45 m_ThresholdFunction = DistanceThresholdFunctionType::New();
46 }
47
48 /**
49 * Standard PrintSelf method.
50 */
51 template< typename TInputImage, typename TOutputImage >
52 void
53 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
PrintSelf(std::ostream & os,Indent indent) const54 ::PrintSelf(std::ostream & os, Indent indent) const
55 {
56 this->Superclass::PrintSelf(os, indent);
57 os << indent << "Number of iterations: " << m_NumberOfIterations
58 << std::endl;
59 os << indent << "Multiplier for confidence interval: " << m_Multiplier
60 << std::endl;
61 os << indent << "ReplaceValue: "
62 << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_ReplaceValue )
63 << std::endl;
64 os << indent << "InitialNeighborhoodRadius: " << m_InitialNeighborhoodRadius
65 << std::endl;
66 }
67
68 template< typename TInputImage, typename TOutputImage >
69 void
70 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
SetSeed(const IndexType & seed)71 ::SetSeed(const IndexType & seed)
72 {
73 this->ClearSeeds();
74 this->AddSeed(seed);
75 }
76
77 template< typename TInputImage, typename TOutputImage >
78 void
79 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
AddSeed(const IndexType & seed)80 ::AddSeed(const IndexType & seed)
81 {
82 m_Seeds.push_back(seed);
83 this->Modified();
84 }
85
86 template< typename TInputImage, typename TOutputImage >
87 void
88 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
ClearSeeds()89 ::ClearSeeds()
90 {
91 if ( !this->m_Seeds.empty() )
92 {
93 this->m_Seeds.clear();
94 this->Modified();
95 }
96 }
97
98 template< typename TInputImage, typename TOutputImage >
99 void
100 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
GenerateInputRequestedRegion()101 ::GenerateInputRequestedRegion()
102 {
103 Superclass::GenerateInputRequestedRegion();
104 if ( this->GetInput() )
105 {
106 InputImagePointer input =
107 const_cast< TInputImage * >( this->GetInput() );
108 input->SetRequestedRegionToLargestPossibleRegion();
109 }
110 }
111
112
113 template< typename TInputImage, typename TOutputImage >
114 void
115 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
EnlargeOutputRequestedRegion(DataObject * output)116 ::EnlargeOutputRequestedRegion(DataObject *output)
117 {
118 Superclass::EnlargeOutputRequestedRegion(output);
119 output->SetRequestedRegionToLargestPossibleRegion();
120 }
121
122 template< typename TInputImage, typename TOutputImage >
123 void
124 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
GenerateData()125 ::GenerateData()
126 {
127 using InputPixelType = typename InputImageType::PixelType;
128
129 using SecondFunctionType = BinaryThresholdImageFunction<OutputImageType>;
130 using IteratorType = FloodFilledImageFunctionConditionalIterator< OutputImageType, DistanceThresholdFunctionType >;
131 using SecondIteratorType = FloodFilledImageFunctionConditionalConstIterator< InputImageType,
132 SecondFunctionType >;
133
134 unsigned int loop;
135
136 typename Superclass::InputImageConstPointer inputImage = this->GetInput();
137 typename Superclass::OutputImagePointer outputImage = this->GetOutput();
138
139 // Zero the output
140 OutputImageRegionType region = outputImage->GetRequestedRegion();
141 outputImage->SetBufferedRegion(region);
142 outputImage->Allocate();
143 outputImage->FillBuffer (NumericTraits< OutputImagePixelType >::ZeroValue());
144
145 // Compute the statistics of the seed point
146 using VectorMeanImageFunctionType = VectorMeanImageFunction< InputImageType >;
147 typename VectorMeanImageFunctionType::Pointer meanFunction =
148 VectorMeanImageFunctionType::New();
149
150 meanFunction->SetInputImage(inputImage);
151 meanFunction->SetNeighborhoodRadius(m_InitialNeighborhoodRadius);
152 using CovarianceImageFunctionType = CovarianceImageFunction< InputImageType >;
153 typename CovarianceImageFunctionType::Pointer varianceFunction =
154 CovarianceImageFunctionType::New();
155 varianceFunction->SetInputImage(inputImage);
156 varianceFunction->SetNeighborhoodRadius(m_InitialNeighborhoodRadius);
157
158 // Set up the image function used for connectivity
159 m_ThresholdFunction->SetInputImage (inputImage);
160
161 CovarianceMatrixType covariance;
162 MeanVectorType mean;
163
164 using ComponentPixelType = typename InputPixelType::ValueType;
165 using ComponentRealType = typename NumericTraits< ComponentPixelType >::RealType;
166
167 const unsigned int dimension = inputImage->GetNumberOfComponentsPerPixel();
168
169 covariance = CovarianceMatrixType(dimension, dimension);
170 mean = MeanVectorType(dimension);
171
172 covariance.fill(NumericTraits< ComponentRealType >::ZeroValue());
173 mean.fill(NumericTraits< ComponentRealType >::ZeroValue());
174
175 using MeanFunctionVectorType = typename VectorMeanImageFunctionType::OutputType;
176 using CovarianceFunctionMatrixType = typename CovarianceImageFunctionType::OutputType;
177
178 typename SeedsContainerType::const_iterator si = m_Seeds.begin();
179 typename SeedsContainerType::const_iterator li = m_Seeds.end();
180 SizeValueType seed_cnt = 0;
181 while ( si != li )
182 {
183 if ( region.IsInside(*si) )
184 {
185 ++seed_cnt;
186 const MeanFunctionVectorType meanContribution = meanFunction->EvaluateAtIndex(*si);
187 const CovarianceFunctionMatrixType covarianceContribution = varianceFunction->EvaluateAtIndex(*si);
188 for ( unsigned int ii = 0; ii < dimension; ii++ )
189 {
190 mean[ii] += meanContribution[ii];
191 for ( unsigned int jj = 0; jj < dimension; jj++ )
192 {
193 covariance[ii][jj] += covarianceContribution[ii][jj];
194 }
195 }
196 }
197 si++;
198 }
199
200 if ( seed_cnt == 0 )
201 {
202 this->UpdateProgress(1.0);
203 // no seeds result in zero image
204 return;
205 }
206
207 for ( unsigned int ik = 0; ik < dimension; ik++ )
208 {
209 mean[ik] /= seed_cnt;
210 for ( unsigned int jk = 0; jk < dimension; jk++ )
211 {
212 covariance[ik][jk] /= seed_cnt;
213 }
214 }
215
216 m_ThresholdFunction->SetMean(mean);
217 m_ThresholdFunction->SetCovariance(covariance);
218
219 m_ThresholdFunction->SetThreshold(m_Multiplier);
220
221 itkDebugMacro(<< "\nMultiplier originally = " << m_Multiplier);
222
223 // Make sure that the multiplier is large enough to include the seed points
224 // themselves.
225 // This is a pragmatic fix, but a questionable practice because it means that
226 // the actual
227 // region may be grown using a multiplier different from the one specified by
228 // the user.
229
230 si = m_Seeds.begin();
231 li = m_Seeds.end();
232 while ( si != li )
233 {
234 if ( region.IsInside(*si) )
235 {
236 const double distance =
237 m_ThresholdFunction->EvaluateDistanceAtIndex(*si);
238 if ( distance > m_Multiplier )
239 {
240 m_Multiplier = distance;
241 }
242 }
243 si++;
244 }
245
246 // Finally setup the eventually modified multiplier. That is actually the
247 // threshold itself.
248 m_ThresholdFunction->SetThreshold(m_Multiplier);
249
250 itkDebugMacro(<< "\nMultiplier after verifying seeds inclusion = " << m_Multiplier);
251
252 // Segment the image, the iterator walks the output image (so Set()
253 // writes into the output image), starting at the seed point. As
254 // the iterator walks, if the corresponding pixel in the input image
255 // (accessed via the "m_ThresholdFunction" assigned to the iterator) is within
256 // the [lower, upper] bounds prescribed, the pixel is added to the
257 // output segmentation and its neighbors become candidates for the
258 // iterator to walk.
259 IteratorType it = IteratorType (outputImage, m_ThresholdFunction, m_Seeds);
260 it.GoToBegin();
261 while ( !it.IsAtEnd() )
262 {
263 it.Set(m_ReplaceValue);
264 ++it;
265 }
266
267 ProgressReporter progress(this, 0, region.GetNumberOfPixels() * m_NumberOfIterations);
268
269 for ( loop = 0; loop < m_NumberOfIterations; ++loop )
270 {
271 // Now that we have an initial segmentation, let's recalculate the
272 // statistics. Since we have already labelled the output, we visit
273 // pixels in the input image that have been set in the output image.
274 // Essentially, we flip the iterator around, so we walk the input
275 // image (so Get() will get pixel values from the input) and constrain
276 // iterator such it only visits pixels that were set in the output.
277 typename SecondFunctionType::Pointer secondFunction = SecondFunctionType::New();
278 secondFunction->SetInputImage (outputImage);
279 secondFunction->ThresholdBetween(m_ReplaceValue, m_ReplaceValue);
280
281 covariance = CovarianceMatrixType(dimension, dimension);
282 mean = MeanVectorType(dimension);
283
284 covariance.fill(NumericTraits< ComponentRealType >::ZeroValue());
285 mean.fill(NumericTraits< ComponentRealType >::ZeroValue());
286
287 SizeValueType num = NumericTraits< SizeValueType >::ZeroValue();
288
289 SecondIteratorType sit =
290 SecondIteratorType (inputImage, secondFunction, m_Seeds);
291 sit.GoToBegin();
292 while ( !sit.IsAtEnd() )
293 {
294 const InputPixelType pixelValue = sit.Get();
295 for ( unsigned int i = 0; i < dimension; i++ )
296 {
297 const auto pixelValueI = static_cast< ComponentRealType >( pixelValue[i] );
298 covariance[i][i] += pixelValueI * pixelValueI;
299 mean[i] += pixelValueI;
300 for ( unsigned int j = i + 1; j < dimension; j++ )
301 {
302 const auto pixelValueJ = static_cast< ComponentRealType >( pixelValue[j] );
303 const ComponentRealType product = pixelValueI * pixelValueJ;
304 covariance[i][j] += product;
305 covariance[j][i] += product;
306 }
307 }
308 ++num;
309 ++sit;
310 }
311 for ( unsigned int ii = 0; ii < dimension; ii++ )
312 {
313 mean[ii] /= static_cast< double >( num );
314 for ( unsigned int jj = 0; jj < dimension; jj++ )
315 {
316 covariance[ii][jj] /= static_cast< double >( num );
317 }
318 }
319
320 for ( unsigned int ik = 0; ik < dimension; ik++ )
321 {
322 for ( unsigned int jk = 0; jk < dimension; jk++ )
323 {
324 covariance[ik][jk] -= mean[ik] * mean[jk];
325 }
326 }
327
328 m_ThresholdFunction->SetMean(mean);
329 m_ThresholdFunction->SetCovariance(covariance);
330
331 // Rerun the segmentation, the iterator walks the output image,
332 // starting at the seed point. As the iterator walks, if the
333 // corresponding pixel in the input image (accessed via the
334 // "m_ThresholdFunction" assigned to the iterator) is within the [lower,
335 // upper] bounds prescribed, the pixel is added to the output
336 // segmentation and its neighbors become candidates for the
337 // iterator to walk.
338 outputImage->FillBuffer (NumericTraits< OutputImagePixelType >::ZeroValue());
339 IteratorType thirdIt = IteratorType (outputImage, m_ThresholdFunction, m_Seeds);
340 thirdIt.GoToBegin();
341 try
342 {
343 while ( !thirdIt.IsAtEnd() )
344 {
345 thirdIt.Set(m_ReplaceValue);
346 ++thirdIt;
347 progress.CompletedPixel(); // potential exception thrown here
348 }
349 }
350 catch ( ProcessAborted & )
351 {
352 break; // interrupt the iterations loop
353 }
354 } // end iteration loop
355
356 if ( this->GetAbortGenerateData() )
357 {
358 ProcessAborted e(__FILE__, __LINE__);
359 e.SetDescription("Process aborted.");
360 e.SetLocation(ITK_LOCATION);
361 throw e;
362 }
363 }
364
365 template< typename TInputImage, typename TOutputImage >
366 const typename
367 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >::CovarianceMatrixType &
368 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
GetCovariance() const369 ::GetCovariance() const
370 {
371 return m_ThresholdFunction->GetCovariance();
372 }
373
374 template< typename TInputImage, typename TOutputImage >
375 const typename
376 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >::MeanVectorType &
377 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
GetMean() const378 ::GetMean() const
379 {
380 return m_ThresholdFunction->GetMean();
381 }
382
383 template< typename TInputImage, typename TOutputImage >
384 const typename VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >::SeedsContainerType &
385 VectorConfidenceConnectedImageFilter< TInputImage, TOutputImage >
GetSeeds() const386 ::GetSeeds() const
387 {
388 itkDebugMacro("returning Seeds");
389 return this->m_Seeds;
390 }
391
392 } // end namespace itk
393
394 #endif
395