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 itkConnectedComponentImageFilter_hxx
19 #define itkConnectedComponentImageFilter_hxx
20 
21 #include "itkConnectedComponentImageFilter.h"
22 
23 #include "itkImageScanlineIterator.h"
24 #include "itkConstShapedNeighborhoodIterator.h"
25 #include "itkImageRegionIterator.h"
26 #include "itkMaskImageFilter.h"
27 #include "itkConnectedComponentAlgorithm.h"
28 #include "itkProgressTransformer.h"
29 
30 namespace itk
31 {
32 template< typename TInputImage, typename TOutputImage, typename TMaskImage >
33 void
34 ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
GenerateInputRequestedRegion()35 ::GenerateInputRequestedRegion()
36 {
37   // call the superclass' implementation of this method
38   Superclass::GenerateInputRequestedRegion();
39 
40   // We need all the input.
41   InputImagePointer input = const_cast< InputImageType * >( this->GetInput() );
42   if ( !input )
43     {
44     return;
45     }
46   input->SetRequestedRegion( input->GetLargestPossibleRegion() );
47 
48   MaskImagePointer mask = const_cast< MaskImageType * >( this->GetMaskImage() );
49   if ( mask )
50     {
51     mask->SetRequestedRegion( input->GetLargestPossibleRegion() );
52     }
53 }
54 
55 template< typename TInputImage, typename TOutputImage, typename TMaskImage >
56 void
57 ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
EnlargeOutputRequestedRegion(DataObject *)58 ::EnlargeOutputRequestedRegion(DataObject *)
59 {
60   this->GetOutput()
61   ->SetRequestedRegion( this->GetOutput()->GetLargestPossibleRegion() );
62 }
63 
64 template< typename TInputImage, typename TOutputImage, typename TMaskImage >
65 void
66 ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
GenerateData()67 ::GenerateData()
68 {
69   this->AllocateOutputs();
70   this->SetupLineOffsets( false );
71   typename TInputImage::ConstPointer input = this->GetInput();
72   typename TMaskImage::ConstPointer mask = this->GetMaskImage();
73 
74   using MaskFilterType = MaskImageFilter< TInputImage, TMaskImage, TInputImage >;
75   typename MaskFilterType::Pointer maskFilter = MaskFilterType::New();
76   if ( mask )
77     {
78     maskFilter->SetInput(input);
79     maskFilter->SetMaskImage(mask);
80     maskFilter->Update();
81     m_Input = maskFilter->GetOutput();
82     }
83   else
84     {
85     m_Input = input;
86     }
87 
88   const typename OutputImageType::RegionType& requestedRegion = this->GetOutput()->GetRequestedRegion();
89   const typename OutputImageType::SizeType& requestedSize = requestedRegion.GetSize();
90 
91   // set up the vars used in the threads
92   const SizeValueType pixelcount = requestedRegion.GetNumberOfPixels();
93   const SizeValueType xsize = requestedSize[0];
94   const SizeValueType linecount = pixelcount / xsize;
95   this->m_LineMap.resize( linecount );
96   this->m_NumberOfLabels.store( 0 );
97 
98   ProgressTransformer progress1( 0.0f, 0.5f, this );
99 
100   MultiThreaderBase* multiThreader = this->GetMultiThreader();
101   multiThreader->SetNumberOfWorkUnits( this->GetNumberOfWorkUnits() );
102   multiThreader->template ParallelizeImageRegionRestrictDirection< TOutputImage::ImageDimension >(
103     0,
104     requestedRegion,
105     [this]( const RegionType& lambdaRegion )
106     {
107       this->DynamicThreadedGenerateData( lambdaRegion );
108     },
109     progress1.GetProcessObject() );
110 
111   SizeValueType nbOfLabels = this->m_NumberOfLabels.load();
112 
113   // insert all the labels into the structure -- an extra loop but
114   // saves complicating the ones that come later
115   this->InitUnion( nbOfLabels );
116 
117   ProgressTransformer progress2( 0.55f, 0.6f, this );
118   multiThreader->ParallelizeArray(
119     0, this->m_WorkUnitResults.size(), [this]( SizeValueType index ) { this->ComputeEquivalence( index, true ); }, progress2.GetProcessObject());
120 
121   ProgressTransformer progress3( 0.6f, 0.75f, this );
122   multiThreader->ParallelizeArray(
123     0, this->m_WorkUnitResults.size(), [this]( SizeValueType index ) { this->ComputeEquivalence( index, false ); }, progress3.GetProcessObject());
124 
125   // AfterThreadedGenerateData
126   SizeValueType numberOfObjects = this->CreateConsecutive( m_BackgroundValue );
127   itkAssertOrThrowMacro( numberOfObjects <= this->m_NumberOfLabels,
128     "Number of consecutive labels cannot be greater than the initial number of labels!");
129   // check for overflow exception here
130   if ( numberOfObjects > static_cast< SizeValueType >( NumericTraits< OutputPixelType >::max() ) )
131     {
132     itkExceptionMacro( << "Number of objects (" << numberOfObjects << ") greater than maximum of output pixel type ("
133       << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( NumericTraits< OutputPixelType >::max() ) << ").");
134     }
135   m_ObjectCount = numberOfObjects;
136 
137   ProgressTransformer progress4( 0.75f, 1.0f, this );
138   multiThreader->template ParallelizeImageRegionRestrictDirection< TOutputImage::ImageDimension >(
139     0,
140     requestedRegion,
141     [this]( const RegionType& lambdaRegion )
142     {
143       this->ThreadedWriteOutput( lambdaRegion );
144     },
145     progress4.GetProcessObject() );
146 
147   // clear and make sure memory is freed
148   std::deque< WorkUnitData >().swap( this->m_WorkUnitResults );
149   OffsetVectorType().swap( this->m_LineOffsets );
150   LineMapType().swap( this->m_LineMap );
151   ConsecutiveVectorType().swap( this->m_Consecutive );
152   UnionFindType().swap( this->m_UnionFind );
153   m_Input = nullptr;
154 }
155 
156 template< typename TInputImage, typename TOutputImage, typename TMaskImage >
157 void
158 ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
DynamicThreadedGenerateData(const RegionType & outputRegionForThread)159 ::DynamicThreadedGenerateData(const RegionType & outputRegionForThread)
160 {
161   using InputLineIteratorType = ImageScanlineConstIterator< InputImageType >;
162   InputLineIteratorType inLineIt(m_Input, outputRegionForThread);
163 
164   WorkUnitData workUnitData = this->CreateWorkUnitData( outputRegionForThread );
165   SizeValueType lineId = workUnitData.firstLine;
166 
167   SizeValueType nbOfLabels = 0;
168   for ( inLineIt.GoToBegin();
169         !inLineIt.IsAtEnd();
170         inLineIt.NextLine() )
171     {
172     LineEncodingType thisLine;
173     while ( !inLineIt.IsAtEndOfLine() )
174       {
175       const InputPixelType PVal = inLineIt.Get();
176       //std::cout << inLineIt.GetIndex() << std::endl;
177       if ( PVal != NumericTraits< InputPixelType >::ZeroValue( PVal ) )
178         {
179         // We've hit the start of a run
180         const IndexType thisIndex = inLineIt.GetIndex();
181         //std::cout << thisIndex << std::endl;
182         SizeValueType length = 1;
183         ++inLineIt;
184         while ( !inLineIt.IsAtEndOfLine() && inLineIt.Get() != NumericTraits< InputPixelType >::ZeroValue( PVal ) )
185           {
186           ++length;
187           ++inLineIt;
188           }
189         // create the run length object to go in the vector
190         RunLength thisRun = { length, thisIndex, 0 };
191         thisLine.push_back(thisRun);
192         nbOfLabels++;
193         }
194       else
195         {
196         ++inLineIt;
197         }
198       }
199     this->m_LineMap[lineId] = thisLine;
200     lineId++;
201     }
202 
203   this->m_NumberOfLabels.fetch_add( nbOfLabels, std::memory_order_relaxed );
204   std::lock_guard< std::mutex > mutexHolder( this->m_Mutex );
205   this->m_WorkUnitResults.push_back( workUnitData );
206 }
207 
208 template< typename TInputImage, typename TOutputImage, typename TMaskImage >
209 void
210 ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
ThreadedWriteOutput(const RegionType & outputRegionForThread)211 ::ThreadedWriteOutput(const RegionType & outputRegionForThread)
212 {
213   // A more complex version that is intended to minimize the number of
214   // visits to the output image which should improve cache
215   // performance on large images. We also want to optimize the
216   // performance of the map by being able to iterate through it,
217   // rather than do lots of look ups. Don't know whether that will
218   // make much difference in practice.
219   // Note - this is unnecessary if AllocateOutputs initalizes to zero
220 
221   OutputImageType * output = this->GetOutput();
222   ImageRegionIterator< OutputImageType > oit(output, outputRegionForThread);
223   ImageRegionIterator< OutputImageType > fstart = oit;
224   ImageRegionIterator< OutputImageType > fend = oit;
225   fend.GoToEnd();
226 
227   WorkUnitData workUnitData = this->CreateWorkUnitData( outputRegionForThread );
228 
229   for ( SizeValueType thisIdx = workUnitData.firstLine;
230         thisIdx <= workUnitData.lastLine;
231         thisIdx++ )
232     {
233     for ( LineEncodingConstIterator cIt = this->m_LineMap[thisIdx].begin();
234           cIt != this->m_LineMap[thisIdx].end();
235           ++cIt )
236       {
237       const SizeValueType Ilab = this->LookupSet(cIt->label);
238       const OutputPixelType lab = this->m_Consecutive[Ilab];
239       oit.SetIndex(cIt->where);
240       // initialize the non labelled pixels
241       for (; fstart != oit; ++fstart )
242         {
243         fstart.Set(m_BackgroundValue);
244         }
245       // now fill the labelled sections
246       for ( SizeValueType i = 0; i < (SizeValueType)cIt->length; ++i, ++oit )
247         {
248         oit.Set(lab);
249         }
250       fstart = oit;
251       }
252     }
253 
254   // fill the rest of the output region with background value
255   for (; fstart != fend; ++fstart )
256     {
257     fstart.Set(m_BackgroundValue);
258     }
259 }
260 
261 template< typename TInputImage, typename TOutputImage, typename TMaskImage >
262 void
263 ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
PrintSelf(std::ostream & os,Indent indent) const264 ::PrintSelf(std::ostream & os, Indent indent) const
265 {
266   Superclass::PrintSelf(os, indent);
267 
268   os << indent << "ObjectCount: "  << m_ObjectCount << std::endl;
269 }
270 } // end namespace itk
271 
272 #endif
273