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