ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
SaitoSquaredDistanceTransform.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 // Local
12 
13 // system
14 #include <algorithm>
15 #include <cstdint>
16 
17 using namespace cloudViewer;
18 
19 bool SaitoSquaredDistanceTransform::EDT_1D(GridElement* slice,
20  std::size_t r,
21  std::size_t c) {
22  GridElement* row = slice;
23 
24  for (std::size_t j = 0; j < r; j++, row += c) {
25  GridElement b = 1;
26  for (std::size_t i = 1; i < c; i++) {
27  GridElement limit = row[i - 1] + b;
28  if (row[i] > limit) {
29  row[i] = limit;
30  b += 2;
31  } else {
32  b = 1;
33  }
34  }
35 
36  b = 1;
37  for (std::size_t i = 1; i < c; i++) {
38  std::size_t colIndex = c - i;
39  GridElement limit = row[colIndex] + b;
40  if (row[colIndex - 1] > limit) {
41  row[colIndex - 1] = limit;
42  b += 2;
43  } else {
44  b = 1;
45  }
46  }
47  }
48 
49  return true;
50 }
51 
52 //:
53 // Assumes given a Lookup table of integer squares.
54 // Also assumes the image \a im already has infinity in all non-zero points.
56  std::size_t sliceIndex,
57  const std::vector<GridElement>& sq) {
58  const Tuple3ui& gridSize = grid.size();
59  std::size_t r = gridSize.y;
60  std::size_t c = gridSize.x;
61  std::size_t voxelCount = r * c;
62 
63  GridElement* sliceData = grid.data() + sliceIndex * voxelCount;
64 
65  // 1st step: vertical row-wise EDT
66  {
67  if (!EDT_1D(sliceData, r, c)) {
68  return false;
69  }
70  }
71 
72  // 2nd step: horizontal scan
73  {
74  std::vector<GridElement> colData;
75  try {
76  colData.resize(r);
77  } catch (const std::bad_alloc&) {
78  // not enough memory
79  return false;
80  }
81 
82  // for each column
83  for (std::size_t i = 0; i < c; ++i) {
84  // fill buffer with column values
85  {
86  GridElement* pt = sliceData + i;
87  for (std::size_t j = 0; j < r; ++j, pt += c) colData[j] = *pt;
88  }
89 
90  // forward scan
91  GridElement* pt = sliceData + i + c;
92  {
93  GridElement a = 0;
94  GridElement buffer = colData[0];
95 
96  for (std::size_t j = 1; j < r; ++j, pt += c) {
97  std::size_t rowIndex = j;
98  if (a != 0) --a;
99  if (colData[rowIndex] > buffer + 1) {
100  GridElement b = (colData[rowIndex] - buffer - 1) / 2;
101  if (rowIndex + b + 1 > r)
102  b = static_cast<GridElement>(r - 1 - rowIndex);
103 
104  GridElement* npt = pt + a * c;
105  for (GridElement l = a; l <= b; ++l) {
106  GridElement m = buffer + sq[l + 1];
107  if (colData[rowIndex + l] <= m) {
108  // proceed to next column
109  break;
110  }
111  if (m < *npt) *npt = m;
112  npt += c;
113  }
114  a = b;
115  } else {
116  a = 0;
117  }
118  buffer = colData[rowIndex];
119  }
120  }
121 
122  // backward scan
123  pt -= 2 * c;
124  {
125  GridElement a = 0;
126  GridElement buffer = colData[r - 1];
127 
128  for (std::size_t j = 1; j < r; ++j, pt -= c) {
129  std::size_t rowIndex = r - j - 1;
130  if (a != 0) --a;
131  if (colData[rowIndex] > buffer + 1) {
132  GridElement b = (colData[rowIndex] - buffer - 1) / 2;
133  if (rowIndex < b)
134  b = static_cast<GridElement>(rowIndex);
135 
136  GridElement* npt = pt - a * c;
137  for (GridElement l = a; l <= b; ++l) {
138  GridElement m = buffer + sq[l + 1];
139  if (colData[rowIndex - l] <= m) {
140  // proceed to next column
141  break;
142  }
143  if (m < *npt) *npt = m;
144  npt -= c;
145  }
146  a = b;
147  } else {
148  a = 0;
149  }
150  buffer = colData[rowIndex];
151  }
152  }
153  }
154  }
155 
156  return true;
157 }
158 
160  Grid3D<GridElement>& grid, GenericProgressCallback* progressCb /*=0*/) {
161  const Tuple3ui& gridSize = grid.size();
162  std::size_t r = gridSize.y;
163  std::size_t c = gridSize.x;
164  std::size_t p = gridSize.z;
165  std::size_t voxelCount = r * c * p;
166 
167  std::size_t diag = static_cast<std::size_t>(
168  ceil(sqrt(static_cast<double>(r * r + c * c + p * p))) - 1);
169  std::size_t nsqr = 2 * (diag + 1);
170 
171  std::vector<GridElement> sq;
172  try {
173  sq.resize(nsqr);
174  } catch (const std::bad_alloc&) {
175  // not enough memory
176  return false;
177  }
178 
179  for (std::size_t i = 0; i < nsqr; ++i) {
180  sq[i] = static_cast<GridElement>(i * i);
181  }
182 
183  const GridElement maxDistance =
185  static_cast<GridElement>(r * r + c * c + p * p) - 1;
186 
187  NormalizedProgress normProgress(progressCb, static_cast<unsigned>(p + r));
188  if (progressCb) {
189  if (progressCb->textCanBeEdited()) {
190  progressCb->setMethodTitle("Saito Distance Transform");
191  char buffer[256];
192  sprintf(buffer, "Box: [%u x %u x %u]", gridSize.x, gridSize.y,
193  gridSize.z);
194  progressCb->setInfo(buffer);
195  }
196  progressCb->update(0);
197  progressCb->start();
198  }
199 
200  GridElement* data = grid.data();
201  {
202  for (std::size_t i = 0; i < voxelCount; ++i) {
203  // DGM: warning we must invert the input image here!
204  if (data[i] == 0)
205  data[i] = maxDistance;
206  else
207  data[i] = 0;
208  }
209  }
210 
211  // 2D EDT for each slice
212  for (std::size_t k = 0; k < p; ++k) {
213  if (!SDT_2D(grid, k, sq)) {
214  return false;
215  }
216 
217  if (progressCb && !normProgress.oneStep()) {
218  // process cancelled by user
219  return false;
220  }
221  }
222 
223  // Now, for each pixel, compute final distance by searching along Z
224  // direction
225  std::size_t rc = r * c;
226  std::vector<GridElement> colData;
227  try {
228  colData.resize(p);
229  } catch (const std::bad_alloc&) {
230  // not enough memory
231  return false;
232  }
233 
234  for (std::size_t j = 0; j < r; ++j, data += c) {
235  for (std::size_t i = 0; i < c; ++i) {
236  GridElement* pt = data + i;
237 
238  for (std::size_t k = 0; k < p; ++k, pt += rc) colData[k] = *pt;
239 
240  pt = data + i + rc;
241  GridElement a = 0;
242  GridElement buffer = colData[0];
243 
244  for (std::size_t k = 1; k < p; ++k, pt += rc) {
245  if (a != 0) --a;
246  if (colData[k] > buffer + 1) {
247  GridElement b = (colData[k] - buffer - 1) / 2;
248  if (k + b + 1 > p) b = static_cast<GridElement>(p - 1 - k);
249 
250  GridElement* npt = pt + a * rc;
251  for (GridElement l = a; l <= b; ++l) {
252  GridElement m = buffer + sq[l + 1];
253  if (colData[k + l] <= m) break; // go to next plane k
254  if (m < *npt) *npt = m;
255  npt += rc;
256  }
257  a = b;
258  } else {
259  a = 0;
260  }
261  buffer = colData[k];
262  }
263 
264  a = 0;
265  pt -= 2 * rc;
266  buffer = colData[p - 1];
267 
268  for (std::size_t k = p - 2; k != static_cast<std::size_t>(-1);
269  --k, pt -= rc) {
270  if (a != 0) --a;
271  if (colData[k] > buffer + 1) {
272  GridElement b = (colData[k] - buffer - 1) / 2;
273  if (k < b) b = static_cast<GridElement>(k);
274 
275  GridElement* npt = pt - a * rc;
276  for (GridElement l = a; l <= b; ++l) {
277  GridElement m = buffer + sq[l + 1];
278  if (colData[k - l] <= m) break; // go to next column k
279  if (m < *npt) *npt = m;
280  npt -= rc;
281  }
282  a = b;
283  } else {
284  a = 0;
285  }
286  buffer = colData[k];
287  }
288  }
289 
290  if (progressCb && !normProgress.oneStep()) {
291  // process cancelled by user
292  return false;
293  }
294  }
295 
296  return true;
297 }
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
virtual void update(float percent)=0
Notifies the algorithm progress.
Simple 3D grid structure.
Definition: Grid3D.h:32
unsigned GridElement
Cell type.
Definition: Grid3D.h:35
GridElement * data()
Gives access to the internal grid data (with margin)
Definition: Grid3D.h:527
const Tuple3ui & size() const
Returns the grid dimensions.
Definition: Grid3D.h:48
bool oneStep()
Increments total progress value of a single unit.
static bool EDT_1D(GridElement *slice, std::size_t r, std::size_t c)
1D Euclidean Distance Transform
static bool SDT_3D(Grid3D< GridElement > &image, GenericProgressCallback *progressCb=nullptr)
3D Exact Squared Distance Transform
static bool SDT_2D(Grid3D< GridElement > &image, std::size_t sliceIndex, const std::vector< GridElement > &sq)
2D Exact Squared Distance Transform
int max(int a, int b)
Definition: cutil_math.h:48
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
Generic file read and write utility for python interface.