ITK 6.0.0
Insight Toolkit
 
Loading...
Searching...
No Matches
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;
83 using InputImagePointer = typename InputImageType::Pointer;
84 using InputImageConstPointer = typename InputImageType::ConstPointer;
85 using IndexType = typename TInputImage::IndexType;
86 using SizeType = typename TInputImage::SizeType;
87 using OffsetType = typename TInputImage::OffsetType;
88 using OutputImageType = TOutputImage;
89 using OutputImagePointer = typename OutputImageType::Pointer;
90 using OutputRegionType = typename TOutputImage::RegionType;
92 using OutputIndexType = typename TOutputImage::IndexType;
93 using OutputSizeType = typename TOutputImage::SizeType;
94 using OutputOffsetType = typename TOutputImage::OffsetType;
95 using OutputImagePixelType = typename TOutputImage::PixelType;
96
98
100
102 : m_EnclosingFilter(enclosingFilter)
103
104 {}
106
107protected:
109 using OutSizeType = typename TOutputImage::RegionType::SizeType;
110
112 {
114 typename InputImageType::IndexType where;
116
117 RunLength(SizeValueType iLength, const IndexType & iWhere, InternalLabelType iLabel = 0)
118 : length(iLength)
119 , where(iWhere)
120 , label(iLabel)
121 {}
122 };
123
124 using LineEncodingType = std::vector<RunLength>;
125 using LineEncodingIterator = typename LineEncodingType::iterator;
126 using LineEncodingConstIterator = typename LineEncodingType::const_iterator;
127
128 using OffsetVectorType = std::vector<OffsetValueType>;
129 using OffsetVectorConstIterator = typename OffsetVectorType::const_iterator;
130
131 using LineMapType = std::vector<LineEncodingType>;
132
133 using UnionFindType = std::vector<InternalLabelType>;
134 using ConsecutiveVectorType = std::vector<OutputPixelType>;
135
137 IndexToLinearIndex(const IndexType & index) const
138 {
139 SizeValueType linearIndex = 0;
140 SizeValueType stride = 1;
141 const RegionType requestedRegion = m_EnclosingFilter->GetOutput()->GetRequestedRegion();
142 // ignore x axis, which is always full size
143 for (unsigned int dim = 1; dim < ImageDimension; ++dim)
144 {
145 itkAssertOrThrowMacro(requestedRegion.GetIndex(dim) <= index[dim], "Index must be within the requested region!");
146 linearIndex += (index[dim] - requestedRegion.GetIndex(dim)) * stride;
147 stride *= requestedRegion.GetSize(dim);
148 }
149 return linearIndex;
150 }
151
152 void
154 {
155 m_UnionFind = UnionFindType(numberOfLabels + 1);
156
157 const auto MapBegin = m_LineMap.begin();
158 const auto MapEnd = m_LineMap.end();
159 InternalLabelType label = 1;
160 for (typename LineMapType::iterator LineIt = MapBegin; LineIt != MapEnd; ++LineIt)
161 {
162 for (auto cIt = LineIt->begin(); cIt != LineIt->end(); ++cIt)
163 {
164 cIt->label = label;
165 m_UnionFind[label] = label;
166 ++label;
167 }
168 }
169 }
170
173 {
174 InternalLabelType l = label;
175 while (l != m_UnionFind[l])
176 {
177 l = m_UnionFind[l]; // transitively sets equivalence
178 }
179 return l;
180 }
181
182 void
184 {
185 const std::lock_guard<std::mutex> lockGuard(m_Mutex);
186 const InternalLabelType E1 = this->LookupSet(label1);
187 const InternalLabelType E2 = this->LookupSet(label2);
188
189 if (E1 < E2)
190 {
191 m_UnionFind[E2] = E1;
192 }
193 else
194 {
195 m_UnionFind[E1] = E2;
196 }
197 }
198
201 {
202 const size_t N = m_UnionFind.size();
203
205 m_Consecutive[0] = backgroundValue;
206
207 OutputPixelType consecutiveLabel = 0;
208 SizeValueType count = 0;
209
210 for (size_t i = 1; i < N; ++i)
211 {
212 const auto label = static_cast<size_t>(m_UnionFind[i]);
213 if (label == i)
214 {
215 if (consecutiveLabel == backgroundValue)
216 {
217 ++consecutiveLabel;
218 }
219 m_Consecutive[label] = consecutiveLabel;
220 ++consecutiveLabel;
221 ++count;
222 }
223 }
224 return count;
225 }
226
227 bool
229 {
230 // This checks whether the line encodings are really neighbors. The first
231 // dimension gets ignored because the encodings are along that axis.
232 SizeValueType diffSum = 0;
233 for (unsigned int i = 1; i < OutputImageDimension; ++i)
234 {
235 const SizeValueType diff = itk::Math::abs(A[i] - B[i]);
236 if (diff > 1)
237 {
238 return false;
239 }
240 diffSum += diff;
241 }
242
243 if (!this->m_FullyConnected)
244 {
245 return (diffSum <= 1); // indices can differ only along one dimension
246 }
247 return true;
248 }
249
250 using CompareLinesCallback = std::function<void(const LineEncodingConstIterator & currentRun,
251 const LineEncodingConstIterator & neighborRun,
252 OffsetValueType oStart,
253 OffsetValueType oLast)>;
254
255 void
257 const LineEncodingType & Neighbour,
258 bool sameLineOffset,
259 bool labelCompare,
260 OutputPixelType background,
261 CompareLinesCallback callback)
262 {
263 bool sameLine = sameLineOffset;
264 if (sameLineOffset)
265 {
266 OutputOffsetType Off = current[0].where - Neighbour[0].where;
267
268 for (unsigned int i = 1; i < ImageDimension; ++i)
269 {
270 if (Off[i] != 0)
271 {
272 sameLine = false;
273 break;
274 }
275 }
276 }
277
278 OffsetValueType offset = 0;
279 if (m_FullyConnected || sameLine)
280 {
281 offset = 1;
282 }
283
284 auto mIt = Neighbour.begin(); // out marker iterator
285
286 for (auto cIt = current.begin(); cIt != current.end(); ++cIt)
287 {
288 if (!labelCompare || cIt->label != InternalLabelType(background))
289 {
290 const OffsetValueType cStart = cIt->where[0]; // the start x position
291 const OffsetValueType cLast = cStart + cIt->length - 1;
292
293 if (labelCompare)
294 {
295 mIt = Neighbour.begin();
296 }
297
298 for (auto nIt = mIt; nIt != Neighbour.end(); ++nIt)
299 {
300 if (!labelCompare || cIt->label != nIt->label)
301 {
302 const OffsetValueType nStart = nIt->where[0];
303 const OffsetValueType nLast = nStart + nIt->length - 1;
304
305 // there are a few ways that neighbouring lines might overlap
306 // neighbor S------------------E
307 // current S------------------------E
308 //-------------
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 const OffsetValueType ss1 = nStart - offset;
319 // OffsetValueType ss2 = nStart + offset;
320 const OffsetValueType ee1 = nLast - offset;
321 const OffsetValueType ee2 = nLast + offset;
322
323 bool eq = false;
324 OffsetValueType oStart = 0;
325 OffsetValueType oLast = 0;
326
327 // the logic here can probably be improved a lot
328 if ((ss1 >= cStart) && (ee2 <= cLast))
329 {
330 // case 1
331 eq = true;
332 oStart = ss1;
333 oLast = ee2;
334 }
335 else if ((ss1 <= cStart) && (ee2 >= cLast))
336 {
337 // case 4
338 eq = true;
339 oStart = cStart;
340 oLast = cLast;
341 }
342 else if ((ss1 <= cLast) && (ee2 >= cLast))
343 {
344 // case 2
345 eq = true;
346 oStart = ss1;
347 oLast = cLast;
348 }
349 else if ((ss1 <= cStart) && (ee2 >= cStart))
350 {
351 // case 3
352 eq = true;
353 oStart = cStart;
354 oLast = ee2;
355 }
356
357 if (eq)
358 {
359 callback(cIt, nIt, oStart, oLast);
360 if (sameLineOffset && oStart == cStart && oLast == cLast)
361 {
362 mIt = nIt;
363 break;
364 }
365 }
366
367 if (!sameLineOffset && ee1 >= cLast)
368 {
369 // No point looking for more overlaps with the current run
370 // because the neighbor run is either case 2 or 4
371 mIt = nIt;
372 break;
373 }
374 }
375 }
376 }
377 }
378 }
379
380 void
381 SetupLineOffsets(bool wholeNeighborhood)
382 {
383 // Create a neighborhood so that we can generate a table of offsets
384 // to "previous" line indexes
385 // We are going to misuse the neighborhood iterators to compute the
386 // offset for us. All this messing around produces an array of
387 // offsets that will be used to index the map
388 const typename TOutputImage::Pointer output = m_EnclosingFilter->GetOutput();
389 using PretendImageType = Image<OffsetValueType, TOutputImage::ImageDimension - 1>;
390 using PretendSizeType = typename PretendImageType::RegionType::SizeType;
391 using PretendIndexType = typename PretendImageType::RegionType::IndexType;
392 using LineNeighborhoodType = ConstShapedNeighborhoodIterator<PretendImageType>;
393
394 auto fakeImage = PretendImageType::New();
395
396 typename PretendImageType::RegionType LineRegion;
397
398 OutSizeType OutSize = output->GetRequestedRegion().GetSize();
399
400 PretendSizeType PretendSize;
401 // The first dimension has been collapsed
402 for (SizeValueType i = 0; i < PretendSize.GetSizeDimension(); ++i)
403 {
404 PretendSize[i] = OutSize[i + 1];
405 }
406
407 LineRegion.SetSize(PretendSize);
408 fakeImage->SetRegions(LineRegion);
409 auto kernelRadius = PretendSizeType::Filled(1);
410 LineNeighborhoodType lnit(kernelRadius, fakeImage, LineRegion);
411
412 if (wholeNeighborhood)
413 {
415 }
416 else
417 {
419 }
420
421 typename LineNeighborhoodType::IndexListType ActiveIndexes = lnit.GetActiveIndexList();
422
423 const PretendIndexType idx = LineRegion.GetIndex();
424 const OffsetValueType offset = fakeImage->ComputeOffset(idx);
425
426 for (auto LI = ActiveIndexes.begin(); LI != ActiveIndexes.end(); ++LI)
427 {
428 m_LineOffsets.push_back(fakeImage->ComputeOffset(idx + lnit.GetOffset(*LI)) - offset);
429 }
430
431 if (wholeNeighborhood)
432 {
433 m_LineOffsets.push_back(0); // center pixel
434 }
435 }
436
438
444
446 CreateWorkUnitData(const RegionType & outputRegionForThread)
447 {
448 const SizeValueType xsizeForThread = outputRegionForThread.GetSize()[0];
449 const SizeValueType numberOfLines = outputRegionForThread.GetNumberOfPixels() / xsizeForThread;
450
451 const SizeValueType firstLine = this->IndexToLinearIndex(outputRegionForThread.GetIndex());
452 const SizeValueType lastLine = firstLine + numberOfLines - 1;
453
454 return WorkUnitData{ firstLine, lastLine };
455 }
456
457 /* Process the map and make appropriate entries in an equivalence table */
458 void
459 ComputeEquivalence(const SizeValueType workUnitResultsIndex, bool strictlyLess)
460 {
461 const OffsetValueType linecount = m_LineMap.size();
462 const WorkUnitData wud = m_WorkUnitResults[workUnitResultsIndex];
463 SizeValueType lastLine = wud.lastLine;
464 if (!strictlyLess)
465 {
466 ++lastLine;
467 // make sure we are not wrapping around
468 itkAssertInDebugAndIgnoreInReleaseMacro(lastLine >= wud.lastLine);
469 }
470 for (SizeValueType thisIdx = wud.firstLine; thisIdx < lastLine; ++thisIdx)
471 {
472 if (!m_LineMap[thisIdx].empty())
473 {
474 auto it = this->m_LineOffsets.begin();
475 while (it != this->m_LineOffsets.end())
476 {
477 const OffsetValueType neighIdx = thisIdx + (*it);
478 // check if the neighbor is in the map
479 if (neighIdx >= 0 && neighIdx < linecount && !m_LineMap[neighIdx].empty())
480 {
481 // Now check whether they are really neighbors
482 const bool areNeighbors = this->CheckNeighbors(m_LineMap[thisIdx][0].where, m_LineMap[neighIdx][0].where);
483 if (areNeighbors)
484 {
485 this->CompareLines(m_LineMap[thisIdx],
486 m_LineMap[neighIdx],
487 false,
488 false,
489 0,
490 [this](const LineEncodingConstIterator & currentRun,
491 const LineEncodingConstIterator & neighborRun,
493 OffsetValueType) { this->LinkLabels(neighborRun->label, currentRun->label); });
494 }
495 }
496 ++it;
497 }
498 }
499 }
500 }
501
502protected:
503 bool m_FullyConnected{ false };
507 std::mutex m_Mutex;
508
509 std::atomic<SizeValueType> m_NumberOfLabels;
510 std::deque<WorkUnitData> m_WorkUnitResults;
512};
513} // end namespace itk
514
515#endif
Const version of ShapedNeighborhoodIterator, defining iteration of a local N-dimensional neighborhood...
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
typename TInputImage::IndexType IndexType
typename OffsetVectorType::const_iterator OffsetVectorConstIterator
typename TOutputImage::SizeType OutputSizeType
typename TOutputImage::RegionType::SizeType OutSizeType
ImageToImageFilter< TInputImage, TOutputImage > EnclosingFilter
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)
std::function< void(const LineEncodingConstIterator &currentRun, const LineEncodingConstIterator &neighborRun, OffsetValueType oStart, OffsetValueType oLast)> CompareLinesCallback
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
typename TOutputImage::IndexType OutputIndexType
std::atomic< SizeValueType > m_NumberOfLabels
typename TInputImage::PixelType InputPixelType
SmartPointer< const Self > ConstPointer
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 transparent reference counting.
Implements a weak reference to an object.
SmartPointer< Self > Pointer
#define itkConceptMacro(name, concept)
bool abs(bool x)
Definition itkMath.h:837
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
long OffsetValueType
Definition itkIntTypes.h:97
TIterator * setConnectivityPrevious(TIterator *it, bool fullyConnected=false)
RunLength(SizeValueType iLength, const IndexType &iWhere, InternalLabelType iLabel=0)