Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
sdfutils.cc
Go to the documentation of this file.
1 
18 #include "ion/text/sdfutils.h"
19 
20 #include "ion/base/logging.h"
21 #include "ion/math/utils.h"
22 #include "ion/math/vector.h"
23 #include "ion/math/vectorutils.h"
24 
25 namespace ion {
26 namespace text {
27 
28 namespace {
29 
30 using base::Array2;
31 using math::Vector2d;
32 using math::Vector2i;
33 
36 typedef Array2<double> Grid;
37 
39 
51 
52 
53 class DistanceComputer {
54  public:
55  DistanceComputer() {}
56  ~DistanceComputer() {}
57 
63  const Grid Compute(const Grid& image);
64 
65  private:
68  struct Data {
69  Data(const Grid& image_in, const Array2<Vector2d>& gradients_in)
70  : image(image_in),
71  gradients(gradients_in),
72  cur_pixel(0, 0),
73  any_distance_changed(false) {}
74 
76  void SetCurDistance(double dist) {
77  distances.Set(cur_pixel[0], cur_pixel[1], dist);
78  }
79  double GetCurDistance() const {
80  return distances.Get(cur_pixel[0], cur_pixel[1]);
81  }
82  void SetCurDistanceToEdge(const Vector2i& dist) {
83  distances_to_edges.Set(cur_pixel[0], cur_pixel[1], dist);
84  }
85  const Vector2i& GetDistanceToEdge(const Vector2i& pixel) const {
86  return distances_to_edges.Get(pixel[0], pixel[1]);
87  }
88 
90  const Grid& image;
92  const Array2<Vector2d>& gradients;
94  Array2<Vector2i> distances_to_edges;
96  Grid distances;
98  Vector2i cur_pixel;
101  };
102 
105  static const Array2<Vector2d> ComputeGradients(const Grid& image);
106 
108  static const Vector2d FilterPixel(const Grid& image, size_t x, size_t y);
109 
111  static const Grid InitializeDistanceGrid(
112  const Grid& image, const Array2<Vector2d>& gradients);
113 
116  static double ApproximateDistanceToEdge(double value,
117  const Vector2d& gradient);
118 
120  static void ComputeDistances(Data* data);
121 
126  static void UpdateDistance(Data* data, const Vector2i& offset, double* dist);
127 
130  static double ComputeDistanceToEdge(Data* data, const Vector2i& pixel,
131  const Vector2d& vec_to_edge_pixel);
132 
134  static const double kLargeDistance;
135 };
136 
137 const double DistanceComputer::kLargeDistance = 1e6;
138 
139 const Grid DistanceComputer::Compute(const Grid& image) {
141  const Array2<Vector2d> gradients = ComputeGradients(image);
142 
144  Data data(image, gradients);
145  data.distances_to_edges =
146  Array2<Vector2i>(image.GetWidth(), image.GetHeight(), Vector2i::Zero());
147  data.distances = InitializeDistanceGrid(image, gradients);
148  ComputeDistances(&data);
149 
150  return data.distances;
151 }
152 
153 const Array2<Vector2d> DistanceComputer::ComputeGradients(const Grid& image) {
154  const size_t h = image.GetHeight();
155  const size_t w = image.GetWidth();
156 
157  Array2<Vector2d> gradients(w, h, Vector2d::Zero());
158 
162 
165  for (size_t y = 1; y < h - 1; ++y) {
166  for (size_t x = 1; x < w - 1; ++x) {
167  const double value = image.Get(x, y);
170  if (value > 0.0 && value < 1.0)
171  gradients.Set(x, y, FilterPixel(image, x, y));
172  }
173  }
174  return gradients;
175 }
176 
177 const Vector2d DistanceComputer::FilterPixel(const Grid& image,
178  size_t x, size_t y) {
181  static const double kSqrt2 = sqrt(2.0);
182  static const double kFilter[3][3] = {
183  { -1.0, 0.0, 1.0 },
184  { -kSqrt2, 0.0, kSqrt2 },
185  { -1.0, 0.0, 1.0 },
186  };
187 
188  Vector2d filtered(0.0, 0.0);
189  for (int i = 0; i < 3; ++i) {
190  for (int j = 0; j < 3; ++j) {
191  const double val = image.Get(x + j - 1, y + i - 1);
192  filtered[0] += kFilter[i][j] * val;
193  filtered[1] += kFilter[j][i] * val;
194  }
195  }
196  return math::Normalized(filtered);
197 }
198 
199 const Grid DistanceComputer::InitializeDistanceGrid(
200  const Grid& image, const Array2<Vector2d>& gradients) {
201  const size_t h = image.GetHeight();
202  const size_t w = image.GetWidth();
203  Grid distances(w, h);
204  for (size_t y = 0; y < h; ++y) {
205  for (size_t x = 0; x < w; ++x) {
206  const double v = image.Get(x, y);
207  const double dist = v <= 0.0 ? kLargeDistance :
208  v >= 1.0 ? 0.0 :
209  ApproximateDistanceToEdge(v, gradients.Get(x, y));
210  distances.Set(x, y, dist);
211  }
212  }
213  return distances;
214 }
215 
216 double DistanceComputer::ApproximateDistanceToEdge(
217  double value, const Vector2d& gradient) {
218  if (gradient[0] == 0.0 || gradient[1] == 0.0) {
220  return 0.5 - value;
221  } else {
225  Vector2d g = math::Normalized(Vector2d(math::Abs(gradient[0]),
226  math::Abs(gradient[1])));
227  if (g[0] < g[1])
228  std::swap(g[0], g[1]);
229  const float gradient_value = static_cast<float>(0.5 * g[1] / g[0]);
230  double dist;
231  if (value < gradient_value) {
233  dist = 0.5 * (g[0] + g[1]) - sqrt(2.0 * g[0] * g[1] * value);
234  } else if (value < 1.0 - gradient_value) {
236  dist = (0.5 - value) * g[0];
237  } else {
239  dist = -0.5 * (g[0] + g[1]) + sqrt(2.0 * g[0] * g[1] * (1.0 - value));
240  }
241  return dist;
242  }
243 }
244 
245 void DistanceComputer::ComputeDistances(Data* data) {
246  const int height = static_cast<int>(data->image.GetHeight());
247  const int width = static_cast<int>(data->image.GetWidth());
248 
250  do {
251  data->any_distance_changed = false;
252 
254  for (int y = 1; y < height; ++y) {
255  data->cur_pixel[1] = y;
256 
258  for (int x = 0; x < width; ++x) {
259  data->cur_pixel[0] = x;
260  double dist = data->GetCurDistance();
261  if (dist > 0.0) {
262  UpdateDistance(data, Vector2i(0, -1), &dist);
263  if (x > 0) {
264  UpdateDistance(data, Vector2i(-1, 0), &dist);
265  UpdateDistance(data, Vector2i(-1, -1), &dist);
266  }
267  if (x < width - 1) {
268  UpdateDistance(data, Vector2i(1, -1), &dist);
269  }
270  }
271  }
272 
274  for (int x = width - 2; x >= 0; --x) {
275  data->cur_pixel[0] = x;
276  double dist = data->GetCurDistance();
277  if (dist > 0.0) {
278  UpdateDistance(data, Vector2i(1, 0), &dist);
279  }
280  }
281  }
282 
284  for (int y = height - 2; y >= 0; --y) {
285  data->cur_pixel[1] = y;
286 
288  for (int x = width - 1; x >= 0; --x) {
289  data->cur_pixel[0] = x;
290  double dist = data->GetCurDistance();
291  if (dist > 0.0) {
292  UpdateDistance(data, Vector2i(0, 1), &dist);
293  if (x > 0) {
294  UpdateDistance(data, Vector2i(-1, 1), &dist);
295  }
296  if (x < width - 1) {
297  UpdateDistance(data, Vector2i(1, 0), &dist);
298  UpdateDistance(data, Vector2i(1, 1), &dist);
299  }
300  }
301  }
302 
304  for (int x = 1; x < width; ++x) {
305  data->cur_pixel[0] = x;
306  double dist = data->GetCurDistance();
307  if (dist > 0.0) {
308  UpdateDistance(data, Vector2i(-1, 0), &dist);
309  }
310  }
311  }
312  } while (data->any_distance_changed);
313 
315  for (int y = 0; y < height; ++y) {
316  for (int x = 0; x < width; ++x) {
317  data->distances.Set(x, y, std::max(0.0, data->distances.Get(x, y)));
318  }
319  }
320 }
321 
322 void DistanceComputer::UpdateDistance(Data* data, const Vector2i& offset,
323  double* dist) {
324  const Vector2i test_pixel = data->cur_pixel + offset;
325  const Vector2i& xy_dist = data->GetDistanceToEdge(test_pixel);
326  const Vector2i edge_pixel = test_pixel - xy_dist;
327  const Vector2i new_xy_dist = xy_dist - offset;
328  const double new_dist = ComputeDistanceToEdge(data, edge_pixel,
329  Vector2d(new_xy_dist));
330  static const double kEpsilon = 1e-3;
331  if (new_dist < *dist - kEpsilon) {
332  data->SetCurDistance(new_dist);
333  data->SetCurDistanceToEdge(new_xy_dist);
334  *dist = new_dist;
335  data->any_distance_changed = true;
336  }
337 }
338 
339 double DistanceComputer::ComputeDistanceToEdge(
340  Data* data, const Vector2i& pixel, const Vector2d& vec_to_edge_pixel) {
342  const double value = math::Clamp(data->image.Get(pixel[0], pixel[1]),
343  0.0, 1.0);
344 
347  if (value == 0.0)
348  return kLargeDistance;
349 
352  const double length = math::Length(vec_to_edge_pixel);
353  const double dist =
354  length > 0.0 ?
356  ApproximateDistanceToEdge(value, vec_to_edge_pixel) :
358  ApproximateDistanceToEdge(value, data->gradients.Get(pixel[0], pixel[1]));
359 
360  return length + dist;
361 }
362 
364 
369 
370 
372 static const Grid PadGrid(const Grid& grid, size_t padding) {
373  const size_t width = grid.GetWidth();
374  const size_t height = grid.GetHeight();
375  Grid padded_grid(width + 2 * padding, height + 2 * padding, 0.0);
376  for (size_t y = 0; y < height ; ++y) {
377  for (size_t x = 0; x < width; ++x) {
378  padded_grid.Set(padding + x, padding + y, grid.Get(x, y));
379  }
380  }
381  return padded_grid;
382 }
383 
385 static const Grid InvertGrid(const Grid& grid) {
386  const size_t w = grid.GetWidth();
387  const size_t h = grid.GetHeight();
388  Grid inverted(w, h);
389  for (size_t y = 0; y < h; ++y) {
390  for (size_t x = 0; x < w; ++x) {
391  inverted.Set(x, y, 1.0 - grid.Get(x, y));
392  }
393  }
394  return inverted;
395 }
396 
399 static const Grid BuildSdfGrid(const Grid& grid) {
400  const size_t height = grid.GetHeight();
401  const size_t width = grid.GetWidth();
402  Grid sdf(width, height);
403  if (height > 0 && width > 0) {
406  DistanceComputer dc;
407  const Grid bg_distances = dc.Compute(grid);
408  const Grid fg_distances = dc.Compute(InvertGrid(grid));
409  for (size_t y = 0; y < height; ++y) {
410  for (size_t x = 0; x < width; ++x) {
411  sdf.Set(x, y, bg_distances.Get(x, y) - fg_distances.Get(x, y));
412  }
413  }
414  }
415  return sdf;
416 }
417 
418 } // anonymous namespace
419 
421 
426 
427 
428 const Grid ComputeSdfGrid(const Grid& image_grid, size_t padding) {
429  return BuildSdfGrid(PadGrid(image_grid, padding));
430 }
431 
432 } // namespace text
433 } // namespace ion
Vector2i cur_pixel
Indices of the current pixel being operated on.
Definition: sdfutils.cc:98
bool any_distance_changed
This is set to true when a value in the distances grid is modified.
Definition: sdfutils.cc:100
std::string text
double value
Grid distances
Final distance values.
Definition: sdfutils.cc:96
uint32 offset
uint32 length
const Array2< Vector2d > & gradients
Local gradients in X and Y.
Definition: sdfutils.cc:92
const T Clamp(const T &val, const T &min_val, const T &max_val)
Clamps a value to lie between a minimum and maximum, inclusive.
Definition: utils.h:204
const Grid & image
The original monochrome image data, as doubles (0 - 1).
Definition: sdfutils.cc:90
const Vector< Dimension, T > Normalized(const Vector< Dimension, T > &v)
Returns a unit-length version of a Vector.
Definition: vectorutils.h:104
Copyright 2016 Google Inc.
int width
const Grid ComputeSdfGrid(const Grid &image_grid, size_t padding)
Public SDF utility functions.
Definition: sdfutils.cc:428
const T Abs(const T &val)
Returns the absolute value of a number in a type-safe way.
Definition: utils.h:42
Array2< Vector2i > distances_to_edges
Current pixel distances in X and Y to edges.
Definition: sdfutils.cc:94
T Length(const Vector< Dimension, T > &v)
Returns the geometric length of a Vector.
Definition: vectorutils.h:70