GteImageUtility2.h
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 #pragma once
9 
10 #include <Imagics/GteImage2.h>
11 
12 namespace gte
13 {
14 
16 {
17 public:
18  // All but the Draw* functions are operations on binary images. Let the
19  // image have d0 columns and d1 rows. The input image must have zeros on
20  // its boundaries x = 0, x = d0-1, y = 0, and y = d1-1. The 0-valued
21  // pixels are considered to be background. The 1-valued pixels are
22  // considered to be foreground. In some of the operations, to save memory
23  // and time the input image is modified by the algorithms. If you need
24  // to preserve the input image, make a copy of it before calling these
25  // functions.
26  //
27  // Dilation and erosion functions do not have the requirement that the
28  // boundary pixels of the binary image inputs be zero.
29 
30  // Compute the 4-connected components of a binary image. The input image
31  // is modified to avoid the cost of making a copy. On output, the image
32  // values are the labels for the components. The array components[k],
33  // k >= 1, contains the indices for the k-th component.
34  static void GetComponents4(Image2<int>& image,
35  std::vector<std::vector<size_t>>& components);
36 
37  // Compute the 8-connected components of a binary image. The input image
38  // is modified to avoid the cost of making a copy. On output, the image
39  // values are the labels for the components. The array components[k],
40  // k >= 1, contains the indices for the k-th component.
41  static void GetComponents8(Image2<int>& image,
42  std::vector<std::vector<size_t>>& components);
43 
44  // Compute a dilation with a structuring element consisting of the
45  // 4-connected neighbors of each pixel. The input image is binary with 0
46  // for background and 1 for foreground. The output image must be an
47  // object different from the input image.
48  static void Dilate4(Image2<int> const& input, Image2<int>& output);
49 
50  // Compute a dilation with a structuring element consisting of the
51  // 8-connected neighbors of each pixel. The input image is binary with 0
52  // for background and 1 for foreground. The output image must be an
53  // object different from the input image.
54  static void Dilate8(Image2<int> const& input, Image2<int>& output);
55 
56  // Compute a dilation with a structing element consisting of neighbors
57  // specified by offsets relative to the pixel. The input image is binary
58  // with 0 for background and 1 for foreground. The output image must be
59  // an object different from the input image.
60  static void Dilate(Image2<int> const& input, int numNeighbors,
61  std::array<int, 2> const* neighbors, Image2<int>& output);
62 
63  // Compute an erosion with a structuring element consisting of the
64  // 4-connected neighbors of each pixel. The input image is binary with 0
65  // for background and 1 for foreground. The output image must be an
66  // object different from the input image. If zeroExterior is true, the
67  // image exterior is assumed to be 0, so 1-valued boundary pixels are set
68  // to 0; otherwise, boundary pixels are set to 0 only when they have
69  // neighboring image pixels that are 0.
70  static void Erode4(Image2<int> const& input, bool zeroExterior,
71  Image2<int>& output);
72 
73  // Compute an erosion with a structuring element consisting of the
74  // 8-connected neighbors of each pixel. The input image is binary with 0
75  // for background and 1 for foreground. The output image must be an
76  // object different from the input image. If zeroExterior is true, the
77  // image exterior is assumed to be 0, so 1-valued boundary pixels are
78  // set to 0; otherwise, boundary pixels are set to 0 only when they have
79  // neighboring image pixels that are 0.
80  static void Erode8(Image2<int> const& input, bool zeroExterior,
81  Image2<int>& output);
82 
83  // Compute an erosion with a structuring element consisting of neighbors
84  // specified by offsets relative to the pixel. The input image is binary
85  // with 0 for background and 1 for foreground. The output image must be
86  // an object different from the input image. If zeroExterior is true, the
87  // image exterior is assumed to be 0, so 1-valued boundary pixels are set
88  // to 0; otherwise, boundary pixels are set to 0 only when they have
89  // neighboring image pixels that are 0.
90  static void Erode(Image2<int> const& input, bool zeroExterior,
91  int numNeighbors, std::array<int, 2> const* neighbors,
92  Image2<int>& output);
93 
94  // Compute an opening with a structuring element consisting of the
95  // 4-connected neighbors of each pixel. The input image is binary with 0
96  // for background and 1 for foreground. The output image must be an
97  // object different from the input image. If zeroExterior is true, the
98  // image exterior is assumed to consist of 0-valued pixels; otherwise,
99  // the image exterior is assumed to consist of 1-valued pixels.
100  static void Open4(Image2<int> const& input, bool zeroExterior,
101  Image2<int>& output);
102 
103  // Compute an opening with a structuring element consisting of the
104  // 8-connected neighbors of each pixel. The input image is binary with 0
105  // for background and 1 for foreground. The output image must be an
106  // object different from the input image. If zeroExterior is true, the
107  // image exterior is assumed to consist of 0-valued pixels; otherwise,
108  // the image exterior is assumed to consist of 1-valued pixels.
109  static void Open8(Image2<int> const& input, bool zeroExterior,
110  Image2<int>& output);
111 
112  // Compute an opening with a structuring element consisting of neighbors
113  // specified by offsets relative to the pixel. The input image is binary
114  // with 0 for background and 1 for foreground. The output image must be
115  // an object different from the input image. If zeroExterior is true, the
116  // image exterior is assumed to consist of 0-valued pixels; otherwise,
117  // the image exterior is assumed to consist of 1-valued pixels.
118  static void Open(Image2<int> const& input, bool zeroExterior,
119  int numNeighbors, std::array<int, 2> const* neighbors,
120  Image2<int>& output);
121 
122  // Compute a closing with a structuring element consisting of the
123  // 4-connected neighbors of each pixel. The input image is binary with 0
124  // for background and 1 for foreground. The output image must be an
125  // object different from the input image. If zeroExterior is true, the
126  // image exterior is assumed to consist of 0-valued pixels; otherwise,
127  // the image exterior is assumed to consist of 1-valued pixels.
128  static void Close4(Image2<int> const& input, bool zeroExterior,
129  Image2<int>& output);
130 
131  // Compute a closing with a structuring element consisting of the
132  // 8-connected neighbors of each pixel. The input image is binary with 0
133  // for background and 1 for foreground. The output image must be an
134  // object different from the input image. If zeroExterior is true, the
135  // image exterior is assumed to consist of 0-valued pixels; otherwise,
136  // the image exterior is assumed to consist of 1-valued pixels.
137  static void Close8(Image2<int> const& input, bool zeroExterior,
138  Image2<int>& output);
139 
140  // Compute a closing with a structuring element consisting of neighbors
141  // specified by offsets relative to the pixel. The input image is binary
142  // with 0 for background and 1 for foreground. The output image must be
143  // an object different from the input image. If zeroExterior is true, the
144  // image exterior is assumed to consist of 0-valued pixels; otherwise,
145  // the image exterior is assumed to consist of 1-valued pixels.
146  static void Close(Image2<int> const& input, bool zeroExterior,
147  int numNeighbors, std::array<int, 2> const* neighbors,
148  Image2<int>& output);
149 
150  // Locate a pixel and walk around the edge of a component. The input
151  // (x,y) is where the search starts for a nonzero pixel. If (x,y) is
152  // outside the component, the walk is around the outside the component.
153  // If the component has a hole and (x,y) is inside that hole, the walk
154  // is around the boundary surrounding the hole. The function returns
155  // 'true' on a success walk. The return value is 'false' when no
156  // boundary was found from the starting (x,y).
157  static bool ExtractBoundary(int x, int y, Image2<int>& image,
158  std::vector<size_t>& boundary);
159 
160  // Use a depth-first search for filling a 4-connected region. This is
161  // nonrecursive, simulated by using a heap-allocated "stack". The input
162  // (x,y) is the seed point that starts the fill.
163  template <typename PixelType>
164  static void FloodFill4(Image2<PixelType>& image, int x, int y,
165  PixelType foreColor, PixelType backColor);
166 
167  // Compute the L1-distance transform of the binary image. The function
168  // returns the maximum distance and a point at which the maximum
169  // distance is attained.
170  static void GetL1Distance(Image2<int>& image, int& maxDistance,
171  int& xMax, int& yMax);
172 
173  // Compute the L2-distance transform of the binary image. The maximum
174  // distance should not be larger than 100, so you have to ensure this is
175  // the case for the input image. The function returns the maximum
176  // distance and a point at which the maximum distance is attained.
177  // Comments about the algorithm are in the source file.
178  static void GetL2Distance(Image2<int> const& image, float& maxDistance,
179  int& xMax, int& yMax, Image2<float>& transform);
180 
181  // Compute a skeleton of a binary image. Boundary pixels are trimmed from
182  // the object one layer at a time based on their adjacency to interior
183  // pixels. At each step the connectivity and cycles of the object are
184  // preserved. The skeleton overwrites the contents of the input image.
185  static void GetSkeleton(Image2<int>& image);
186 
187  // In the remaining public member functions, the callback represents the
188  // action you want applied to each pixel as it is visited.
189 
190  // Visit pixels in a (2*thick+1)x(2*thick+1) square centered at (x,y).
191  static void DrawThickPixel(int x, int y, int thick,
192  std::function<void(int, int)> const& callback);
193 
194  // Visit pixels using Bresenham's line drawing algorithm.
195  static void DrawLine(int x0, int y0, int x1, int y1,
196  std::function<void(int, int)> const& callback);
197 
198  // Visit pixels using Bresenham's circle drawing algorithm. Set 'solid'
199  // to false for drawing only the circle. Set 'solid' to true to draw all
200  // pixels on and inside the circle.
201  static void DrawCircle(int xCenter, int yCenter, int radius, bool solid,
202  std::function<void(int, int)> const& callback);
203 
204  // Visit pixels in a rectangle of the specified dimensions. Set 'solid'
205  // to false for drawing only the rectangle. Set 'solid' to true to draw
206  // all pixels on and inside the rectangle.
207  static void DrawRectangle(int xMin, int yMin, int xMax, int yMax,
208  bool solid, std::function<void(int, int)> const& callback);
209 
210  // Visit the pixels using Bresenham's algorithm for the axis-aligned
211  // ellipse ((x-xc)/a)^2 + ((y-yc)/b)^2 = 1, where xCenter is xc,
212  // yCenter is yc, xExtent is a, and yExtent is b.
213  static void DrawEllipse(int xCenter, int yCenter, int xExtent, int yExtent,
214  std::function<void(int, int)> const& callback);
215 
216  // Use a depth-first search for filling a 4-connected region. This is
217  // nonrecursive, simulated by using a heap-allocated "stack". The input
218  // (x,y) is the seed point that starts the fill. The x-value is in
219  // {0..xSize-1} and the y-value is in {0..ySize-1}.
220  template <typename PixelType>
221  static void DrawFloodFill4(int x, int y, int xSize, int ySize,
222  PixelType foreColor, PixelType backColor,
223  std::function<void(int, int, PixelType)> const& setCallback,
224  std::function<PixelType(int, int)> const& getCallback);
225 
226 private:
227  // Connected component labeling using depth-first search.
228  static void GetComponents(int numNeighbors, int const* delta,
229  Image2<int>& image, std::vector<std::vector<size_t>>& components);
230 
231  // Support for GetL2Distance.
232  static void L2Check(int x, int y, int dx, int dy, Image2<int>& xNear,
233  Image2<int>& yNear, Image2<int>& dist);
234 
235  // Support for GetSkeleton.
236  static bool Interior2 (Image2<int>& image, int x, int y);
237  static bool Interior3 (Image2<int>& image, int x, int y);
238  static bool Interior4 (Image2<int>& image, int x, int y);
239  static bool MarkInterior (Image2<int>& image, int value,
240  bool (*function)(Image2<int>&,int,int));
241  static bool IsArticulation (Image2<int>& image, int x, int y);
242  static bool ClearInteriorAdjacent (Image2<int>& image, int value);
243  static int const msArticulation[256];
244 };
245 
246 
247 template <typename PixelType>
249  PixelType foreColor, PixelType backColor)
250 {
251  // Test for a valid seed.
252  int const dim0 = image.GetDimension(0);
253  int const dim1 = image.GetDimension(1);
254  if (x < 0 || x >= dim0 || y < 0 || y >= dim1)
255  {
256  // The seed point is outside the image domain, so nothing to fill.
257  return;
258  }
259 
260  // Allocate the maximum amount of space needed for the stack. An empty
261  // stack has top == -1.
262  size_t const numPixels = image.GetNumPixels();
263  std::vector<int> xStack(numPixels), yStack(numPixels);
264 
265  // Push seed point onto stack if it has the background color. All points
266  // pushed onto stack have background color backColor.
267  int top = 0;
268  xStack[top] = x;
269  yStack[top] = y;
270 
271  while (top >= 0) // stack is not empty
272  {
273  // Read top of stack. Do not pop since we need to return to this
274  // top value later to restart the fill in a different direction.
275  x = xStack[top];
276  y = yStack[top];
277 
278  // Fill the pixel.
279  image(x, y) = foreColor;
280 
281  int xp1 = x + 1;
282  if (xp1 < dim0 && image(xp1, y) == backColor)
283  {
284  // Push pixel with background color.
285  ++top;
286  xStack[top] = xp1;
287  yStack[top] = y;
288  continue;
289  }
290 
291  int xm1 = x - 1;
292  if (0 <= xm1 && image(xm1, y) == backColor)
293  {
294  // Push pixel with background color.
295  ++top;
296  xStack[top] = xm1;
297  yStack[top] = y;
298  continue;
299  }
300 
301  int yp1 = y + 1;
302  if (yp1 < dim1 && image(x, yp1) == backColor)
303  {
304  // Push pixel with background color.
305  ++top;
306  xStack[top] = x;
307  yStack[top] = yp1;
308  continue;
309  }
310 
311  int ym1 = y - 1;
312  if (0 <= ym1 && image(x, ym1) == backColor)
313  {
314  // Push pixel with background color.
315  ++top;
316  xStack[top] = x;
317  yStack[top] = ym1;
318  continue;
319  }
320 
321  // Done in all directions, pop and return to search other directions.
322  --top;
323  }
324 }
325 
326 template <typename PixelType>
327 void ImageUtility2::DrawFloodFill4(int x, int y, int xSize, int ySize,
328  PixelType foreColor, PixelType backColor,
329  std::function<void(int, int, PixelType)> const& setCallback,
330  std::function<PixelType(int, int)> const& getCallback)
331 {
332  // Test for a valid seed.
333  if (x < 0 || x >= xSize || y < 0 || y >= ySize)
334  {
335  // The seed point is outside the image domain, so nothing to fill.
336  return;
337  }
338 
339  // Allocate the maximum amount of space needed for the stack. An empty
340  // stack has top == -1.
341  int const numPixels = xSize * ySize;
342  std::vector<int> xStack(numPixels), yStack(numPixels);
343 
344  // Push seed point onto stack if it has the background color. All points
345  // pushed onto stack have background color backColor.
346  int top = 0;
347  xStack[top] = x;
348  yStack[top] = y;
349 
350  while (top >= 0) // stack is not empty
351  {
352  // Read top of stack. Do not pop since we need to return to this
353  // top value later to restart the fill in a different direction.
354  x = xStack[top];
355  y = yStack[top];
356 
357  // Fill the pixel.
358  setCallback(x, y, foreColor);
359 
360  int xp1 = x + 1;
361  if (xp1 < xSize && getCallback(xp1, y) == backColor)
362  {
363  // Push pixel with background color.
364  ++top;
365  xStack[top] = xp1;
366  yStack[top] = y;
367  continue;
368  }
369 
370  int xm1 = x - 1;
371  if (0 <= xm1 && getCallback(xm1, y) == backColor)
372  {
373  // Push pixel with background color.
374  ++top;
375  xStack[top] = xm1;
376  yStack[top] = y;
377  continue;
378  }
379 
380  int yp1 = y + 1;
381  if (yp1 < ySize && getCallback(x, yp1) == backColor)
382  {
383  // Push pixel with background color.
384  ++top;
385  xStack[top] = x;
386  yStack[top] = yp1;
387  continue;
388  }
389 
390  int ym1 = y - 1;
391  if (0 <= ym1 && getCallback(x, ym1) == backColor)
392  {
393  // Push pixel with background color.
394  ++top;
395  xStack[top] = x;
396  yStack[top] = ym1;
397  continue;
398  }
399 
400  // Done in all directions, pop and return to search other directions.
401  --top;
402  }
403 }
404 
405 }
int GetDimension(int d) const
Definition: GteImage.h:169
GLenum GLenum GLsizei void * image
Definition: glext.h:2724
GLsizei const GLfloat * value
Definition: glcorearb.h:819
GLint GLenum GLint x
Definition: glcorearb.h:404
GLfixed y1
Definition: glext.h:4952
size_t GetNumPixels() const
Definition: GteImage.h:193
static void FloodFill4(Image2< PixelType > &image, int x, int y, PixelType foreColor, PixelType backColor)
GLuint GLfloat x0
Definition: glext.h:9013
GLuint GLfloat GLfloat GLfloat x1
Definition: glext.h:9013
GLenum GLenum GLenum input
Definition: glext.h:9913
GLenum GLenum GLuint components
Definition: glext.h:8352
GLuint GLfloat GLfloat y0
Definition: glext.h:9013
GLuint GLenum GLenum transform
Definition: glext.h:10574
GLdouble GLdouble GLdouble GLdouble top
Definition: glext.h:6472
GLint y
Definition: glcorearb.h:98
#define GTE_IMPEXP
Definition: GTEngineDEF.h:63
static void DrawFloodFill4(int x, int y, int xSize, int ySize, PixelType foreColor, PixelType backColor, std::function< void(int, int, PixelType)> const &setCallback, std::function< PixelType(int, int)> const &getCallback)


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