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 itkBoxUtilities_h
19 #define itkBoxUtilities_h
20
21 #include "itkProgressReporter.h"
22 #include "itkShapedNeighborhoodIterator.h"
23 #include "itkImageRegionIterator.h"
24 #include "itkConstantBoundaryCondition.h"
25 #include "itkImageRegionIteratorWithIndex.h"
26 #include "itkImageRegionIterator.h"
27 #include "itkOffset.h"
28 #include "itkNeighborhoodAlgorithm.h"
29 #include "itkShapedNeighborhoodIterator.h"
30 #include "itkZeroFluxNeumannBoundaryCondition.h"
31
32 /*
33 *
34 * This code was contributed in the Insight Journal paper:
35 * "Efficient implementation of kernel filtering"
36 * by Beare R., Lehmann G
37 * https://hdl.handle.net/1926/555
38 * http://www.insight-journal.org/browse/publication/160
39 *
40 */
41
42 namespace itk_impl_details
43 {
44
45 template< typename TIterator >
46 inline TIterator *
47 setConnectivityEarlyBox(TIterator *it, bool fullyConnected = false)
48 {
49 // activate the "previous" neighbours
50 typename TIterator::OffsetType offset;
51 it->ClearActiveList();
52 if ( !fullyConnected )
53 {
54 // only activate the neighbors that are face connected
55 // to the current pixel. do not include the center pixel
56 offset.Fill(0);
57 for ( unsigned int d = 0; d < TIterator::Dimension; ++d )
58 {
59 offset[d] = -1;
60 it->ActivateOffset(offset);
61 offset[d] = 0;
62 }
63 }
64 else
65 {
66 // activate all neighbors that are face+edge+vertex
67 // connected to the current pixel. do not include the center pixel
68 unsigned int centerIndex = it->GetCenterNeighborhoodIndex();
69 for ( unsigned int d = 0; d < centerIndex; d++ )
70 {
71 offset = it->GetOffset(d);
72 // check for positives in any dimension
73 bool keep = true;
74 for ( unsigned int i = 0; i < TIterator::Dimension; i++ )
75 {
76 if ( offset[i] > 0 )
77 {
78 keep = false;
79 break;
80 }
81 }
82 if ( keep )
83 {
84 it->ActivateOffset(offset);
85 }
86 }
87 offset.Fill(0);
88 it->DeactivateOffset(offset);
89 }
90 return it;
91 }
92
93 }
94
95 namespace itk
96 {
97
98 template< typename TInputImage, typename TOutputImage >
99 void
BoxAccumulateFunction(const TInputImage * inputImage,const TOutputImage * outputImage,typename TInputImage::RegionType inputRegion,typename TOutputImage::RegionType outputRegion,ProgressReporter & progress)100 BoxAccumulateFunction(const TInputImage *inputImage,
101 const TOutputImage *outputImage,
102 typename TInputImage::RegionType inputRegion,
103 typename TOutputImage::RegionType outputRegion
104 #if defined(ITKV4_COMPATIBILITY)
105 , ProgressReporter & progress)
106 #else
107 )
108 #endif
109 {
110 // type alias
111 using InputImageType = TInputImage;
112 using OffsetType = typename TInputImage::OffsetType;
113 using OutputImageType = TOutputImage;
114 using OutputPixelType = typename TOutputImage::PixelType;
115
116 using InputIterator = ImageRegionConstIterator< TInputImage >;
117
118 using NOutputIterator = ShapedNeighborhoodIterator< TOutputImage >;
119 InputIterator inIt(inputImage, inputRegion);
120 typename TInputImage::SizeType kernelRadius;
121 kernelRadius.Fill(1);
122
123 NOutputIterator noutIt(kernelRadius, outputImage, outputRegion);
124 // this iterator is fully connected
125 itk_impl_details::setConnectivityEarlyBox(&noutIt, true);
126
127 ConstantBoundaryCondition< OutputImageType > oBC;
128 oBC.SetConstant(NumericTraits< OutputPixelType >::ZeroValue() );
129 noutIt.OverrideBoundaryCondition(&oBC);
130 // This uses several iterators. An alternative and probably better
131 // approach would be to copy the input to the output and convolve
132 // with the following weights (in 2D)
133 // -(dim - 1) 1
134 // 1 1
135 // The result of each convolution needs to get written back to the
136 // image being convolved so that the accumulation propagates
137 // This should be implementable with neighborhood operators.
138
139 std::vector< int > weights;
140 typename NOutputIterator::ConstIterator sIt;
141 for ( auto idxIt = noutIt.GetActiveIndexList().begin();
142 idxIt != noutIt.GetActiveIndexList().end();
143 idxIt++ )
144 {
145 OffsetType offset = noutIt.GetOffset(*idxIt);
146 int w = -1;
147 for ( unsigned int k = 0; k < InputImageType::ImageDimension; k++ )
148 {
149 if ( offset[k] != 0 )
150 {
151 w *= offset[k];
152 }
153 }
154 // std::cout << offset << " " << w << std::endl;
155 weights.push_back(w);
156 }
157
158 for ( inIt.GoToBegin(), noutIt.GoToBegin(); !noutIt.IsAtEnd(); ++inIt, ++noutIt )
159 {
160 OutputPixelType sum = 0;
161 int k;
162 for ( k = 0, sIt = noutIt.Begin(); !sIt.IsAtEnd(); ++sIt, ++k )
163 {
164 sum += sIt.Get() * weights[k];
165 }
166 noutIt.SetCenterPixel( sum + inIt.Get() );
167 #if defined(ITKV4_COMPATIBILITY)
168 progress.CompletedPixel();
169 #endif
170 }
171 }
172
173 // a function to generate corners of arbitrary dimension box
174 template< typename TImage >
175 std::vector< typename TImage::OffsetType >
CornerOffsets(const TImage * im)176 CornerOffsets(const TImage *im)
177 {
178 using NIterator = ShapedNeighborhoodIterator< TImage >;
179 typename TImage::SizeType unitradius;
180 unitradius.Fill(1);
181 NIterator n1( unitradius, im, im->GetRequestedRegion() );
182 unsigned int centerIndex = n1.GetCenterNeighborhoodIndex();
183 typename NIterator::OffsetType offset;
184 std::vector< typename TImage::OffsetType > result;
185 for ( unsigned int d = 0; d < centerIndex * 2 + 1; d++ )
186 {
187 offset = n1.GetOffset(d);
188 // check whether this is a corner - corners have no zeros
189 bool corner = true;
190 for ( unsigned int k = 0; k < TImage::ImageDimension; k++ )
191 {
192 if ( offset[k] == 0 )
193 {
194 corner = false;
195 break;
196 }
197 }
198 if ( corner )
199 {
200 result.push_back(offset);
201 }
202 }
203 return ( result );
204 }
205
206 template< typename TInputImage, typename TOutputImage >
207 void
BoxMeanCalculatorFunction(const TInputImage * accImage,TOutputImage * outputImage,typename TInputImage::RegionType inputRegion,typename TOutputImage::RegionType outputRegion,typename TInputImage::SizeType radius,ProgressReporter & progress)208 BoxMeanCalculatorFunction(const TInputImage *accImage,
209 TOutputImage *outputImage,
210 typename TInputImage::RegionType inputRegion,
211 typename TOutputImage::RegionType outputRegion,
212 typename TInputImage::SizeType radius
213 #if defined(ITKV4_COMPATIBILITY)
214 , ProgressReporter & progress)
215 #else
216 )
217 #endif
218 {
219 // type alias
220 using InputImageType = TInputImage;
221 using RegionType = typename TInputImage::RegionType;
222 using SizeType = typename TInputImage::SizeType;
223 using IndexType = typename TInputImage::IndexType;
224 using OffsetType = typename TInputImage::OffsetType;
225 using OutputImageType = TOutputImage;
226 using OutputPixelType = typename TOutputImage::PixelType;
227 // use the face generator for speed
228 using FaceCalculatorType = NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< InputImageType >;
229 using FaceListType = typename FaceCalculatorType::FaceListType;
230 using FaceListTypeIt = typename FaceCalculatorType::FaceListType::iterator;
231 FaceCalculatorType faceCalculator;
232
233 FaceListType faceList;
234 FaceListTypeIt fit;
235 ZeroFluxNeumannBoundaryCondition< TInputImage > nbc;
236
237 // this process is actually slightly asymmetric because we need to
238 // subtract rectangles that are next to our kernel, not overlapping it
239 SizeType kernelSize;
240 SizeType internalRadius;
241 SizeType regionLimit;
242
243 IndexType regionStart = inputRegion.GetIndex();
244 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
245 {
246 kernelSize[i] = radius[i] * 2 + 1;
247 internalRadius[i] = radius[i] + 1;
248 regionLimit[i] = inputRegion.GetSize()[i] + regionStart[i] - 1;
249 }
250
251 using AccPixType = typename NumericTraits< OutputPixelType >::RealType;
252 // get a set of offsets to corners for a unit hypercube in this image
253 std::vector< OffsetType > unitCorners = CornerOffsets< TInputImage >(accImage);
254 std::vector< OffsetType > realCorners;
255 std::vector< AccPixType > weights;
256 // now compute the weights
257 for ( unsigned int k = 0; k < unitCorners.size(); k++ )
258 {
259 int prod = 1;
260 OffsetType thisCorner;
261 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
262 {
263 prod *= unitCorners[k][i];
264 if ( unitCorners[k][i] > 0 )
265 {
266 thisCorner[i] = radius[i];
267 }
268 else
269 {
270 thisCorner[i] = -( static_cast<OffsetValueType>(radius[i]) + 1 );
271 }
272 }
273 weights.push_back( (AccPixType)prod );
274 realCorners.push_back(thisCorner);
275 }
276
277 faceList = faceCalculator(accImage, outputRegion, internalRadius);
278 // start with the body region
279 for ( fit = faceList.begin(); fit != faceList.end(); ++fit )
280 {
281 if ( fit == faceList.begin() )
282 {
283 // this is the body region. This is meant to be an optimized
284 // version that doesn't use neighborhood regions
285 // compute the various offsets
286 AccPixType pixelscount = 1;
287 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
288 {
289 pixelscount *= (AccPixType)( 2 * radius[i] + 1 );
290 }
291
292 using OutputIteratorType = ImageRegionIterator< OutputImageType >;
293 using InputIteratorType = ImageRegionConstIterator< InputImageType >;
294
295 using CornerItVecType = std::vector< InputIteratorType >;
296 CornerItVecType cornerItVec;
297 // set up the iterators for each corner
298 for ( unsigned int k = 0; k < realCorners.size(); k++ )
299 {
300 typename InputImageType::RegionType tReg = ( *fit );
301 tReg.SetIndex(tReg.GetIndex() + realCorners[k]);
302 InputIteratorType tempIt(accImage, tReg);
303 tempIt.GoToBegin();
304 cornerItVec.push_back(tempIt);
305 }
306 // set up the output iterator
307 OutputIteratorType oIt(outputImage, *fit);
308 // now do the work
309 for ( oIt.GoToBegin(); !oIt.IsAtEnd(); ++oIt )
310 {
311 AccPixType sum = 0;
312 // check each corner
313 for ( unsigned int k = 0; k < cornerItVec.size(); k++ )
314 {
315 sum += weights[k] * cornerItVec[k].Get();
316 // increment each corner iterator
317 ++( cornerItVec[k] );
318 }
319 oIt.Set( static_cast< OutputPixelType >( sum / pixelscount ) );
320 #if defined(ITKV4_COMPATIBILITY)
321 progress.CompletedPixel();
322 #endif
323 }
324 }
325 else
326 {
327 // now we need to deal with the border regions
328 using OutputIteratorType = ImageRegionIteratorWithIndex< OutputImageType >;
329 OutputIteratorType oIt(outputImage, *fit);
330 // now do the work
331 for ( oIt.GoToBegin(); !oIt.IsAtEnd(); ++oIt )
332 {
333 // figure out the number of pixels in the box by creating an
334 // equivalent region and cropping - this could probably be
335 // included in the loop below.
336 RegionType currentKernelRegion;
337 currentKernelRegion.SetSize(kernelSize);
338 // compute the region's index
339 IndexType kernelRegionIdx = oIt.GetIndex();
340 IndexType centIndex = kernelRegionIdx;
341 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
342 {
343 kernelRegionIdx[i] -= radius[i];
344 }
345 currentKernelRegion.SetIndex(kernelRegionIdx);
346 currentKernelRegion.Crop(inputRegion);
347 OffsetValueType edgepixelscount = currentKernelRegion.GetNumberOfPixels();
348 AccPixType sum = 0;
349 // rules are : for each corner,
350 // for each dimension
351 // if dimension offset is positive -> this is
352 // a leading edge. Crop if outside the input
353 // region
354 // if dimension offset is negative -> this is
355 // a trailing edge. Ignore if it is outside
356 // image region
357 for ( unsigned int k = 0; k < realCorners.size(); k++ )
358 {
359 IndexType thisCorner = centIndex + realCorners[k];
360 bool includeCorner = true;
361 for ( unsigned int j = 0; j < TInputImage::ImageDimension; j++ )
362 {
363 if ( unitCorners[k][j] > 0 )
364 {
365 // leading edge - crop it
366 if ( thisCorner[j] > static_cast< OffsetValueType >( regionLimit[j] ) )
367 {
368 thisCorner[j] = static_cast< OffsetValueType >( regionLimit[j] );
369 }
370 }
371 else
372 {
373 // trailing edge - check bounds
374 if ( thisCorner[j] < regionStart[j] )
375 {
376 includeCorner = false;
377 break;
378 }
379 }
380 }
381 if ( includeCorner )
382 {
383 sum += accImage->GetPixel(thisCorner) * weights[k];
384 }
385 }
386
387 oIt.Set( static_cast< OutputPixelType >( sum / (AccPixType)edgepixelscount ) );
388 #if defined(ITKV4_COMPATIBILITY)
389 progress.CompletedPixel();
390 #endif
391 }
392 }
393 }
394 }
395
396 template< typename TInputImage, typename TOutputImage >
397 void
BoxSigmaCalculatorFunction(const TInputImage * accImage,TOutputImage * outputImage,typename TInputImage::RegionType inputRegion,typename TOutputImage::RegionType outputRegion,typename TInputImage::SizeType radius,ProgressReporter & progress)398 BoxSigmaCalculatorFunction(const TInputImage *accImage,
399 TOutputImage *outputImage,
400 typename TInputImage::RegionType inputRegion,
401 typename TOutputImage::RegionType outputRegion,
402 typename TInputImage::SizeType radius
403 #if defined(ITKV4_COMPATIBILITY)
404 , ProgressReporter & progress)
405 #else
406 )
407 #endif
408 {
409 // type alias
410 using InputImageType = TInputImage;
411 using RegionType = typename TInputImage::RegionType;
412 using SizeType = typename TInputImage::SizeType;
413 using IndexType = typename TInputImage::IndexType;
414 using OffsetType = typename TInputImage::OffsetType;
415 using OutputImageType = TOutputImage;
416 using OutputPixelType = typename TOutputImage::PixelType;
417 using InputPixelType = typename TInputImage::PixelType;
418 // use the face generator for speed
419 using FaceCalculatorType = typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< InputImageType >;
420 using FaceListType = typename FaceCalculatorType::FaceListType;
421 using FaceListTypeIt = typename FaceCalculatorType::FaceListType::iterator;
422 FaceCalculatorType faceCalculator;
423
424 FaceListType faceList;
425 FaceListTypeIt fit;
426 ZeroFluxNeumannBoundaryCondition< TInputImage > nbc;
427
428 // this process is actually slightly asymmetric because we need to
429 // subtract rectangles that are next to our kernel, not overlapping it
430 SizeType kernelSize;
431 SizeType internalRadius;
432 SizeType regionLimit;
433 IndexType regionStart = inputRegion.GetIndex();
434 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
435 {
436 kernelSize[i] = radius[i] * 2 + 1;
437 internalRadius[i] = radius[i] + 1;
438 regionLimit[i] = inputRegion.GetSize()[i] + regionStart[i] - 1;
439 }
440
441 using AccPixType = typename NumericTraits< OutputPixelType >::RealType;
442 // get a set of offsets to corners for a unit hypercube in this image
443 std::vector< OffsetType > unitCorners = CornerOffsets< TInputImage >(accImage);
444 std::vector< OffsetType > realCorners;
445 std::vector< AccPixType > weights;
446 // now compute the weights
447 for ( unsigned int k = 0; k < unitCorners.size(); k++ )
448 {
449 int prod = 1;
450 OffsetType thisCorner;
451 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
452 {
453 prod *= unitCorners[k][i];
454 if ( unitCorners[k][i] > 0 )
455 {
456 thisCorner[i] = radius[i];
457 }
458 else
459 {
460 thisCorner[i] = -( static_cast<OffsetValueType>( radius[i] ) + 1 );
461 }
462 }
463 weights.push_back( (AccPixType)prod );
464 realCorners.push_back(thisCorner);
465 }
466
467 faceList = faceCalculator(accImage, outputRegion, internalRadius);
468 // start with the body region
469 for ( fit = faceList.begin(); fit != faceList.end(); ++fit )
470 {
471 if ( fit == faceList.begin() )
472 {
473 // this is the body region. This is meant to be an optimized
474 // version that doesn't use neighborhood regions
475 // compute the various offsets
476 AccPixType pixelscount = 1;
477 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
478 {
479 pixelscount *= (AccPixType)( 2 * radius[i] + 1 );
480 }
481
482 using OutputIteratorType = ImageRegionIterator< OutputImageType >;
483 using InputIteratorType = ImageRegionConstIterator< InputImageType >;
484
485 using CornerItVecType = std::vector< InputIteratorType >;
486 CornerItVecType cornerItVec;
487 // set up the iterators for each corner
488 for ( unsigned int k = 0; k < realCorners.size(); k++ )
489 {
490 typename InputImageType::RegionType tReg = ( *fit );
491 tReg.SetIndex(tReg.GetIndex() + realCorners[k]);
492 InputIteratorType tempIt(accImage, tReg);
493 tempIt.GoToBegin();
494 cornerItVec.push_back(tempIt);
495 }
496 // set up the output iterator
497 OutputIteratorType oIt(outputImage, *fit);
498 // now do the work
499 for ( oIt.GoToBegin(); !oIt.IsAtEnd(); ++oIt )
500 {
501 AccPixType sum = 0;
502 AccPixType squareSum = 0;
503 // check each corner
504 for ( unsigned int k = 0; k < cornerItVec.size(); k++ )
505 {
506 const InputPixelType & i = cornerItVec[k].Get();
507 sum += weights[k] * i[0];
508 squareSum += weights[k] * i[1];
509 // increment each corner iterator
510 ++( cornerItVec[k] );
511 }
512
513 oIt.Set( static_cast< OutputPixelType >( std::sqrt( ( squareSum - sum * sum / pixelscount ) / ( pixelscount - 1 ) ) ) );
514 #if defined(ITKV4_COMPATIBILITY)
515 progress.CompletedPixel();
516 #endif
517 }
518 }
519 else
520 {
521 // now we need to deal with the border regions
522 using OutputIteratorType = ImageRegionIteratorWithIndex< OutputImageType >;
523 OutputIteratorType oIt(outputImage, *fit);
524 // now do the work
525 for ( oIt.GoToBegin(); !oIt.IsAtEnd(); ++oIt )
526 {
527 // figure out the number of pixels in the box by creating an
528 // equivalent region and cropping - this could probably be
529 // included in the loop below.
530 RegionType currentKernelRegion;
531 currentKernelRegion.SetSize(kernelSize);
532 // compute the region's index
533 IndexType kernelRegionIdx = oIt.GetIndex();
534 IndexType centIndex = kernelRegionIdx;
535 for ( unsigned int i = 0; i < TInputImage::ImageDimension; i++ )
536 {
537 kernelRegionIdx[i] -= radius[i];
538 }
539 currentKernelRegion.SetIndex(kernelRegionIdx);
540 currentKernelRegion.Crop(inputRegion);
541 SizeValueType edgepixelscount = currentKernelRegion.GetNumberOfPixels();
542 AccPixType sum = 0;
543 AccPixType squareSum = 0;
544 // rules are : for each corner,
545 // for each dimension
546 // if dimension offset is positive -> this is
547 // a leading edge. Crop if outside the input
548 // region
549 // if dimension offset is negative -> this is
550 // a trailing edge. Ignore if it is outside
551 // image region
552 for ( unsigned int k = 0; k < realCorners.size(); k++ )
553 {
554 IndexType thisCorner = centIndex + realCorners[k];
555 bool includeCorner = true;
556 for ( unsigned int j = 0; j < TInputImage::ImageDimension; j++ )
557 {
558 if ( unitCorners[k][j] > 0 )
559 {
560 // leading edge - crop it
561 if ( thisCorner[j] > static_cast< OffsetValueType >( regionLimit[j] ) )
562 {
563 thisCorner[j] = static_cast< OffsetValueType >( regionLimit[j] );
564 }
565 }
566 else
567 {
568 // trailing edge - check bounds
569 if ( thisCorner[j] < regionStart[j] )
570 {
571 includeCorner = false;
572 break;
573 }
574 }
575 }
576 if ( includeCorner )
577 {
578 const InputPixelType & i = accImage->GetPixel(thisCorner);
579 sum += weights[k] * i[0];
580 squareSum += weights[k] * i[1];
581 }
582 }
583
584 oIt.Set( static_cast< OutputPixelType >( std::sqrt( ( squareSum - sum * sum
585 / edgepixelscount ) / ( edgepixelscount - 1 ) ) ) );
586 #if defined(ITKV4_COMPATIBILITY)
587 progress.CompletedPixel();
588 #endif
589 }
590 }
591 }
592 }
593
594 template< typename TInputImage, typename TOutputImage >
595 void
BoxSquareAccumulateFunction(const TInputImage * inputImage,TOutputImage * outputImage,typename TInputImage::RegionType inputRegion,typename TOutputImage::RegionType outputRegion,ProgressReporter & progress)596 BoxSquareAccumulateFunction(const TInputImage *inputImage,
597 TOutputImage *outputImage,
598 typename TInputImage::RegionType inputRegion,
599 typename TOutputImage::RegionType outputRegion
600 #if defined(ITKV4_COMPATIBILITY)
601 , ProgressReporter & progress)
602 #else
603 )
604 #endif
605 {
606 // type alias
607 using InputImageType = TInputImage;
608 using OffsetType = typename TInputImage::OffsetType;
609 using OutputImageType = TOutputImage;
610 using OutputPixelType = typename TOutputImage::PixelType;
611 using ValueType = typename OutputPixelType::ValueType;
612 using InputPixelType = typename TInputImage::PixelType;
613
614 using InputIterator = ImageRegionConstIterator< TInputImage >;
615
616 using NOutputIterator = ShapedNeighborhoodIterator< TOutputImage >;
617 InputIterator inIt(inputImage, inputRegion);
618 typename TInputImage::SizeType kernelRadius;
619 kernelRadius.Fill(1);
620
621 NOutputIterator noutIt(kernelRadius, outputImage, outputRegion);
622 // this iterator is fully connected
623 itk_impl_details::setConnectivityEarlyBox(&noutIt, true);
624
625 ConstantBoundaryCondition< OutputImageType > oBC;
626 oBC.SetConstant(NumericTraits< OutputPixelType >::ZeroValue() );
627 noutIt.OverrideBoundaryCondition(&oBC);
628 // This uses several iterators. An alternative and probably better
629 // approach would be to copy the input to the output and convolve
630 // with the following weights (in 2D)
631 // -(dim - 1) 1
632 // 1 1
633 // The result of each convolution needs to get written back to the
634 // image being convolved so that the accumulation propagates
635 // This should be implementable with neighborhood operators.
636
637 std::vector< int > weights;
638 typename NOutputIterator::ConstIterator sIt;
639 for ( auto idxIt = noutIt.GetActiveIndexList().begin();
640 idxIt != noutIt.GetActiveIndexList().end();
641 idxIt++ )
642 {
643 OffsetType offset = noutIt.GetOffset(*idxIt);
644 int w = -1;
645 for ( unsigned int k = 0; k < InputImageType::ImageDimension; k++ )
646 {
647 if ( offset[k] != 0 )
648 {
649 w *= offset[k];
650 }
651 }
652 weights.push_back(w);
653 }
654
655 for ( inIt.GoToBegin(), noutIt.GoToBegin(); !noutIt.IsAtEnd(); ++inIt, ++noutIt )
656 {
657 ValueType sum = 0;
658 ValueType squareSum = 0;
659 int k;
660 for ( k = 0, sIt = noutIt.Begin(); !sIt.IsAtEnd(); ++sIt, ++k )
661 {
662 const OutputPixelType & v = sIt.Get();
663 sum += v[0] * weights[k];
664 squareSum += v[1] * weights[k];
665 }
666 OutputPixelType o;
667 const InputPixelType & i = inIt.Get();
668 o[0] = sum + i;
669 o[1] = squareSum + i * i;
670 noutIt.SetCenterPixel(o);
671 #if defined(ITKV4_COMPATIBILITY)
672 progress.CompletedPixel();
673 #endif
674 }
675 }
676 } //namespace itk
677
678 #endif
679