36 typedef Array2<double> Grid;
53 class DistanceComputer {
56 ~DistanceComputer() {}
63 const Grid Compute(
const Grid&
image);
69 Data(
const Grid& image_in,
const Array2<Vector2d>& gradients_in)
76 void SetCurDistance(
double dist) {
79 double GetCurDistance()
const {
82 void SetCurDistanceToEdge(
const Vector2i& dist) {
85 const Vector2i& GetDistanceToEdge(
const Vector2i& pixel)
const {
105 static const Array2<Vector2d> ComputeGradients(
const Grid&
image);
108 static const Vector2d FilterPixel(
const Grid&
image,
size_t x,
size_t y);
111 static const Grid InitializeDistanceGrid(
116 static double ApproximateDistanceToEdge(
double value,
117 const Vector2d& gradient);
120 static void ComputeDistances(Data* data);
126 static void UpdateDistance(Data* data,
const Vector2i&
offset,
double* dist);
130 static double ComputeDistanceToEdge(Data* data,
const Vector2i& pixel,
131 const Vector2d& vec_to_edge_pixel);
134 static const double kLargeDistance;
137 const double DistanceComputer::kLargeDistance = 1e6;
139 const Grid DistanceComputer::Compute(
const Grid&
image) {
141 const Array2<Vector2d>
gradients = ComputeGradients(image);
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);
150 return data.distances;
153 const Array2<Vector2d> DistanceComputer::ComputeGradients(
const Grid& image) {
154 const size_t h = image.GetHeight();
155 const size_t w = image.GetWidth();
157 Array2<Vector2d>
gradients(w, h, Vector2d::Zero());
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));
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] = {
184 { -kSqrt2, 0.0, kSqrt2 },
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;
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();
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 :
209 ApproximateDistanceToEdge(v, gradients.Get(x, y));
216 double DistanceComputer::ApproximateDistanceToEdge(
217 double value,
const Vector2d& gradient) {
218 if (gradient[0] == 0.0 || gradient[1] == 0.0) {
228 std::swap(g[0], g[1]);
229 const float gradient_value =
static_cast<float>(0.5 * g[1] / g[0]);
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];
239 dist = -0.5 * (g[0] + g[1]) + sqrt(2.0 * g[0] * g[1] * (1.0 - value));
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());
251 data->any_distance_changed =
false;
254 for (
int y = 1; y < height; ++y) {
255 data->cur_pixel[1] = y;
258 for (
int x = 0; x <
width; ++x) {
259 data->cur_pixel[0] = x;
260 double dist = data->GetCurDistance();
262 UpdateDistance(data, Vector2i(0, -1), &dist);
264 UpdateDistance(data, Vector2i(-1, 0), &dist);
265 UpdateDistance(data, Vector2i(-1, -1), &dist);
268 UpdateDistance(data, Vector2i(1, -1), &dist);
274 for (
int x = width - 2; x >= 0; --x) {
275 data->cur_pixel[0] = x;
276 double dist = data->GetCurDistance();
278 UpdateDistance(data, Vector2i(1, 0), &dist);
284 for (
int y = height - 2; y >= 0; --y) {
285 data->cur_pixel[1] = y;
288 for (
int x = width - 1; x >= 0; --x) {
289 data->cur_pixel[0] = x;
290 double dist = data->GetCurDistance();
292 UpdateDistance(data, Vector2i(0, 1), &dist);
294 UpdateDistance(data, Vector2i(-1, 1), &dist);
297 UpdateDistance(data, Vector2i(1, 0), &dist);
298 UpdateDistance(data, Vector2i(1, 1), &dist);
304 for (
int x = 1; x <
width; ++x) {
305 data->cur_pixel[0] = x;
306 double dist = data->GetCurDistance();
308 UpdateDistance(data, Vector2i(-1, 0), &dist);
312 }
while (data->any_distance_changed);
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)));
322 void DistanceComputer::UpdateDistance(Data* data,
const Vector2i&
offset,
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);
335 data->any_distance_changed =
true;
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]),
348 return kLargeDistance;
356 ApproximateDistanceToEdge(value, vec_to_edge_pixel) :
358 ApproximateDistanceToEdge(value, data->gradients.Get(pixel[0], pixel[1]));
360 return length + dist;
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));
385 static const Grid InvertGrid(
const Grid& grid) {
386 const size_t w = grid.GetWidth();
387 const size_t h = grid.GetHeight();
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));
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) {
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));
429 return BuildSdfGrid(PadGrid(image_grid, padding));
Vector2i cur_pixel
Indices of the current pixel being operated on.
bool any_distance_changed
This is set to true when a value in the distances grid is modified.
Grid distances
Final distance values.
const Array2< Vector2d > & gradients
Local gradients in X and Y.
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.
const Grid & image
The original monochrome image data, as doubles (0 - 1).
const Vector< Dimension, T > Normalized(const Vector< Dimension, T > &v)
Returns a unit-length version of a Vector.
Copyright 2016 Google Inc.
const Grid ComputeSdfGrid(const Grid &image_grid, size_t padding)
Public SDF utility functions.
const T Abs(const T &val)
Returns the absolute value of a number in a type-safe way.
Array2< Vector2i > distances_to_edges
Current pixel distances in X and Y to edges.
T Length(const Vector< Dimension, T > &v)
Returns the geometric length of a Vector.