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 itkBinaryContourImageFilter_hxx
19 #define itkBinaryContourImageFilter_hxx
20 
21 #include "itkBinaryContourImageFilter.h"
22 
23 #include "itkImageScanlineIterator.h"
24 #include "itkConstShapedNeighborhoodIterator.h"
25 #include "itkMaskImageFilter.h"
26 #include "itkConnectedComponentAlgorithm.h"
27 #include "itkProgressTransformer.h"
28 #include "itkMath.h"
29 
30 namespace itk
31 {
32 template< typename TInputImage, typename TOutputImage >
33 BinaryContourImageFilter< TInputImage, TOutputImage >
BinaryContourImageFilter()34 ::BinaryContourImageFilter() :
35   ScanlineFilterCommon< TInputImage, TOutputImage >(this),
36   m_ForegroundValue( NumericTraits< OutputImagePixelType >::max() ),
37   m_BackgroundValue( NumericTraits< OutputImagePixelType >::NonpositiveMin() )
38 {
39   this->SetInPlace(false);
40   this->DynamicMultiThreadingOn();
41 }
42 
43 
44 template< typename TInputImage, typename TOutputImage >
45 void
46 BinaryContourImageFilter< TInputImage, TOutputImage >
GenerateInputRequestedRegion()47 ::GenerateInputRequestedRegion()
48 {
49   // call the superclass' implementation of this method
50   Superclass::GenerateInputRequestedRegion();
51 
52   // We need all the input.
53   InputImagePointer input = const_cast< InputImageType * >( this->GetInput() );
54   if ( !input )
55     {
56     return;
57     }
58   input->SetRequestedRegionToLargestPossibleRegion();
59 }
60 
61 template< typename TInputImage, typename TOutputImage >
62 void
63 BinaryContourImageFilter< TInputImage, TOutputImage >
EnlargeOutputRequestedRegion(DataObject *)64 ::EnlargeOutputRequestedRegion(DataObject *)
65 {
66   OutputImagePointer output = this->GetOutput();
67   output->SetRequestedRegionToLargestPossibleRegion();
68 }
69 
70 template<typename TInputImage, typename TOutputImage>
71 void
72 BinaryContourImageFilter<TInputImage, TOutputImage>
GenerateData()73 ::GenerateData()
74 {
75   this->UpdateProgress(0.0f);
76   this->AllocateOutputs();
77   this->BeforeThreadedGenerateData();
78   this->SetupLineOffsets(true);
79 
80   ProgressTransformer progress1( 0.05f, 0.5f, this );
81 
82   RegionType reqRegion = this->GetOutput()->GetRequestedRegion();
83 
84   this->GetMultiThreader()->SetNumberOfWorkUnits( this->GetNumberOfWorkUnits() );
85   //parallelize in a way which does not split the region along X axis
86   constexpr unsigned int restrictedDirection = 0;
87   this->GetMultiThreader()->template ParallelizeImageRegionRestrictDirection< ImageDimension >( restrictedDirection,
88     reqRegion,
89     [this](const RegionType & outputRegionForThread) { this->DynamicThreadedGenerateData( outputRegionForThread ); },
90     progress1.GetProcessObject()
91     );
92 
93   ProgressTransformer progress2( 0.5f, 0.99f, this );
94 
95   //avoid splitting the region along X
96   this->GetMultiThreader()->template ParallelizeImageRegionRestrictDirection< ImageDimension >( restrictedDirection,
97     reqRegion,
98     [this](const RegionType & outputRegionForThread) { this->ThreadedIntegrateData( outputRegionForThread ); },
99     progress2.GetProcessObject()
100     );
101 
102   this->AfterThreadedGenerateData();
103   this->UpdateProgress(1.0f);
104 }
105 
106 template< typename TInputImage, typename TOutputImage >
107 void
108 BinaryContourImageFilter< TInputImage, TOutputImage >
BeforeThreadedGenerateData()109 ::BeforeThreadedGenerateData()
110 {
111   OutputImagePointer output = this->GetOutput();
112   InputImageConstPointer input = this->GetInput();
113 
114   RegionType reqRegion = output->GetRequestedRegion();
115   SizeValueType pixelcount = reqRegion.GetNumberOfPixels();
116   SizeValueType xsize = reqRegion.GetSize()[0];
117   SizeValueType linecount = pixelcount / xsize;
118 
119   m_ForegroundLineMap.clear();
120   m_ForegroundLineMap.resize(linecount);
121 
122   m_BackgroundLineMap.clear();
123   m_BackgroundLineMap.resize(linecount);
124 }
125 
126 template< typename TInputImage, typename TOutputImage >
127 void
128 BinaryContourImageFilter< TInputImage, TOutputImage >
DynamicThreadedGenerateData(const RegionType & outputRegionForThread)129 ::DynamicThreadedGenerateData(const RegionType & outputRegionForThread)
130 {
131   OutputImagePointer      output  = this->GetOutput();
132   InputImageConstPointer  input   = this->GetInput();
133 
134   using InputLineIteratorType = ImageScanlineConstIterator< InputImageType >;
135   InputLineIteratorType inLineIt(input, outputRegionForThread);
136 
137   using OutputLineIteratorType = ImageScanlineIterator<OutputImageType>;
138   OutputLineIteratorType outLineIt(output, outputRegionForThread);
139 
140   outLineIt.GoToBegin();
141   for ( inLineIt.GoToBegin();
142         !inLineIt.IsAtEnd();
143         inLineIt.NextLine(), outLineIt.NextLine() )
144     {
145     SizeValueType lineId = this->IndexToLinearIndex( inLineIt.GetIndex() );
146     LineEncodingType fgLine;
147     LineEncodingType bgLine;
148 
149     while ( !inLineIt.IsAtEndOfLine() )
150       {
151       InputImagePixelType PVal = inLineIt.Get();
152 
153       if ( Math::AlmostEquals(PVal, m_ForegroundValue) )
154         {
155         // We've hit the start of a run
156         SizeValueType length = 0;
157         IndexType thisIndex = inLineIt.GetIndex();
158 
159         outLineIt.Set(m_BackgroundValue);
160 
161         ++length;
162         ++inLineIt;
163         ++outLineIt;
164 
165         while ( !inLineIt.IsAtEndOfLine()
166                 && Math::AlmostEquals( inLineIt.Get(), m_ForegroundValue ) )
167           {
168           outLineIt.Set(m_BackgroundValue);
169           ++length;
170           ++inLineIt;
171           ++outLineIt;
172           }
173         // create the run length object to go in the vector
174         fgLine.push_back( RunLength( length, thisIndex ) );
175         }
176       else
177         {
178         // We've hit the start of a run
179         SizeValueType length = 0;
180         IndexType thisIndex = inLineIt.GetIndex();
181 
182         outLineIt.Set(PVal);
183         ++length;
184         ++inLineIt;
185         ++outLineIt;
186         while ( !inLineIt.IsAtEndOfLine()
187                 && Math::NotAlmostEquals( inLineIt.Get(), m_ForegroundValue ) )
188           {
189           outLineIt.Set( inLineIt.Get() );
190           ++length;
191           ++inLineIt;
192           ++outLineIt;
193           }
194         // create the run length object to go in the vector
195         bgLine.push_back( RunLength( length, thisIndex ) );
196         }
197       }
198 
199     m_ForegroundLineMap[lineId] = fgLine;
200     m_BackgroundLineMap[lineId] = bgLine;
201     lineId++;
202     }
203 }
204 
205 template< typename TInputImage, typename TOutputImage >
206 void
207 BinaryContourImageFilter< TInputImage, TOutputImage >
ThreadedIntegrateData(const RegionType & outputRegionForThread)208 ::ThreadedIntegrateData(const RegionType & outputRegionForThread)
209 {
210   OutputImagePointer output = this->GetOutput();
211 
212   using OutputLineIteratorType = ImageScanlineIterator<OutputImageType>;
213   OutputLineIteratorType outLineIt(output, outputRegionForThread);
214 
215   OffsetValueType linecount = m_ForegroundLineMap.size();
216 
217   for (outLineIt.GoToBegin(); !outLineIt.IsAtEnd(); outLineIt.NextLine())
218     {
219     SizeValueType thisIdx = this->IndexToLinearIndex(outLineIt.GetIndex());
220     if ( !m_ForegroundLineMap[thisIdx].empty() )
221       {
222       for ( OffsetVectorConstIterator I = this->m_LineOffsets.begin();
223             I != this->m_LineOffsets.end();
224             ++I )
225         {
226         OffsetValueType neighIdx = thisIdx + ( *I );
227 
228         // check if the neighbor is in the map
229         if ( neighIdx >= 0 && neighIdx < OffsetValueType(linecount) && !m_BackgroundLineMap[neighIdx].empty() )
230           {
231           // Now check whether they are really neighbors
232           bool areNeighbors = this->CheckNeighbors(m_ForegroundLineMap[thisIdx][0].where, m_BackgroundLineMap[neighIdx][0].where);
233           if ( areNeighbors )
234             {
235             this->CompareLines(
236               m_ForegroundLineMap[thisIdx],
237               m_BackgroundLineMap[neighIdx],
238               true,
239               false,
240               m_BackgroundValue,
241               [this, output](
242                  const LineEncodingConstIterator& foregroundRun,
243                  const LineEncodingConstIterator&,
244                  OffsetValueType oStart,
245                  OffsetValueType oLast)
246               {
247                 itkAssertInDebugAndIgnoreInReleaseMacro(oStart <= oLast);
248                 OutputIndexType idx = foregroundRun->where;
249                 for ( OffsetValueType x = oStart; x <= oLast; ++x )
250                   {
251                   idx[0] = x;
252                   output->SetPixel(idx, this->m_ForegroundValue);
253                   }
254               });
255             }
256           }
257         }
258       }
259     }
260 }
261 
262 template< typename TInputImage, typename TOutputImage >
263 void
264 BinaryContourImageFilter< TInputImage, TOutputImage >
AfterThreadedGenerateData()265 ::AfterThreadedGenerateData()
266 {
267   m_ForegroundLineMap.clear();
268   m_BackgroundLineMap.clear();
269 }
270 
271 template< typename TInputImage, typename TOutputImage >
272 void
273 BinaryContourImageFilter< TInputImage, TOutputImage >
PrintSelf(std::ostream & os,Indent indent) const274 ::PrintSelf(std::ostream & os, Indent indent) const
275 {
276   Superclass::PrintSelf(os, indent);
277 
278   os << indent << "FullyConnected: "  << this->m_FullyConnected << std::endl;
279   os << indent << "BackgroundValue: " <<
280         static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_BackgroundValue ) << std::endl;
281   os << indent << "ForegroundValue: "
282      << static_cast< typename NumericTraits< InputImagePixelType >::PrintType >( m_ForegroundValue ) << std::endl;
283 }
284 } // end namespace itk
285 
286 #endif
287