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