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