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 itkBinaryImageToLabelMapFilter_hxx
19 #define itkBinaryImageToLabelMapFilter_hxx
20
21 #include "itkBinaryImageToLabelMapFilter.h"
22 #include "itkNumericTraits.h"
23 #include "itkImageScanlineIterator.h"
24 #include "itkConstShapedNeighborhoodIterator.h"
25 #include "itkConnectedComponentAlgorithm.h"
26 #include "itkProgressReporter.h"
27 #include "itkProgressTransformer.h"
28
29 namespace itk
30 {
31 template< typename TInputImage, typename TOutputImage >
32 BinaryImageToLabelMapFilter< TInputImage, TOutputImage >
BinaryImageToLabelMapFilter()33 ::BinaryImageToLabelMapFilter() :
34 ScanlineFilterCommon< TInputImage, TOutputImage >(this),
35 m_OutputBackgroundValue( NumericTraits< OutputPixelType >::NonpositiveMin() )
36 {
37 this->m_NumberOfObjects = 0;
38 this->m_InputForegroundValue = NumericTraits< InputPixelType >::max();
39 }
40
41 template< typename TInputImage, typename TOutputImage >
42 void
43 BinaryImageToLabelMapFilter< TInputImage, TOutputImage >
GenerateInputRequestedRegion()44 ::GenerateInputRequestedRegion()
45 {
46 // call the superclass' implementation of this method
47 Superclass::GenerateInputRequestedRegion();
48
49 // We need all the input.
50 InputImagePointer input = const_cast< InputImageType * >( this->GetInput() );
51 if ( !input )
52 {
53 return;
54 }
55 input->SetRequestedRegion( input->GetLargestPossibleRegion() );
56 }
57
58 template< typename TInputImage, typename TOutputImage >
59 void
60 BinaryImageToLabelMapFilter< TInputImage, TOutputImage >
EnlargeOutputRequestedRegion(DataObject *)61 ::EnlargeOutputRequestedRegion(DataObject *)
62 {
63 TOutputImage * output = this->GetOutput();
64 output->SetRequestedRegion( output->GetLargestPossibleRegion() );
65 }
66
67 template< typename TInputImage, typename TOutputImage >
68 void
69 BinaryImageToLabelMapFilter< TInputImage, TOutputImage >
GenerateData()70 ::GenerateData()
71 {
72 // Call a method that can be overriden by a subclass to allocate
73 // memory for the filter's outputs
74 this->AllocateOutputs();
75 this->SetupLineOffsets( false );
76 OutputImageType * output = this->GetOutput();
77 output->SetBackgroundValue(this->m_OutputBackgroundValue);
78
79 const typename OutputImageType::RegionType & requestedRegion = output->GetRequestedRegion();
80 const typename OutputImageType::SizeType & requestedSize = requestedRegion.GetSize();
81
82 const SizeValueType pixelcount = requestedRegion.GetNumberOfPixels();
83 const SizeValueType xsize = requestedSize[0];
84 const SizeValueType linecount = pixelcount / xsize;
85 this->m_LineMap.resize(linecount);
86 this->m_NumberOfLabels.store( 0 );
87 this->SetupLineOffsets( false );
88
89 ProgressTransformer progress1( 0.0f, 0.5f, this );
90
91 MultiThreaderBase* multiThreader = this->GetMultiThreader();
92 multiThreader->SetNumberOfWorkUnits( this->GetNumberOfWorkUnits() );
93 multiThreader->template ParallelizeImageRegionRestrictDirection< TOutputImage::ImageDimension >(
94 0,
95 requestedRegion,
96 [this]( const RegionType& lambdaRegion )
97 {
98 this->DynamicThreadedGenerateData( lambdaRegion );
99 },
100 progress1.GetProcessObject());
101
102 // compute the total number of labels
103 SizeValueType nbOfLabels = this->m_NumberOfLabels.load();
104
105 // insert all the labels into the structure -- an extra loop but
106 // saves complicating the ones that come later
107 this->InitUnion( nbOfLabels );
108
109 ProgressTransformer progress2( 0.55f, 0.6f, this );
110 multiThreader->ParallelizeArray(
111 0, this->m_WorkUnitResults.size(), [this]( SizeValueType index ) { this->ComputeEquivalence( index, true ); }, progress2.GetProcessObject());
112
113 ProgressTransformer progress3( 0.6f, 0.75f, this );
114 multiThreader->ParallelizeArray(
115 0, this->m_WorkUnitResults.size(), [this]( SizeValueType index ) { this->ComputeEquivalence( index, false ); }, progress3.GetProcessObject());
116
117 // AfterThreadedGenerateData
118 typename TInputImage::ConstPointer input = this->GetInput();
119 m_NumberOfObjects = this->CreateConsecutive(m_OutputBackgroundValue);
120 ProgressReporter progress(this, 0, linecount, 25, 0.75f, 0.25f);
121 // check for overflow exception here
122 if ( m_NumberOfObjects > static_cast< SizeValueType >( NumericTraits< OutputPixelType >::max() ) )
123 {
124 itkExceptionMacro( << "Number of objects (" << m_NumberOfObjects << ") greater than maximum of output pixel type ("
125 << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( NumericTraits< OutputPixelType >::max() ) << ").");
126 }
127
128 for ( SizeValueType thisIdx = 0; thisIdx < linecount; thisIdx++ )
129 {
130 // now fill the labelled sections
131 LineEncodingConstIterator cIt = this->m_LineMap[thisIdx].begin();
132 const LineEncodingConstIterator cEnd = this->m_LineMap[thisIdx].end();
133
134 while ( cIt != cEnd )
135 {
136 const InternalLabelType Ilab = this->LookupSet(cIt->label);
137 const OutputPixelType lab = this->m_Consecutive[Ilab];
138 output->SetLine(cIt->where, cIt->length, lab);
139 ++cIt;
140 }
141 progress.CompletedPixel();
142 }
143
144 //clear and make sure memory is freed
145 std::deque<WorkUnitData>().swap(this->m_WorkUnitResults);
146 OffsetVectorType().swap(this->m_LineOffsets);
147 LineMapType().swap(this->m_LineMap);
148 }
149
150 template< typename TInputImage, typename TOutputImage >
151 void
152 BinaryImageToLabelMapFilter< TInputImage, TOutputImage >
DynamicThreadedGenerateData(const RegionType & outputRegionForThread)153 ::DynamicThreadedGenerateData(const RegionType & outputRegionForThread)
154 {
155 const TInputImage * input = this->GetInput();
156 using InputLineIteratorType = ImageScanlineConstIterator< InputImageType >;
157 InputLineIteratorType inLineIt(input, outputRegionForThread);
158
159 WorkUnitData workUnitData = this->CreateWorkUnitData( outputRegionForThread );
160 SizeValueType lineId = workUnitData.firstLine;
161
162 SizeValueType nbOfLabels = 0;
163 for ( inLineIt.GoToBegin();
164 !inLineIt.IsAtEnd();
165 inLineIt.NextLine() )
166 {
167 LineEncodingType thisLine;
168 while ( !inLineIt.IsAtEndOfLine() )
169 {
170 const InputPixelType pixelValue = inLineIt.Get();
171 if ( pixelValue == this->m_InputForegroundValue )
172 {
173 // We've hit the start of a run
174 SizeValueType length = 0;
175 IndexType thisIndex;
176 thisIndex = inLineIt.GetIndex();
177 ++length;
178 ++inLineIt;
179 while ( !inLineIt.IsAtEndOfLine()
180 && inLineIt.Get() == this->m_InputForegroundValue )
181 {
182 ++length;
183 ++inLineIt;
184 }
185 // create the run length object to go in the vector
186 RunLength thisRun( length, thisIndex, 0 ); // will give a real label later
187 thisLine.push_back(thisRun);
188 ++nbOfLabels;
189 }
190 else
191 {
192 ++inLineIt;
193 }
194 }
195 // equivalent to assignment because thisLine goes of out scope afterwards
196 this->m_LineMap[lineId].swap( thisLine );
197 ++lineId;
198 }
199
200 this->m_NumberOfLabels.fetch_add( nbOfLabels, std::memory_order_relaxed );
201 std::lock_guard<std::mutex> mutexHolder(this->m_Mutex);
202 this->m_WorkUnitResults.push_back( workUnitData );
203 }
204
205
206 template< typename TInputImage, typename TOutputImage >
207 void
208 BinaryImageToLabelMapFilter< TInputImage, TOutputImage >
PrintSelf(std::ostream & os,Indent indent) const209 ::PrintSelf(std::ostream & os, Indent indent) const
210 {
211 Superclass::PrintSelf(os, indent);
212
213 os << indent << "InputForegroundValue: "
214 << static_cast< typename NumericTraits< InputPixelType >::PrintType >( this->m_InputForegroundValue ) << std::endl;
215 os << indent << "OutputBackgroundValue: "
216 << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( this->m_OutputBackgroundValue )
217 << std::endl;
218 os << indent << "Number of Objects: " << this->m_NumberOfObjects << std::endl;
219 }
220 } // end namespace itk
221
222 #endif
223