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 itkMovingHistogramImageFilter_hxx
19 #define itkMovingHistogramImageFilter_hxx
20 
21 #include "itkMovingHistogramImageFilter.h"
22 #include "itkImageRegionIteratorWithIndex.h"
23 #include "itkOffset.h"
24 #include "itkProgressReporter.h"
25 #include "itkNumericTraits.h"
26 #include "itkImageRegionIterator.h"
27 #include "itkImageLinearConstIteratorWithIndex.h"
28 
29 namespace itk
30 {
31 template< typename TInputImage, typename TOutputImage, typename TKernel, typename THistogram >
32 MovingHistogramImageFilter< TInputImage, TOutputImage, TKernel, THistogram >
MovingHistogramImageFilter()33 ::MovingHistogramImageFilter()
34 {
35   this->DynamicMultiThreadingOn();
36 }
37 
38 // a modified version that uses line iterators and only moves the
39 // histogram in one direction. Hopefully it will be a bit simpler and
40 // faster due to improved memory access and a tighter loop.
41 template< typename TInputImage, typename TOutputImage, typename TKernel, typename THistogram >
42 void
43 MovingHistogramImageFilter< TInputImage, TOutputImage, TKernel, THistogram >
DynamicThreadedGenerateData(const OutputImageRegionType & outputRegionForThread)44 ::DynamicThreadedGenerateData(const OutputImageRegionType & outputRegionForThread)
45 {
46   HistogramType histogram;
47   this->ConfigureHistogram( histogram );
48 
49   OutputImageType *     outputImage = this->GetOutput();
50   const InputImageType *inputImage = this->GetInput();
51   RegionType            inputRegion = inputImage->GetRequestedRegion();
52 
53   // initialize the histogram
54   for ( auto listIt = this->m_KernelOffsets.begin();
55         listIt != this->m_KernelOffsets.end();
56         listIt++ )
57     {
58     IndexType idx = outputRegionForThread.GetIndex() + ( *listIt );
59     if ( inputRegion.IsInside(idx) )
60       {
61       histogram.AddPixel( inputImage->GetPixel(idx) );
62       }
63     else
64       {
65       histogram.AddBoundary();
66       }
67     }
68 
69   // now move the histogram
70   FixedArray< short, ImageDimension > direction;
71   direction.Fill(1);
72   int axis = ImageDimension - 1;
73   OffsetType offset;
74   offset.Fill(0);
75   RegionType stRegion;
76   stRegion.SetSize( this->m_Kernel.GetSize() );
77   stRegion.PadByRadius(1);   // must pad the region by one because of the translation
78 
79   OffsetType   centerOffset;
80   unsigned int i;
81   for ( i = 0; i < ImageDimension; i++ )
82     {
83     centerOffset[i] = stRegion.GetSize()[i] / 2;
84     }
85 
86   int BestDirection = this->m_Axes[axis];
87   int LineLength = inputRegion.GetSize()[BestDirection];
88 
89   // init the offset and get the lists for the best axis
90   offset[BestDirection] = direction[BestDirection];
91   // it's very important for performances to get a pointer and not a copy
92   const OffsetListType *addedList = &this->m_AddedOffsets[offset];
93   const OffsetListType *removedList = &this->m_RemovedOffsets[offset];
94 
95   using InputLineIteratorType = ImageLinearConstIteratorWithIndex< InputImageType >;
96   InputLineIteratorType InLineIt(inputImage, outputRegionForThread);
97   InLineIt.SetDirection(BestDirection);
98 
99   InLineIt.GoToBegin();
100   IndexType LineStart;
101   //PrevLineStart = InLineIt.GetIndex();
102   InLineIt.GoToBegin();
103 
104   using HistVecType = typename std::vector< HistogramType >;
105   HistVecType HistVec(ImageDimension);
106   using IndexVecType = typename std::vector< IndexType >;
107   IndexVecType PrevLineStartVec(ImageDimension);
108 
109   // Steps is used to keep track of the order in which the line
110   // iterator passes over the various dimensions.
111   auto * Steps = new int[ImageDimension];
112 
113   for ( i = 0; i < ImageDimension; i++ )
114     {
115     HistVec[i] = histogram;
116     PrevLineStartVec[i] = InLineIt.GetIndex();
117     Steps[i] = 0;
118     }
119 
120   while ( !InLineIt.IsAtEnd() )
121     {
122     HistogramType & histRef = HistVec[BestDirection];
123     IndexType      PrevLineStart = InLineIt.GetIndex();
124     for ( InLineIt.GoToBeginOfLine(); !InLineIt.IsAtEndOfLine(); ++InLineIt )
125       {
126       // Update the historgram
127       IndexType currentIdx = InLineIt.GetIndex();
128       outputImage->SetPixel( currentIdx,
129                              static_cast< OutputPixelType >( histRef.GetValue( inputImage->GetPixel(currentIdx) ) ) );
130       stRegion.SetIndex(currentIdx - centerOffset);
131       PushHistogram(histRef, addedList, removedList, inputRegion,
132                     stRegion, inputImage, currentIdx);
133       }
134     Steps[BestDirection] += LineLength;
135     InLineIt.NextLine();
136     if ( InLineIt.IsAtEnd() )
137       {
138       break;
139       }
140     LineStart = InLineIt.GetIndex();
141     // This section updates the histogram for the next line
142     // Since we aren't zig zagging we need to figure out which
143     // histogram to update and the direction in which to push
144     // it. Then we need to copy that histogram to the relevant
145     // places
146     OffsetType LineOffset, Changes;
147     // Figure out which stored histogram to move and in
148     // which direction
149     int LineDirection = 0;
150     // This function deals with changing planes etc
151     this->GetDirAndOffset(LineStart, PrevLineStart,
152                     LineOffset, Changes, LineDirection);
153     ++( Steps[LineDirection] );
154     IndexType             PrevLineStartHist = LineStart - LineOffset;
155     const OffsetListType *addedListLine = &this->m_AddedOffsets[LineOffset];
156     const OffsetListType *removedListLine = &this->m_RemovedOffsets[LineOffset];
157     HistogramType &       tmpHist = HistVec[LineDirection];
158     stRegion.SetIndex(PrevLineStart - centerOffset);
159     // Now move the histogram
160     PushHistogram(tmpHist, addedListLine, removedListLine, inputRegion,
161                   stRegion, inputImage, PrevLineStartHist);
162 
163     //PrevLineStartVec[LineDirection] = LineStart;
164     // copy the updated histogram and line start entries to the
165     // relevant directions. When updating direction 2, for example,
166     // new copies of directions 0 and 1 should be made.
167     for ( i = 0; i < ImageDimension; i++ )
168       {
169       if ( Steps[i] > Steps[LineDirection] )
170         {
171         //PrevLineStartVec[i] = LineStart;
172         HistVec[i] = HistVec[LineDirection];
173         }
174       }
175     }
176   delete[] Steps;
177 }
178 
179 template< typename TInputImage, typename TOutputImage, typename TKernel, typename THistogram >
180 void
181 MovingHistogramImageFilter< TInputImage, TOutputImage, TKernel, THistogram >
PushHistogram(HistogramType & histogram,const OffsetListType * addedList,const OffsetListType * removedList,const RegionType & inputRegion,const RegionType & kernRegion,const InputImageType * inputImage,const IndexType currentIdx)182 ::PushHistogram(HistogramType & histogram,
183                 const OffsetListType *addedList,
184                 const OffsetListType *removedList,
185                 const RegionType & inputRegion,
186                 const RegionType & kernRegion,
187                 const InputImageType *inputImage,
188                 const IndexType currentIdx)
189 {
190   if ( inputRegion.IsInside(kernRegion) )
191     {
192     // update the histogram
193     for ( auto addedIt = addedList->begin(); addedIt != addedList->end(); addedIt++ )
194       {
195       histogram.AddPixel( inputImage->GetPixel( currentIdx + ( *addedIt ) ) );
196       }
197     for ( auto removedIt = removedList->begin();
198           removedIt != removedList->end();
199           removedIt++ )
200       {
201       histogram.RemovePixel( inputImage->GetPixel( currentIdx + ( *removedIt ) ) );
202       }
203     }
204   else
205     {
206     // update the histogram
207     for ( auto addedIt = addedList->begin(); addedIt != addedList->end(); addedIt++ )
208       {
209       IndexType idx = currentIdx + ( *addedIt );
210       if ( inputRegion.IsInside(idx) )
211                 { histogram.AddPixel( inputImage->GetPixel(idx) ); }
212       else
213                 { histogram.AddBoundary(); }
214       }
215     for ( auto removedIt = removedList->begin();
216           removedIt != removedList->end();
217           removedIt++ )
218       {
219       IndexType idx = currentIdx + ( *removedIt );
220       if ( inputRegion.IsInside(idx) )
221                 { histogram.RemovePixel( inputImage->GetPixel(idx) ); }
222       else
223                 { histogram.RemoveBoundary(); }
224       }
225     }
226 }
227 
228 } // end namespace itk
229 #endif
230