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