ITK  6.0.0
Insight Toolkit
itkScanlineFilterCommon.h
Go to the documentation of this file.
1/*=========================================================================
2 *
3 * Copyright NumFOCUS
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 * https://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 itkScanlineFilterCommon_h
19#define itkScanlineFilterCommon_h
20
23#include <atomic>
24#include <deque>
25#include <functional>
26#include <mutex>
27#include <vector>
28
29namespace itk
30{
40template <typename TInputImage, typename TOutputImage>
42{
43public:
44 ITK_DISALLOW_COPY_AND_MOVE(ScanlineFilterCommon);
45
49 void
50 Register() const
51 {
52 auto * obj = static_cast<Object *>(m_EnclosingFilter.GetPointer());
53 obj->Register();
54 }
55 void
56 UnRegister() const noexcept
57 {
58 auto * obj = static_cast<Object *>(m_EnclosingFilter.GetPointer());
59 obj->UnRegister();
60 }
61 static Pointer
63 {
65 if (smartPtr == nullptr)
66 {
67 smartPtr = new Self(nullptr);
68 }
69 smartPtr->UnRegister();
70 return smartPtr;
71 }
72
77 using OutputPixelType = typename TOutputImage::PixelType;
78 using InputPixelType = typename TInputImage::PixelType;
79 static constexpr unsigned int ImageDimension = TOutputImage::ImageDimension;
80 static constexpr unsigned int OutputImageDimension = TOutputImage::ImageDimension;
81 static constexpr unsigned int InputImageDimension = TInputImage::ImageDimension;
82 using InputImageType = TInputImage;
87 using OffsetType = typename TInputImage::OffsetType;
88 using OutputImageType = TOutputImage;
94 using OutputOffsetType = typename TOutputImage::OffsetType;
95 using OutputImagePixelType = typename TOutputImage::PixelType;
96
97#ifdef ITK_USE_CONCEPT_CHECKING
98 // Concept checking -- input and output dimensions must be the same
100#endif
101
103
105 : m_EnclosingFilter(enclosingFilter)
106
107 {}
109
110protected:
113
115 {
119
120 RunLength(SizeValueType iLength, const IndexType & iWhere, InternalLabelType iLabel = 0)
121 : length(iLength)
122 , where(iWhere)
123 , label(iLabel)
124 {}
125 };
126
127 using LineEncodingType = std::vector<RunLength>;
128 using LineEncodingIterator = typename LineEncodingType::iterator;
129 using LineEncodingConstIterator = typename LineEncodingType::const_iterator;
130
131 using OffsetVectorType = std::vector<OffsetValueType>;
132 using OffsetVectorConstIterator = typename OffsetVectorType::const_iterator;
133
134 using LineMapType = std::vector<LineEncodingType>;
135
136 using UnionFindType = std::vector<InternalLabelType>;
137 using ConsecutiveVectorType = std::vector<OutputPixelType>;
138
140 IndexToLinearIndex(const IndexType & index) const
141 {
142 SizeValueType linearIndex = 0;
143 SizeValueType stride = 1;
144 const RegionType requestedRegion = m_EnclosingFilter->GetOutput()->GetRequestedRegion();
145 // ignore x axis, which is always full size
146 for (unsigned int dim = 1; dim < ImageDimension; ++dim)
147 {
148 itkAssertOrThrowMacro(requestedRegion.GetIndex(dim) <= index[dim], "Index must be within the requested region!");
149 linearIndex += (index[dim] - requestedRegion.GetIndex(dim)) * stride;
150 stride *= requestedRegion.GetSize(dim);
151 }
152 return linearIndex;
153 }
154
155 void
157 {
158 m_UnionFind = UnionFindType(numberOfLabels + 1);
159
160 const auto MapBegin = m_LineMap.begin();
161 const auto MapEnd = m_LineMap.end();
162 InternalLabelType label = 1;
163 for (typename LineMapType::iterator LineIt = MapBegin; LineIt != MapEnd; ++LineIt)
164 {
165 for (auto cIt = LineIt->begin(); cIt != LineIt->end(); ++cIt)
166 {
167 cIt->label = label;
168 m_UnionFind[label] = label;
169 ++label;
170 }
171 }
172 }
173
176 {
177 InternalLabelType l = label;
178 while (l != m_UnionFind[l])
179 {
180 l = m_UnionFind[l]; // transitively sets equivalence
181 }
182 return l;
183 }
184
185 void
187 {
188 const std::lock_guard<std::mutex> lockGuard(m_Mutex);
189 const InternalLabelType E1 = this->LookupSet(label1);
190 const InternalLabelType E2 = this->LookupSet(label2);
191
192 if (E1 < E2)
193 {
194 m_UnionFind[E2] = E1;
195 }
196 else
197 {
198 m_UnionFind[E1] = E2;
199 }
200 }
201
204 {
205 const size_t N = m_UnionFind.size();
206
208 m_Consecutive[0] = backgroundValue;
209
210 OutputPixelType consecutiveLabel = 0;
211 SizeValueType count = 0;
212
213 for (size_t i = 1; i < N; ++i)
214 {
215 const auto label = static_cast<size_t>(m_UnionFind[i]);
216 if (label == i)
217 {
218 if (consecutiveLabel == backgroundValue)
219 {
220 ++consecutiveLabel;
221 }
222 m_Consecutive[label] = consecutiveLabel;
223 ++consecutiveLabel;
224 ++count;
225 }
226 }
227 return count;
228 }
229
230 bool
232 {
233 // This checks whether the line encodings are really neighbors. The first
234 // dimension gets ignored because the encodings are along that axis.
235 SizeValueType diffSum = 0;
236 for (unsigned int i = 1; i < OutputImageDimension; ++i)
237 {
238 const SizeValueType diff = itk::Math::abs(A[i] - B[i]);
239 if (diff > 1)
240 {
241 return false;
242 }
243 diffSum += diff;
244 }
245
246 if (!this->m_FullyConnected)
247 {
248 return (diffSum <= 1); // indices can differ only along one dimension
249 }
250 return true;
251 }
252
253 using CompareLinesCallback = std::function<void(const LineEncodingConstIterator & currentRun,
254 const LineEncodingConstIterator & neighborRun,
255 OffsetValueType oStart,
256 OffsetValueType oLast)>;
257
258 void
260 const LineEncodingType & Neighbour,
261 bool sameLineOffset,
262 bool labelCompare,
263 OutputPixelType background,
264 CompareLinesCallback callback)
265 {
266 bool sameLine = sameLineOffset;
267 if (sameLineOffset)
268 {
269 OutputOffsetType Off = current[0].where - Neighbour[0].where;
270
271 for (unsigned int i = 1; i < ImageDimension; ++i)
272 {
273 if (Off[i] != 0)
274 {
275 sameLine = false;
276 break;
277 }
278 }
279 }
280
281 OffsetValueType offset = 0;
282 if (m_FullyConnected || sameLine)
283 {
284 offset = 1;
285 }
286
287 auto mIt = Neighbour.begin(); // out marker iterator
288
289 for (auto cIt = current.begin(); cIt != current.end(); ++cIt)
290 {
291 if (!labelCompare || cIt->label != InternalLabelType(background))
292 {
293 const OffsetValueType cStart = cIt->where[0]; // the start x position
294 const OffsetValueType cLast = cStart + cIt->length - 1;
295
296 if (labelCompare)
297 {
298 mIt = Neighbour.begin();
299 }
300
301 for (auto nIt = mIt; nIt != Neighbour.end(); ++nIt)
302 {
303 if (!labelCompare || cIt->label != nIt->label)
304 {
305 const OffsetValueType nStart = nIt->where[0];
306 const OffsetValueType nLast = nStart + nIt->length - 1;
307
308 // there are a few ways that neighbouring lines might overlap
309 // neighbor S------------------E
310 // current S------------------------E
311 //-------------
312 // neighbor S------------------E
313 // current S----------------E
314 //-------------
315 // neighbor S------------------E
316 // current S------------------E
317 //-------------
318 // neighbor S------------------E
319 // current S-------E
320 //-------------
321 const OffsetValueType ss1 = nStart - offset;
322 // OffsetValueType ss2 = nStart + offset;
323 const OffsetValueType ee1 = nLast - offset;
324 const OffsetValueType ee2 = nLast + offset;
325
326 bool eq = false;
327 OffsetValueType oStart = 0;
328 OffsetValueType oLast = 0;
329
330 // the logic here can probably be improved a lot
331 if ((ss1 >= cStart) && (ee2 <= cLast))
332 {
333 // case 1
334 eq = true;
335 oStart = ss1;
336 oLast = ee2;
337 }
338 else if ((ss1 <= cStart) && (ee2 >= cLast))
339 {
340 // case 4
341 eq = true;
342 oStart = cStart;
343 oLast = cLast;
344 }
345 else if ((ss1 <= cLast) && (ee2 >= cLast))
346 {
347 // case 2
348 eq = true;
349 oStart = ss1;
350 oLast = cLast;
351 }
352 else if ((ss1 <= cStart) && (ee2 >= cStart))
353 {
354 // case 3
355 eq = true;
356 oStart = cStart;
357 oLast = ee2;
358 }
359
360 if (eq)
361 {
362 callback(cIt, nIt, oStart, oLast);
363 if (sameLineOffset && oStart == cStart && oLast == cLast)
364 {
365 mIt = nIt;
366 break;
367 }
368 }
369
370 if (!sameLineOffset && ee1 >= cLast)
371 {
372 // No point looking for more overlaps with the current run
373 // because the neighbor run is either case 2 or 4
374 mIt = nIt;
375 break;
376 }
377 }
378 }
379 }
380 }
381 }
382
383 void
384 SetupLineOffsets(bool wholeNeighborhood)
385 {
386 // Create a neighborhood so that we can generate a table of offsets
387 // to "previous" line indexes
388 // We are going to misuse the neighborhood iterators to compute the
389 // offset for us. All this messing around produces an array of
390 // offsets that will be used to index the map
391 const typename TOutputImage::Pointer output = m_EnclosingFilter->GetOutput();
392 using PretendImageType = Image<OffsetValueType, TOutputImage::ImageDimension - 1>;
393 using PretendSizeType = typename PretendImageType::RegionType::SizeType;
394 using PretendIndexType = typename PretendImageType::RegionType::IndexType;
395 using LineNeighborhoodType = ConstShapedNeighborhoodIterator<PretendImageType>;
396
397 auto fakeImage = PretendImageType::New();
398
399 typename PretendImageType::RegionType LineRegion;
400
401 OutSizeType OutSize = output->GetRequestedRegion().GetSize();
402
403 PretendSizeType PretendSize;
404 // The first dimension has been collapsed
405 for (SizeValueType i = 0; i < PretendSize.GetSizeDimension(); ++i)
406 {
407 PretendSize[i] = OutSize[i + 1];
408 }
409
410 LineRegion.SetSize(PretendSize);
411 fakeImage->SetRegions(LineRegion);
412 auto kernelRadius = PretendSizeType::Filled(1);
413 LineNeighborhoodType lnit(kernelRadius, fakeImage, LineRegion);
414
415 if (wholeNeighborhood)
416 {
418 }
419 else
420 {
422 }
423
424 typename LineNeighborhoodType::IndexListType ActiveIndexes = lnit.GetActiveIndexList();
425
426 const PretendIndexType idx = LineRegion.GetIndex();
427 const OffsetValueType offset = fakeImage->ComputeOffset(idx);
428
429 for (auto LI = ActiveIndexes.begin(); LI != ActiveIndexes.end(); ++LI)
430 {
431 m_LineOffsets.push_back(fakeImage->ComputeOffset(idx + lnit.GetOffset(*LI)) - offset);
432 }
433
434 if (wholeNeighborhood)
435 {
436 m_LineOffsets.push_back(0); // center pixel
437 }
438 }
439
441
443 {
446 };
447
449 CreateWorkUnitData(const RegionType & outputRegionForThread)
450 {
451 const SizeValueType xsizeForThread = outputRegionForThread.GetSize()[0];
452 const SizeValueType numberOfLines = outputRegionForThread.GetNumberOfPixels() / xsizeForThread;
453
454 const SizeValueType firstLine = this->IndexToLinearIndex(outputRegionForThread.GetIndex());
455 const SizeValueType lastLine = firstLine + numberOfLines - 1;
456
457 return WorkUnitData{ firstLine, lastLine };
458 }
459
460 /* Process the map and make appropriate entries in an equivalence table */
461 void
462 ComputeEquivalence(const SizeValueType workUnitResultsIndex, bool strictlyLess)
463 {
464 const OffsetValueType linecount = m_LineMap.size();
465 const WorkUnitData wud = m_WorkUnitResults[workUnitResultsIndex];
466 SizeValueType lastLine = wud.lastLine;
467 if (!strictlyLess)
468 {
469 ++lastLine;
470 // make sure we are not wrapping around
471 itkAssertInDebugAndIgnoreInReleaseMacro(lastLine >= wud.lastLine);
472 }
473 for (SizeValueType thisIdx = wud.firstLine; thisIdx < lastLine; ++thisIdx)
474 {
475 if (!m_LineMap[thisIdx].empty())
476 {
477 auto it = this->m_LineOffsets.begin();
478 while (it != this->m_LineOffsets.end())
479 {
480 const OffsetValueType neighIdx = thisIdx + (*it);
481 // check if the neighbor is in the map
482 if (neighIdx >= 0 && neighIdx < linecount && !m_LineMap[neighIdx].empty())
483 {
484 // Now check whether they are really neighbors
485 const bool areNeighbors = this->CheckNeighbors(m_LineMap[thisIdx][0].where, m_LineMap[neighIdx][0].where);
486 if (areNeighbors)
487 {
488 this->CompareLines(m_LineMap[thisIdx],
489 m_LineMap[neighIdx],
490 false,
491 false,
492 0,
493 [this](const LineEncodingConstIterator & currentRun,
494 const LineEncodingConstIterator & neighborRun,
496 OffsetValueType) { this->LinkLabels(neighborRun->label, currentRun->label); });
497 }
498 }
499 ++it;
500 }
501 }
502 }
503 }
504
505protected:
506 bool m_FullyConnected{ false };
510 std::mutex m_Mutex;
511
512 std::atomic<SizeValueType> m_NumberOfLabels;
513 std::deque<WorkUnitData> m_WorkUnitResults;
515};
516} // end namespace itk
517
518#endif
Const version of ShapedNeighborhoodIterator, defining iteration of a local N-dimensional neighborhood...
const SizeType & GetSize() const
Base class for filters that take an image as input and produce an image as output.
Templated n-dimensional image class.
Definition: itkImage.h:89
static T::Pointer Create()
Base class for most ITK classes.
Definition: itkObject.h:62
void UnRegister() const noexcept override
void Register() const override
Helper class for a group of filters which operate on scan-lines.
typename TInputImage::IndexType IndexType
typename OffsetVectorType::const_iterator OffsetVectorConstIterator
typename TOutputImage::SizeType OutputSizeType
typename TOutputImage::RegionType::SizeType OutSizeType
typename LineEncodingType::const_iterator LineEncodingConstIterator
InternalLabelType LookupSet(const InternalLabelType label)
void InitUnion(InternalLabelType numberOfLabels)
ConsecutiveVectorType m_Consecutive
std::deque< WorkUnitData > m_WorkUnitResults
typename InputImageType::Pointer InputImagePointer
SizeValueType IndexToLinearIndex(const IndexType &index) const
typename TInputImage::OffsetType OffsetType
void ComputeEquivalence(const SizeValueType workUnitResultsIndex, bool strictlyLess)
void SetupLineOffsets(bool wholeNeighborhood)
bool CheckNeighbors(const OutputIndexType &A, const OutputIndexType &B) const
static constexpr unsigned int InputImageDimension
typename TOutputImage::PixelType OutputImagePixelType
typename LineEncodingType::iterator LineEncodingIterator
WorkUnitData CreateWorkUnitData(const RegionType &outputRegionForThread)
static constexpr unsigned int ImageDimension
void LinkLabels(const InternalLabelType label1, const InternalLabelType label2)
std::vector< LineEncodingType > LineMapType
SizeValueType CreateConsecutive(OutputPixelType backgroundValue)
typename TOutputImage::OffsetType OutputOffsetType
typename InputImageType::ConstPointer InputImageConstPointer
std::vector< RunLength > LineEncodingType
typename TInputImage::SizeType SizeType
std::vector< OffsetValueType > OffsetVectorType
std::vector< InternalLabelType > UnionFindType
std::vector< OutputPixelType > ConsecutiveVectorType
typename TOutputImage::RegionType OutputRegionType
void UnRegister() const noexcept
std::function< void(const LineEncodingConstIterator &currentRun, const LineEncodingConstIterator &neighborRun, OffsetValueType oStart, OffsetValueType oLast)> CompareLinesCallback
typename TOutputImage::IndexType OutputIndexType
std::atomic< SizeValueType > m_NumberOfLabels
typename TInputImage::PixelType InputPixelType
void CompareLines(const LineEncodingType &current, const LineEncodingType &Neighbour, bool sameLineOffset, bool labelCompare, OutputPixelType background, CompareLinesCallback callback)
WeakPointer< EnclosingFilter > m_EnclosingFilter
static constexpr unsigned int OutputImageDimension
typename OutputImageType::Pointer OutputImagePointer
ScanlineFilterCommon(EnclosingFilter *enclosingFilter)
typename TOutputImage::PixelType OutputPixelType
Implements a weak reference to an object.
SmartPointer< const Self > ConstPointer
static Pointer New()
SmartPointer< Self > Pointer
#define itkConceptMacro(name, concept)
bool abs(bool x)
Definition: itkMath.h:839
The "itk" namespace contains all Insight Segmentation and Registration Toolkit (ITK) classes....
TIterator * setConnectivity(TIterator *it, bool fullyConnected=false)
unsigned long SizeValueType
Definition: itkIntTypes.h:86
TIterator * setConnectivityPrevious(TIterator *it, bool fullyConnected=false)
long OffsetValueType
Definition: itkIntTypes.h:97
RunLength(SizeValueType iLength, const IndexType &iWhere, InternalLabelType iLabel=0)
void SetSize(const SizeValueType val[VDimension])
Definition: itkSize.h:180