GteImageUtility2.cpp
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #include <GTEnginePCH.h>
10 #include <cmath>
11 #include <limits>
12 using namespace gte;
13 
15  std::vector<std::vector<size_t>>& components)
16 {
17  std::array<int, 4> neighbors;
18  image.GetNeighborhood(neighbors);
19  GetComponents(4, &neighbors[0], image, components);
20 }
21 
23  std::vector<std::vector<size_t>>& components)
24 {
25  std::array<int, 8> neighbors;
26  image.GetNeighborhood(neighbors);
27  GetComponents(8, &neighbors[0], image, components);
28 }
29 
31 {
32  std::array<std::array<int, 2>, 4> neighbors;
33  input.GetNeighborhood(neighbors);
34  Dilate(input, 4, &neighbors[0], output);
35 }
36 
38 {
39  std::array<std::array<int, 2>, 8> neighbors;
40  input.GetNeighborhood(neighbors);
41  Dilate(input, 8, &neighbors[0], output);
42 }
43 
44 void ImageUtility2::Dilate(Image2<int> const& input, int numNeighbors,
45  std::array<int, 2> const* neighbors, Image2<int>& output)
46 {
47  // If the assertion is triggered, the function will run but the output
48  // will not be correct.
49  LogAssert(&output != &input, "Input and output must be different.");
50 
51  output = input;
52 
53  // If the pixel at (x,y) is 1, then the pixels at (x+dx,y+dy) are set to 1
54  // where (dx,dy) is in the 'neighbors' array. Boundary testing is used to
55  // avoid accessing out-of-range pixels.
56  int const dim0 = input.GetDimension(0);
57  int const dim1 = input.GetDimension(1);
58  for (int y = 0; y < dim1; ++y)
59  {
60  for (int x = 0; x < dim0; ++x)
61  {
62  if (input(x, y) == 1)
63  {
64  for (int j = 0; j < numNeighbors; ++j)
65  {
66  int xNbr = x + neighbors[j][0];
67  int yNbr = y + neighbors[j][1];
68  if (0 <= xNbr && xNbr < dim0 && 0 <= yNbr && yNbr < dim1)
69  {
70  output(xNbr, yNbr) = 1;
71  }
72  }
73  }
74  }
75  }
76 }
77 
78 void ImageUtility2::Erode4(Image2<int> const& input, bool zeroExterior,
79  Image2<int>& output)
80 {
81  std::array<std::array<int, 2>, 4> neighbors;
82  input.GetNeighborhood(neighbors);
83  Erode(input, zeroExterior, 4, &neighbors[0], output);
84 }
85 
86 void ImageUtility2::Erode8(Image2<int> const& input, bool zeroExterior,
87  Image2<int>& output)
88 {
89  std::array<std::array<int, 2>, 8> neighbors;
90  input.GetNeighborhood(neighbors);
91  Erode(input, zeroExterior, 8, &neighbors[0], output);
92 }
93 
94 void ImageUtility2::Erode(Image2<int> const& input, bool zeroExterior,
95  int numNeighbors, std::array<int, 2> const* neighbors,
96  Image2<int>& output)
97 {
98  // If the assertion is triggered, the function will run but the output
99  // will not be correct.
100  LogAssert(&output != &input, "Input and output must be different.");
101 
102  output = input;
103 
104  // If the pixel at (x,y) is 1, it is changed to 0 when at least one
105  // neighbor (x+dx,y+dy) is 0, where (dx,dy) is in the 'neighbors'
106  // array.
107  int const dim0 = input.GetDimension(0);
108  int const dim1 = input.GetDimension(1);
109  for (int y = 0; y < dim1; ++y)
110  {
111  for (int x = 0; x < dim0; ++x)
112  {
113  if (input(x, y) == 1)
114  {
115  for (int j = 0; j < numNeighbors; ++j)
116  {
117  int xNbr = x + neighbors[j][0];
118  int yNbr = y + neighbors[j][1];
119  if (0 <= xNbr && xNbr < dim0 && 0 <= yNbr && yNbr < dim1)
120  {
121  if (input(xNbr, yNbr) == 0)
122  {
123  output(x, y) = 0;
124  break;
125  }
126  }
127  else if (zeroExterior)
128  {
129  output(x, y) = 0;
130  break;
131  }
132  }
133  }
134  }
135  }
136 }
137 
138 void ImageUtility2::Open4(Image2<int> const& input, bool zeroExterior,
139  Image2<int>& output)
140 {
141  Image2<int> temp(input.GetDimension(0), input.GetDimension(1));
142  Erode4(input, zeroExterior, temp);
143  Dilate4(temp, output);
144 }
145 
146 void ImageUtility2::Open8(Image2<int> const& input, bool zeroExterior,
147  Image2<int>& output)
148 {
149  Image2<int> temp(input.GetDimension(0), input.GetDimension(1));
150  Erode8(input, zeroExterior, temp);
151  Dilate8(temp, output);
152 }
153 
154 void ImageUtility2::Open(Image2<int> const& input, bool zeroExterior,
155  int numNeighbors, std::array<int, 2> const* neighbors,
156  Image2<int>& output)
157 {
158  Image2<int> temp(input.GetDimension(0), input.GetDimension(1));
159  Erode(input, zeroExterior, numNeighbors, neighbors, temp);
160  Dilate(temp, numNeighbors, neighbors, output);
161 }
162 
163 void ImageUtility2::Close4(Image2<int> const& input, bool zeroExterior,
164  Image2<int>& output)
165 {
166  Image2<int> temp(input.GetDimension(0), input.GetDimension(1));
167  Dilate4(input, temp);
168  Erode4(temp, zeroExterior, output);
169 }
170 
171 void ImageUtility2::Close8(Image2<int> const& input, bool zeroExterior,
172  Image2<int>& output)
173 {
174  Image2<int> temp(input.GetDimension(0), input.GetDimension(1));
175  Dilate8(input, temp);
176  Erode8(temp, zeroExterior, output);
177 }
178 
179 void ImageUtility2::Close(Image2<int> const& input, bool zeroExterior,
180  int numNeighbors, std::array<int, 2> const* neighbors,
181  Image2<int>& output)
182 {
183  Image2<int> temp(input.GetDimension(0), input.GetDimension(1));
184  Dilate(input, numNeighbors, neighbors, temp);
185  Erode(temp, zeroExterior, numNeighbors, neighbors, output);
186 }
187 
189  std::vector<size_t>& boundary)
190 {
191  // Find a first boundary pixel.
192  size_t const numPixels = image.GetNumPixels();
193  size_t i;
194  for (i = image.GetIndex(x, y); i < numPixels; ++i)
195  {
196  if (image[i])
197  {
198  break;
199  }
200  }
201  if (i == numPixels)
202  {
203  // No boundary pixel found.
204  return false;
205  }
206 
207  int const dx[8] = { -1, 0, +1, +1, +1, 0, -1, -1 };
208  int const dy[8] = { -1, -1, -1, 0, +1, +1, +1, 0 };
209 
210  // Create a new point list that contains the first boundary point.
211  boundary.push_back(i);
212 
213  // The direction from background 0 to boundary pixel 1 is (dx[7],dy[7]).
214  std::array<int,2> coord = image.GetCoordinates(i);
215  int x0 = coord[0], y0 = coord[1];
216  int cx = x0, cy = y0;
217  int nx = x0-1, ny = y0, dir = 7;
218 
219  // Traverse the boundary in clockwise order. Mark visited pixels as 2.
220  image(cx, cy) = 2;
221  bool notDone = true;
222  while (notDone)
223  {
224  int j, nbr;
225  for (j = 0, nbr = dir; j < 8; ++j, nbr = (nbr + 1) % 8)
226  {
227  nx = cx + dx[nbr];
228  ny = cy + dy[nbr];
229  if (image(nx, ny)) // next boundary pixel found
230  {
231  break;
232  }
233  }
234 
235  if (j == 8) // (cx,cy) is isolated
236  {
237  notDone = false;
238  continue;
239  }
240 
241  if (nx == x0 && ny == y0) // boundary traversal completed
242  {
243  notDone = false;
244  continue;
245  }
246 
247  // (nx,ny) is next boundary point, add point to list. Note that
248  // the index for the pixel is computed for the original image, not
249  // for the larger temporary image.
250  boundary.push_back(image.GetIndex(nx, ny));
251 
252  // Mark visited pixels as 2.
253  image(nx, ny) = 2;
254 
255  // Start search for next point.
256  cx = nx;
257  cy = ny;
258  dir = (j + 5 + dir) % 8;
259  }
260 
261  return true;
262 }
263 
265  int& xMax, int& yMax)
266 {
267  int const dim0 = image.GetDimension(0);
268  int const dim1 = image.GetDimension(1);
269  int const dim0m1 = dim0 - 1;
270  int const dim1m1 = dim1 - 1;
271 
272  // Use a grass-fire approach, computing distance from boundary to
273  // interior one pass at a time.
274  bool changeMade = true;
275  int distance;
276  for (distance = 1, xMax = 0, yMax = 0; changeMade; ++distance)
277  {
278  changeMade = false;
279  int distanceP1 = distance + 1;
280  for (int y = 1; y < dim1m1; ++y)
281  {
282  for (int x = 1; x < dim0m1; ++x)
283  {
284  if (image(x, y) == distance)
285  {
286  if (image(x-1, y) >= distance
287  && image(x+1, y) >= distance
288  && image(x, y-1) >= distance
289  && image(x, y+1) >= distance)
290  {
291  image(x, y) = distanceP1;
292  xMax = x;
293  yMax = y;
294  changeMade = true;
295  }
296  }
297  }
298  }
299  }
300 
301  maxDistance = --distance;
302 }
303 
305  float& maxDistance, int& xMax, int& yMax, Image2<float>& transform)
306 {
307  // This program calculates the Euclidean distance transform of a binary
308  // input image. The adaptive algorithm is guaranteed to give exact
309  // distances for all distances < 100. Algorithm sent to me by John Gauch.
310  //
311  // From John Gauch at University of Kansas:
312  // The basic idea is similar to a EDT described recently in PAMI by
313  // Laymarie from McGill. By keeping the dx and dy offset to the nearest
314  // edge (feature) point in the image, we can search to see which dx dy is
315  // closest to a given point by examining a set of neighbors. The Laymarie
316  // method (and Borgfors) look at a fixed 3x3 or 5x5 neighborhood and call
317  // it a day. What we did was calculate (painfully) what neighborhoods you
318  // need to look at to guarentee that the exact distance is obtained. Thus,
319  // you will see in the code, that we L2Check the current distance and
320  // depending on what we have so far, we extend the search region. Since
321  // our algorithm for L2Checking the exactness of each neighborhood is on
322  // the order N^4, we have only gone to N=100. In theory, you could make
323  // this large enough to get all distances exact. We have implemented the
324  // algorithm to get all distances < 100 to be exact.
325  int const dim0 = image.GetDimension(0);
326  int const dim1 = image.GetDimension(1);
327  int const dim0m1 = dim0 - 1;
328  int const dim1m1 = dim1 - 1;
329  int x, y, distance;
330 
331  // Create and initialize intermediate images.
332  Image2<int> xNear(dim0, dim1);
333  Image2<int> yNear(dim0, dim1);
334  Image2<int> dist(dim0, dim1);
335  for (y = 0; y < dim1; ++y)
336  {
337  for (x = 0; x < dim0; ++x)
338  {
339  if (image(x, y) != 0)
340  {
341  xNear(x, y) = 0;
342  yNear(x, y) = 0;
343  dist(x, y) = std::numeric_limits<int>::max();
344  }
345  else
346  {
347  xNear(x, y) = x;
348  yNear(x, y) = y;
349  dist(x, y) = 0;
350  }
351  }
352  }
353 
354  int const K1 = 1;
355  int const K2 = 169; // 13^2
356  int const K3 = 961; // 31^2
357  int const K4 = 2401; // 49^2
358  int const K5 = 5184; // 72^2
359 
360  // Pass in the ++ direction.
361  for (y = 0; y < dim1; ++y)
362  {
363  for (x = 0; x < dim0; ++x)
364  {
365  distance = dist(x, y);
366  if (distance > K1)
367  {
368  L2Check(x, y, -1, 0, xNear, yNear, dist);
369  L2Check(x, y, -1, -1, xNear, yNear, dist);
370  L2Check(x, y, 0, -1, xNear, yNear, dist);
371  }
372  if (distance > K2)
373  {
374  L2Check(x, y, -2, -1, xNear, yNear, dist);
375  L2Check(x, y, -1, -2, xNear, yNear, dist);
376  }
377  if (distance > K3)
378  {
379  L2Check(x, y, -3, -1, xNear, yNear, dist);
380  L2Check(x, y, -3, -2, xNear, yNear, dist);
381  L2Check(x, y, -2, -3, xNear, yNear, dist);
382  L2Check(x, y, -1, -3, xNear, yNear, dist);
383  }
384  if (distance > K4)
385  {
386  L2Check(x, y, -4, -1, xNear, yNear, dist);
387  L2Check(x, y, -4, -3, xNear, yNear, dist);
388  L2Check(x, y, -3, -4, xNear, yNear, dist);
389  L2Check(x, y, -1, -4, xNear, yNear, dist);
390  }
391  if (distance > K5)
392  {
393  L2Check(x, y, -5, -1, xNear, yNear, dist);
394  L2Check(x, y, -5, -2, xNear, yNear, dist);
395  L2Check(x, y, -5, -3, xNear, yNear, dist);
396  L2Check(x, y, -5, -4, xNear, yNear, dist);
397  L2Check(x, y, -4, -5, xNear, yNear, dist);
398  L2Check(x, y, -2, -5, xNear, yNear, dist);
399  L2Check(x, y, -3, -5, xNear, yNear, dist);
400  L2Check(x, y, -1, -5, xNear, yNear, dist);
401  }
402  }
403  }
404 
405  // Pass in -- direction.
406  for (y = dim1m1; y >= 0; --y)
407  {
408  for (x = dim0m1; x >= 0; --x)
409  {
410  distance = dist(x, y);
411  if (distance > K1)
412  {
413  L2Check(x, y, 1, 0, xNear, yNear, dist);
414  L2Check(x, y, 1, 1, xNear, yNear, dist);
415  L2Check(x, y, 0, 1, xNear, yNear, dist);
416  }
417  if (distance > K2)
418  {
419  L2Check(x, y, 2, 1, xNear, yNear, dist);
420  L2Check(x, y, 1, 2, xNear, yNear, dist);
421  }
422  if (distance > K3)
423  {
424  L2Check(x, y, 3, 1, xNear, yNear, dist);
425  L2Check(x, y, 3, 2, xNear, yNear, dist);
426  L2Check(x, y, 2, 3, xNear, yNear, dist);
427  L2Check(x, y, 1, 3, xNear, yNear, dist);
428  }
429  if (distance > K4)
430  {
431  L2Check(x, y, 4, 1, xNear, yNear, dist);
432  L2Check(x, y, 4, 3, xNear, yNear, dist);
433  L2Check(x, y, 3, 4, xNear, yNear, dist);
434  L2Check(x, y, 1, 4, xNear, yNear, dist);
435  }
436  if (distance > K5)
437  {
438  L2Check(x, y, 5, 1, xNear, yNear, dist);
439  L2Check(x, y, 5, 2, xNear, yNear, dist);
440  L2Check(x, y, 5, 3, xNear, yNear, dist);
441  L2Check(x, y, 5, 4, xNear, yNear, dist);
442  L2Check(x, y, 4, 5, xNear, yNear, dist);
443  L2Check(x, y, 2, 5, xNear, yNear, dist);
444  L2Check(x, y, 3, 5, xNear, yNear, dist);
445  L2Check(x, y, 1, 5, xNear, yNear, dist);
446  }
447  }
448  }
449 
450  // Pass in the +- direction.
451  for (y = dim1m1; y >= 0; --y)
452  {
453  for (x = 0; x < dim0; ++x)
454  {
455  distance = dist(x, y);
456  if (distance > K1)
457  {
458  L2Check(x, y, -1, 0, xNear, yNear, dist);
459  L2Check(x, y, -1, 1, xNear, yNear, dist);
460  L2Check(x, y, 0, 1, xNear, yNear, dist);
461  }
462  if (distance > K2)
463  {
464  L2Check(x, y, -2, 1, xNear, yNear, dist);
465  L2Check(x, y, -1, 2, xNear, yNear, dist);
466  }
467  if (distance > K3)
468  {
469  L2Check(x, y, -3, 1, xNear, yNear, dist);
470  L2Check(x, y, -3, 2, xNear, yNear, dist);
471  L2Check(x, y, -2, 3, xNear, yNear, dist);
472  L2Check(x, y, -1, 3, xNear, yNear, dist);
473  }
474  if (distance > K4)
475  {
476  L2Check(x, y, -4, 1, xNear, yNear, dist);
477  L2Check(x, y, -4, 3, xNear, yNear, dist);
478  L2Check(x, y, -3, 4, xNear, yNear, dist);
479  L2Check(x, y, -1, 4, xNear, yNear, dist);
480  }
481  if (distance > K5)
482  {
483  L2Check(x, y, -5, 1, xNear, yNear, dist);
484  L2Check(x, y, -5, 2, xNear, yNear, dist);
485  L2Check(x, y, -5, 3, xNear, yNear, dist);
486  L2Check(x, y, -5, 4, xNear, yNear, dist);
487  L2Check(x, y, -4, 5, xNear, yNear, dist);
488  L2Check(x, y, -2, 5, xNear, yNear, dist);
489  L2Check(x, y, -3, 5, xNear, yNear, dist);
490  L2Check(x, y, -1, 5, xNear, yNear, dist);
491  }
492  }
493  }
494 
495  // Pass in the -+ direction.
496  for (y = 0; y < dim1; ++y)
497  {
498  for (x = dim0m1; x >= 0; --x)
499  {
500  distance = dist(x, y);
501  if (distance > K1)
502  {
503  L2Check(x, y, 1, 0, xNear, yNear, dist);
504  L2Check(x, y, 1, -1, xNear, yNear, dist);
505  L2Check(x, y, 0, -1, xNear, yNear, dist);
506  }
507  if (distance > K2)
508  {
509  L2Check(x, y, 2, -1, xNear, yNear, dist);
510  L2Check(x, y, 1, -2, xNear, yNear, dist);
511  }
512  if (distance > K3)
513  {
514  L2Check(x, y, 3, -1, xNear, yNear, dist);
515  L2Check(x, y, 3, -2, xNear, yNear, dist);
516  L2Check(x, y, 2, -3, xNear, yNear, dist);
517  L2Check(x, y, 1, -3, xNear, yNear, dist);
518  }
519  if (distance > K4)
520  {
521  L2Check(x, y, 4, -1, xNear, yNear, dist);
522  L2Check(x, y, 4, -3, xNear, yNear, dist);
523  L2Check(x, y, 3, -4, xNear, yNear, dist);
524  L2Check(x, y, 1, -4, xNear, yNear, dist);
525  }
526  if (distance > K5)
527  {
528  L2Check(x, y, 5, -1, xNear, yNear, dist);
529  L2Check(x, y, 5, -2, xNear, yNear, dist);
530  L2Check(x, y, 5, -3, xNear, yNear, dist);
531  L2Check(x, y, 5, -4, xNear, yNear, dist);
532  L2Check(x, y, 4, -5, xNear, yNear, dist);
533  L2Check(x, y, 2, -5, xNear, yNear, dist);
534  L2Check(x, y, 3, -5, xNear, yNear, dist);
535  L2Check(x, y, 1, -5, xNear, yNear, dist);
536  }
537  }
538  }
539 
540  xMax = 0;
541  yMax = 0;
542  maxDistance = 0.0f;
543  for (y = 0; y < dim1; ++y)
544  {
545  for (x = 0; x < dim0; ++x)
546  {
547  float fdistance = sqrt((float)dist(x, y));
548  if (fdistance > maxDistance)
549  {
550  maxDistance = fdistance;
551  xMax = x;
552  yMax = y;
553  }
554  transform(x, y) = fdistance;
555  }
556  }
557 }
558 
560 {
561  int const dim0 = image.GetDimension(0);
562  int const dim1 = image.GetDimension(1);
563 
564  // Trim pixels, mark interior as 4.
565  bool notDone = true;
566  while (notDone)
567  {
568  if (MarkInterior(image, 4, Interior4))
569  {
570  // No interior pixels, trimmed set is at most 2-pixels thick.
571  notDone = false;
572  continue;
573  }
574 
575  if (ClearInteriorAdjacent(image, 4))
576  {
577  // All remaining interior pixels are either articulation points
578  // or part of blobs whose boundary pixels are all articulation
579  // points. An example of the latter case is shown below. The
580  // background pixels are marked with '.' rather than '0' for
581  // readability. The interior pixels are marked with '4' and the
582  // boundary pixels are marked with '1'.
583  //
584  // .........
585  // .....1...
586  // ..1.1.1..
587  // .1.141...
588  // ..14441..
589  // ..1441.1.
590  // .1.11.1..
591  // ..1..1...
592  // .........
593  //
594  // This is a pathological problem where there are many small holes
595  // (0-pixel with north, south, west, and east neighbors all
596  // 1-pixels) that your application can try to avoid by an initial
597  // pass over the image to fill in such holes. Of course, you do
598  // have problems with checkerboard patterns...
599  notDone = false;
600  continue;
601  }
602  }
603 
604  // Trim pixels, mark interior as 3.
605  notDone = true;
606  while (notDone)
607  {
608  if (MarkInterior(image, 3, Interior3))
609  {
610  // No interior pixels, trimmed set is at most 2-pixels thick.
611  notDone = false;
612  continue;
613  }
614 
615  if (ClearInteriorAdjacent(image, 3))
616  {
617  // All remaining 3-values can be safely removed since they are
618  // not articulation points and the removal will not cause new
619  // holes.
620  for (int y = 0; y < dim1; ++y)
621  {
622  for (int x = 0; x < dim0; ++x)
623  {
624  if (image(x, y) == 3 && !IsArticulation(image, x, y))
625  {
626  image(x, y) = 0;
627  }
628  }
629  }
630  notDone = false;
631  continue;
632  }
633  }
634 
635  // Trim pixels, mark interior as 2.
636  notDone = true;
637  while (notDone)
638  {
639  if (MarkInterior(image, 2, Interior2))
640  {
641  // No interior pixels, trimmed set is at most 1-pixel thick.
642  // Call it a skeleton.
643  notDone = false;
644  continue;
645  }
646 
647  if (ClearInteriorAdjacent(image, 2))
648  {
649  // Removes 2-values that are not articulation points.
650  for (int y = 0; y < dim1; ++y)
651  {
652  for (int x = 0; x < dim0; ++x)
653  {
654  if (image(x, y) == 2 && !IsArticulation(image, x, y))
655  {
656  image(x, y) = 0;
657  }
658  }
659  }
660  notDone = false;
661  continue;
662  }
663  }
664 
665  // Make the skeleton a binary image.
666  size_t const numPixels = image.GetNumPixels();
667  for (size_t i = 0; i < numPixels; ++i)
668  {
669  if (image[i] != 0)
670  {
671  image[i] = 1;
672  }
673  }
674 }
675 
676 void ImageUtility2::DrawThickPixel(int x, int y, int thick,
677  std::function<void(int, int)> const& callback)
678 {
679  for (int dy = -thick; dy <= thick; ++dy)
680  {
681  for (int dx = -thick; dx <= thick; ++dx)
682  {
683  callback(x + dx, y + dy);
684  }
685  }
686 }
687 
688 void ImageUtility2::DrawLine(int x0, int y0, int x1, int y1,
689  std::function<void(int, int)> const& callback)
690 {
691  // Starting point of line.
692  int x = x0, y = y0;
693 
694  // Direction of line.
695  int dx = x1 - x0, dy = y1 - y0;
696 
697  // Increment or decrement depending on direction of line.
698  int sx = (dx > 0 ? 1 : (dx < 0 ? -1 : 0));
699  int sy = (dy > 0 ? 1 : (dy < 0 ? -1 : 0));
700 
701  // Decision parameters for pixel selection.
702  if (dx < 0)
703  {
704  dx = -dx;
705  }
706  if (dy < 0)
707  {
708  dy = -dy;
709  }
710  int ax = 2 * dx, ay = 2 * dy;
711  int decX, decY;
712 
713  // Determine largest direction component, single-step related variable.
714  int maxValue = dx, var = 0;
715  if (dy > maxValue)
716  {
717  var = 1;
718  }
719 
720  // Traverse Bresenham line.
721  switch (var)
722  {
723  case 0: // Single-step in x-direction.
724  decY = ay - dx;
725  for (; ; x += sx, decY += ay)
726  {
727  callback(x, y);
728 
729  // Take Bresenham step.
730  if (x == x1)
731  {
732  break;
733  }
734  if (decY >= 0)
735  {
736  decY -= ax;
737  y += sy;
738  }
739  }
740  break;
741  case 1: // Single-step in y-direction.
742  decX = ax - dy;
743  for (; ; y += sy, decX += ax)
744  {
745  callback(x, y);
746 
747  // Take Bresenham step.
748  if (y == y1)
749  {
750  break;
751  }
752  if (decX >= 0)
753  {
754  decX -= ay;
755  x += sx;
756  }
757  }
758  break;
759  }
760 }
761 
762 void ImageUtility2::DrawCircle(int xCenter, int yCenter, int radius,
763  bool solid, std::function<void(int, int)> const& callback)
764 {
765  int x, y, dec;
766 
767  if (solid)
768  {
769  int xValue, yMin, yMax, i;
770  for (x = 0, y = radius, dec = 3 - 2*radius; x <= y; ++x)
771  {
772  xValue = xCenter + x;
773  yMin = yCenter - y;
774  yMax = yCenter + y;
775  for (i = yMin; i <= yMax; ++i)
776  {
777  callback(xValue, i);
778  }
779 
780  xValue = xCenter - x;
781  for (i = yMin; i <= yMax; ++i)
782  {
783  callback(xValue, i);
784  }
785 
786  xValue = xCenter + y;
787  yMin = yCenter - x;
788  yMax = yCenter + x;
789  for (i = yMin; i <= yMax; ++i)
790  {
791  callback(xValue, i);
792  }
793 
794  xValue = xCenter - y;
795  for (i = yMin; i <= yMax; ++i)
796  {
797  callback(xValue, i);
798  }
799 
800  if (dec >= 0)
801  {
802  dec += -4*(y--) + 4;
803  }
804  dec += 4*x + 6;
805  }
806  }
807  else
808  {
809  for (x = 0, y = radius, dec = 3 - 2*radius; x <= y; ++x)
810  {
811  callback(xCenter + x, yCenter + y);
812  callback(xCenter + x, yCenter - y);
813  callback(xCenter - x, yCenter + y);
814  callback(xCenter - x, yCenter - y);
815  callback(xCenter + y, yCenter + x);
816  callback(xCenter + y, yCenter - x);
817  callback(xCenter - y, yCenter + x);
818  callback(xCenter - y, yCenter - x);
819 
820  if (dec >= 0)
821  {
822  dec += -4*(y--) + 4;
823  }
824  dec += 4*x + 6;
825  }
826  }
827 }
828 
829 void ImageUtility2::DrawRectangle(int xMin, int yMin, int xMax,
830  int yMax, bool solid, std::function<void(int, int)> const& callback)
831 {
832  int x, y;
833 
834  if (solid)
835  {
836  for (y = yMin; y <= yMax; ++y)
837  {
838  for (x = xMin; x <= xMax; ++x)
839  {
840  callback(x, y);
841  }
842  }
843  }
844  else
845  {
846  for (x = xMin; x <= xMax; ++x)
847  {
848  callback(x, yMin);
849  callback(x, yMax);
850  }
851  for (y = yMin + 1; y <= yMax - 1; ++y)
852  {
853  callback(xMin, y);
854  callback(xMax, y);
855  }
856  }
857 }
858 
859 void ImageUtility2::DrawEllipse(int xCenter, int yCenter, int xExtent, int yExtent,
860  std::function<void(int, int)> const& callback)
861 {
862  int xExtSqr = xExtent * xExtent, yExtSqr = yExtent * yExtent;
863  int x, y, dec;
864 
865  x = 0;
866  y = yExtent;
867  dec = 2 * yExtSqr + xExtSqr * (1 - 2 * yExtent);
868  for (; yExtSqr * x <= xExtSqr * y; ++x)
869  {
870  callback(xCenter + x, yCenter + y);
871  callback(xCenter - x, yCenter + y);
872  callback(xCenter + x, yCenter - y);
873  callback(xCenter - x, yCenter - y);
874 
875  if (dec >= 0)
876  {
877  dec += 4 * xExtSqr * (1 - y);
878  --y;
879  }
880  dec += yExtSqr * (4 * x + 6);
881  }
882  if (y == 0 && x < xExtent)
883  {
884  // The discretization caused us to reach the y-axis before the
885  // x-values reached the ellipse vertices. Draw a solid line along
886  // the x-axis to those vertices.
887  for (; x <= xExtent; ++x)
888  {
889  callback(xCenter + x, yCenter);
890  callback(xCenter - x, yCenter);
891  }
892  return;
893  }
894 
895  x = xExtent;
896  y = 0;
897  dec = 2 * xExtSqr + yExtSqr * (1 - 2 * xExtent);
898  for (; xExtSqr * y <= yExtSqr * x; ++y)
899  {
900  callback(xCenter + x, yCenter + y);
901  callback(xCenter - x, yCenter + y);
902  callback(xCenter + x, yCenter - y);
903  callback(xCenter - x, yCenter - y);
904 
905  if (dec >= 0)
906  {
907  dec += 4 * yExtSqr * (1 - x);
908  --x;
909  }
910  dec += xExtSqr * (4 * y + 6);
911  }
912  if (x == 0 && y < yExtent)
913  {
914  // The discretization caused us to reach the x-axis before the
915  // y-values reached the ellipse vertices. Draw a solid line along
916  // the y-axis to those vertices.
917  for (; y <= yExtent; ++y)
918  {
919  callback(xCenter, yCenter + y);
920  callback(xCenter, yCenter - y);
921  }
922  }
923 }
924 
925 void ImageUtility2::GetComponents(int numNeighbors, int const* delta,
926  Image2<int>& image, std::vector<std::vector<size_t>>& components)
927 {
928  size_t const numPixels = image.GetNumPixels();
929  std::vector<int> numElements(numPixels);
930  std::vector<size_t> vstack(numPixels);
931  size_t i, numComponents = 0;
932  int label = 2;
933  for (i = 0; i < numPixels; ++i)
934  {
935  if (image[i] == 1)
936  {
937  int top = -1;
938  vstack[++top] = i;
939 
940  int& count = numElements[numComponents + 1];
941  count = 0;
942  while (top >= 0)
943  {
944  size_t v = vstack[top];
945  image[v] = -1;
946  int j;
947  for (j = 0; j < numNeighbors; ++j)
948  {
949  size_t adj = v + delta[j];
950  if (image[adj] == 1)
951  {
952  vstack[++top] = adj;
953  break;
954  }
955  }
956  if (j == numNeighbors)
957  {
958  image[v] = label;
959  ++count;
960  --top;
961  }
962  }
963 
964  ++numComponents;
965  ++label;
966  }
967  }
968 
969  if (numComponents > 0)
970  {
971  components.resize(numComponents + 1);
972  for (i = 1; i <= numComponents; ++i)
973  {
974  components[i].resize(numElements[i]);
975  numElements[i] = 0;
976  }
977 
978  for (i = 0; i < numPixels; ++i)
979  {
980  int value = image[i];
981  if (value != 0)
982  {
983  // Labels started at 2 to support the depth-first search,
984  // so they need to be decremented for the correct labels.
985  image[i] = --value;
986  components[value][numElements[value]] = i;
987  ++numElements[value];
988  }
989  }
990  }
991 }
992 
993 void ImageUtility2::L2Check(int x, int y, int dx, int dy, Image2<int>& xNear,
994  Image2<int>& yNear, Image2<int>& dist)
995 {
996  int const dim0 = dist.GetDimension(0);
997  int const dim1 = dist.GetDimension(1);
998  int xp = x + dx, yp = y + dy;
999  if (0 <= xp && xp < dim0 && 0 <= yp && yp < dim1)
1000  {
1001  if (dist(xp, yp) < dist(x, y))
1002  {
1003  int dx0 = xNear(xp, yp) - x;
1004  int dy0 = yNear(xp, yp) - y;
1005  int newDist = dx0*dx0 + dy0*dy0;
1006  if (newDist < dist(x, y))
1007  {
1008  xNear(x, y) = xNear(xp, yp);
1009  yNear(x, y) = yNear(xp, yp);
1010  dist(x, y) = newDist;
1011  }
1012  }
1013  }
1014 }
1015 
1017 {
1018  bool b1 = (image(x, y-1) != 0);
1019  bool b3 = (image(x+1, y) != 0);
1020  bool b5 = (image(x, y+1) != 0);
1021  bool b7 = (image(x-1, y) != 0);
1022  return (b1 && b3) || (b3 && b5) || (b5 && b7) || (b7 && b1);
1023 }
1024 
1026 {
1027  int numNeighbors = 0;
1028  if (image(x-1, y) != 0)
1029  {
1030  ++numNeighbors;
1031  }
1032  if (image(x+1, y) != 0)
1033  {
1034  ++numNeighbors;
1035  }
1036  if (image(x, y-1) != 0)
1037  {
1038  ++numNeighbors;
1039  }
1040  if (image(x, y+1) != 0)
1041  {
1042  ++numNeighbors;
1043  }
1044  return numNeighbors == 3;
1045 }
1046 
1048 {
1049  return image(x-1, y) != 0
1050  && image(x+1, y) != 0
1051  && image(x, y-1) != 0
1052  && image(x, y+1) != 0;
1053 }
1054 
1056  bool (*function)(Image2<int>&,int,int))
1057 {
1058  int const dim0 = image.GetDimension(0);
1059  int const dim1 = image.GetDimension(1);
1060  bool noInterior = true;
1061  for (int y = 0; y < dim1; ++y)
1062  {
1063  for (int x = 0; x < dim0; ++x)
1064  {
1065  if (image(x, y) > 0)
1066  {
1067  if (function(image, x, y))
1068  {
1069  image(x, y) = value;
1070  noInterior = false;
1071  }
1072  else
1073  {
1074  image(x, y) = 1;
1075  }
1076  }
1077  }
1078  }
1079  return noInterior;
1080 }
1081 
1082 bool ImageUtility2::IsArticulation(Image2<int>& image, int x, int y)
1083 {
1084  // Converts 8 neighbors of pixel (x,y) to an 8-bit value, bit = 1 iff
1085  // pixel is set.
1086  int byteMask = 0;
1087  if (image(x-1, y-1) != 0)
1088  {
1089  byteMask |= 0x01;
1090  }
1091  if (image(x, y-1) != 0)
1092  {
1093  byteMask |= 0x02;
1094  }
1095  if (image(x+1, y-1) != 0)
1096  {
1097  byteMask |= 0x04;
1098  }
1099  if (image(x+1, y) != 0)
1100  {
1101  byteMask |= 0x08;
1102  }
1103  if (image(x+1, y+1) != 0)
1104  {
1105  byteMask |= 0x10;
1106  }
1107  if (image(x, y+1) != 0)
1108  {
1109  byteMask |= 0x20;
1110  }
1111  if (image(x-1, y+1) != 0)
1112  {
1113  byteMask |= 0x40;
1114  }
1115  if (image(x-1, y) != 0)
1116  {
1117  byteMask |= 0x80;
1118  }
1119 
1120  return msArticulation[byteMask] == 1;
1121 }
1122 
1124 {
1125  int const dim0 = image.GetDimension(0);
1126  int const dim1 = image.GetDimension(1);
1127  bool noRemoval = true;
1128  for (int y = 0; y < dim1; ++y)
1129  {
1130  for (int x = 0; x < dim0; ++x)
1131  {
1132  if (image(x, y) == 1)
1133  {
1134  bool interiorAdjacent =
1135  image(x-1, y-1) == value ||
1136  image(x , y-1) == value ||
1137  image(x+1, y-1) == value ||
1138  image(x+1, y ) == value ||
1139  image(x+1, y+1) == value ||
1140  image(x , y+1) == value ||
1141  image(x-1, y+1) == value ||
1142  image(x-1, y ) == value;
1143 
1144  if (interiorAdjacent && !IsArticulation(image, x, y))
1145  {
1146  image(x, y) = 0;
1147  noRemoval = false;
1148  }
1149  }
1150  }
1151  }
1152  return noRemoval;
1153 }
1154 
1155 
1156 int const ImageUtility2::msArticulation[256] =
1157 {
1158  0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
1159  0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,
1160  0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,
1161  0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,
1162  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1163  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1164  0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,
1165  0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,
1166  0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,
1167  1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,
1168  0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,
1169  0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,
1170  0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,
1171  1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,
1172  0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,
1173  0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0
1174 };
static bool IsArticulation(Image2< int > &image, int x, int y)
static void DrawCircle(int xCenter, int yCenter, int radius, bool solid, std::function< void(int, int)> const &callback)
size_t GetIndex(int x, int y) const
Definition: GteImage2.h:146
static bool ExtractBoundary(int x, int y, Image2< int > &image, std::vector< size_t > &boundary)
static bool Interior4(Image2< int > &image, int x, int y)
static void Open4(Image2< int > const &input, bool zeroExterior, Image2< int > &output)
static void Erode8(Image2< int > const &input, bool zeroExterior, Image2< int > &output)
#define LogAssert(condition, message)
Definition: GteLogger.h:86
static void GetComponents(int numNeighbors, int const *delta, Image2< int > &image, std::vector< std::vector< size_t >> &components)
static void DrawLine(int x0, int y0, int x1, int y1, std::function< void(int, int)> const &callback)
int GetDimension(int d) const
Definition: GteImage.h:169
GLenum GLenum GLsizei void * image
Definition: glext.h:2724
static void Erode(Image2< int > const &input, bool zeroExterior, int numNeighbors, std::array< int, 2 > const *neighbors, Image2< int > &output)
static void GetComponents4(Image2< int > &image, std::vector< std::vector< size_t >> &components)
static void Close8(Image2< int > const &input, bool zeroExterior, Image2< int > &output)
GLsizei const GLfloat * value
Definition: glcorearb.h:819
GLsizei GLsizei GLfloat distance
Definition: glext.h:9704
static void GetL2Distance(Image2< int > const &image, float &maxDistance, int &xMax, int &yMax, Image2< float > &transform)
GLuint coord
Definition: glext.h:5871
GLint GLenum GLint x
Definition: glcorearb.h:404
static bool ClearInteriorAdjacent(Image2< int > &image, int value)
static void GetComponents8(Image2< int > &image, std::vector< std::vector< size_t >> &components)
static void Erode4(Image2< int > const &input, bool zeroExterior, Image2< int > &output)
static void L2Check(int x, int y, int dx, int dy, Image2< int > &xNear, Image2< int > &yNear, Image2< int > &dist)
GLfixed y1
Definition: glext.h:4952
static void GetSkeleton(Image2< int > &image)
void GetCoordinates(size_t index, int &x, int &y) const
Definition: GteImage2.h:184
static void GetL1Distance(Image2< int > &image, int &maxDistance, int &xMax, int &yMax)
GLuint GLsizei const GLchar * label
Definition: glcorearb.h:2540
static void Open8(Image2< int > const &input, bool zeroExterior, Image2< int > &output)
size_t GetNumPixels() const
Definition: GteImage.h:193
static bool MarkInterior(Image2< int > &image, int value, bool(*function)(Image2< int > &, int, int))
GLuint GLfloat x0
Definition: glext.h:9013
GLint GLsizei count
Definition: glcorearb.h:400
GLbyte nx
Definition: glext.h:6082
static void Close4(Image2< int > const &input, bool zeroExterior, Image2< int > &output)
static void Close(Image2< int > const &input, bool zeroExterior, int numNeighbors, std::array< int, 2 > const *neighbors, Image2< int > &output)
static void DrawEllipse(int xCenter, int yCenter, int xExtent, int yExtent, std::function< void(int, int)> const &callback)
static void Dilate4(Image2< int > const &input, Image2< int > &output)
static bool Interior2(Image2< int > &image, int x, int y)
static void DrawRectangle(int xMin, int yMin, int xMax, int yMax, bool solid, std::function< void(int, int)> const &callback)
GLuint GLfloat GLfloat GLfloat x1
Definition: glext.h:9013
static void Dilate(Image2< int > const &input, int numNeighbors, std::array< int, 2 > const *neighbors, Image2< int > &output)
const GLdouble * v
Definition: glcorearb.h:832
GLenum GLenum GLenum input
Definition: glext.h:9913
GLenum GLenum GLuint components
Definition: glext.h:8352
void GetNeighborhood(std::array< int, 4 > &nbr) const
Definition: GteImage2.h:384
GLuint GLfloat GLfloat y0
Definition: glext.h:9013
static int const msArticulation[256]
static void DrawThickPixel(int x, int y, int thick, std::function< void(int, int)> const &callback)
static void Open(Image2< int > const &input, bool zeroExterior, int numNeighbors, std::array< int, 2 > const *neighbors, Image2< int > &output)
GLuint GLenum GLenum transform
Definition: glext.h:10574
GLfixed ny
Definition: glext.h:4889
static void Dilate8(Image2< int > const &input, Image2< int > &output)
GLdouble GLdouble GLdouble GLdouble top
Definition: glext.h:6472
static bool Interior3(Image2< int > &image, int x, int y)
GLint y
Definition: glcorearb.h:98


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:00