[gimp] app: fix line art labellization.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: fix line art labellization.
- Date: Wed, 14 Nov 2018 12:42:24 +0000 (UTC)
commit 93a49951a0423658af04333820451e5a6d001edf
Author: Jehan <jehan girinstud io>
Date: Wed Oct 31 10:14:20 2018 +0100
app: fix line art labellization.
The older labelling based off CImg code was broken (probably because of
me, from my port). Anyway I realized what it was trying to do was too
generic, which is why we had to fix the result later (labeling all
non-stroke pixels as 0, etc.). Instead I just implemented a simpler
labelling and only look for stroke regions. It still over-label a bit
the painting but a lot less, and is much faster.
app/core/gimplineart.c | 264 +++++++++++++++++++++----------------------------
1 file changed, 114 insertions(+), 150 deletions(-)
---
diff --git a/app/core/gimplineart.c b/app/core/gimplineart.c
index e290590aed..28af7bab99 100644
--- a/app/core/gimplineart.c
+++ b/app/core/gimplineart.c
@@ -68,8 +68,11 @@ typedef struct _Edgel
guint next, previous;
} Edgel;
-static GeglBuffer * gimp_lineart_get_labels (GeglBuffer *line_art,
- gboolean is_high_connectivity);
+static void gimp_lineart_add_label_equivalency (GHashTable *equivalencies,
+ guint label1,
+ guint label2);
+static GeglBuffer * gimp_lineart_label (GeglBuffer *line_art,
+ guint32 *n_labels);
static void gimp_lineart_erode (GeglBuffer *buffer,
gint s);
static void gimp_lineart_denoise (GeglBuffer *buffer,
@@ -304,6 +307,7 @@ gimp_lineart_close (GeglBuffer *line_art,
if (erode_size)
gimp_lineart_erode (strokes, 2 * erode_size);
}
+
/* Denoise (remove small connected components) */
gimp_lineart_denoise (strokes, minimal_lineart_area);
@@ -459,128 +463,126 @@ gimp_lineart_close (GeglBuffer *line_art,
/* Private functions */
+static void
+gimp_lineart_add_label_equivalency (GHashTable *equivalencies,
+ guint label1,
+ guint label2)
+{
+ gpointer key = GUINT_TO_POINTER (MAX (label1, label2));
+ gpointer eq = GUINT_TO_POINTER (MIN (label1, label2));
+ gpointer old_eq = g_hash_table_lookup (equivalencies, key);
+
+ if (old_eq && old_eq != eq)
+ eq = MIN (old_eq, eq);
+
+ /* Check that the equivalent label has no equivalent itself. */
+ if ((old_eq = g_hash_table_lookup (equivalencies, eq)))
+ g_hash_table_insert (equivalencies, key, old_eq);
+ else
+ g_hash_table_insert (equivalencies, key, eq);
+}
+
+/**
+ * Label connected stroke pixels in regions, and leave all non-stroke
+ * pixels with label 0.
+ */
static GeglBuffer *
-gimp_lineart_get_labels (GeglBuffer *line_art,
- gboolean is_high_connectivity)
+gimp_lineart_label (GeglBuffer *line_art,
+ guint32 *n_labels)
{
- /*
- * Converted from CImg.get_label() code, with tolerance = 0 (used to
- * determine if two neighboring pixels belong to the same region).
- * The algorithm of connected components computation has been primarily done
- * by A. Meijster, according to the publication: 'W.H. Hesselink, A.
- * Meijster, C. Bron, "Concurrent Determination of Connected Components.",
- * In: Science of Computer Programming 41 (2001), pp. 173--194'.
- * The submitted code has then been modified to fit CImg first, then GIMP.
- */
- guint32 *data;
- gint width = gegl_buffer_get_width (line_art);
- gint height = gegl_buffer_get_height (line_art);
- guint32 counter = 0;
- guint32 p = 0;
-
- /* Create neighborhood tables. */
- int dx[4], dy[4];
-
- dx[0] = 1; dy[0] = 0;
- dx[1] = 0; dy[1] = 1;
- if (is_high_connectivity)
- {
- dx[2] = 1; dy[2] = 1;
- dx[3] = 1; dy[3] = -1;
- }
+ GeglBufferIterator *gi;
+ guint *labels;
+ guint *label;
+ GHashTable *equivalencies;
+ gint width = gegl_buffer_get_width (line_art);
+ gint height = gegl_buffer_get_height (line_art);
+ gint x;
+ gint y;
+
+ equivalencies = g_hash_table_new (NULL, NULL);
- data = g_new (guint32,
- babl_format_get_bytes_per_pixel (babl_format_n (babl_type ("u32"), 1)) * width * height);
+ labels = g_new (guint, sizeof (guint) * width * height);
- /* Init label numbers. */
- for (guint32 i = 0; i < width * height; i++)
- data[i] = i;
+ gi = gegl_buffer_iterator_new (line_art, gegl_buffer_get_extent (line_art),
+ 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
- /* For each neighbour-direction, label. */
- for (unsigned int n = 0; n < (is_high_connectivity ? 4 : 2); ++n)
+ *n_labels = 0;
+ while (gegl_buffer_iterator_next (gi))
{
- GeglBufferIterator *gi;
- const gint _dx = dx[n];
- const gint _dy = dy[n];
-
- const gint y0 = (_dy < 0) ? -_dy : 0;
- const gint it_width = width - _dx + 1;
- const gint it_height = (_dy < 0) ? height - y0 + 1: height - _dy - y0 + 1;
- const glong offset = _dy * width + _dx;
-
- gi = gegl_buffer_iterator_new (line_art, GEGL_RECTANGLE (0, y0, it_width, it_height),
- 0, babl_format ("Y u32"),
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
- gegl_buffer_iterator_add (gi, line_art, GEGL_RECTANGLE (_dx, y0 + _dy, it_width, it_height),
- 0, babl_format ("Y u32"),
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- while (gegl_buffer_iterator_next (gi))
- {
- GeglRectangle *roi = &gi->items[0].roi;
- guint32 *pixel = (guint32*) gi->items[0].data;
- guint32 *neighbour = (guint32*) gi->items[1].data;
- gint k;
- gint x = roi->x;
- gint y = roi->y;
+ guint8 *stroke = (guint8*) gi->items[0].data;
+ gint startx = gi->items[0].roi.x;
+ gint starty = gi->items[0].roi.y;
+ gint endy = starty + gi->items[0].roi.height;
+ gint endx = startx + gi->items[0].roi.width;
- for (k = 0; k < gi->length; k++)
- {
- if (pixel == neighbour)
- {
- const glong p = width * y;
- const guint32 q = p + offset;
- guint32 i, j;
+ for (y = starty; y < endy; y++)
+ for (x = startx; x < endx; x++)
+ {
+ label = labels + y * width + x;
- for (i = MAX (p, q), j = MIN (p, q); i != j && data[i] != i; )
- {
- i = (guint32) data[i];
- if (i < j)
- {
- /* Swap i and j. */
- guint32 temp = i;
- i = j;
- j = temp;
- }
- }
- if (i != j)
- data[i] = j;
- for (guint32 _p = (guint32) p; _p != j; )
- {
- const guint32 h = (guint32) data[_p];
+ *label = 0;
+ if (*stroke)
+ {
+ if (x > 0 && y > 0)
+ {
+ guint *pxy = label - width - 1;
- data[_p] = (guint32) j;
- _p = h;
- }
- for (guint32 _q = (guint32) q; _q != j; )
- {
- const guint32 h = (guint32) data[_q];
+ *label = *pxy;
+ }
+ if (y > 0)
+ {
+ guint *py = label - width;
- data[_q] = (guint32) j;
- _q = h;
- }
- }
- pixel++;
- neighbour++;
+ if (! *label)
+ *label = *py;
+ else if (*py && *label != *py)
+ gimp_lineart_add_label_equivalency (equivalencies,
+ *label, *py);
+ }
+ if (y > 0 && x < width - 1)
+ {
+ guint *py_nx = label - width + 1;
- x++;
- if (x - roi->x >= roi->width)
- {
- x = roi->x;
- y++;
- }
- }
- }
- }
+ if (! *label)
+ *label = *py_nx;
+ else if (*py_nx && *label != *py_nx)
+ gimp_lineart_add_label_equivalency (equivalencies,
+ *label, *py_nx);
+ }
+ if (x > 0)
+ {
+ guint *px = label - 1;
- /* Resolve equivalences. */
- p = 0;
- for (guint32 i = 0; i < width * height; i++)
- {
- data[i] = data[i] == p ? counter++ : data[data[i]];
- p++;
+ if (! *label)
+ *label = *px;
+ else if (*px && *label != *px)
+ gimp_lineart_add_label_equivalency (equivalencies,
+ *label, *px);
+ }
+ if (! *label)
+ *label = ++(*n_labels);
+ }
+ stroke++;
+ }
}
- return gegl_buffer_linear_new_from_data (data,
+ label = labels;
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ if (*label > 1)
+ {
+ gpointer eq = g_hash_table_lookup (equivalencies,
+ GINT_TO_POINTER (*label));
+
+ if (eq)
+ *label = GPOINTER_TO_INT (eq);
+ }
+ label++;
+ }
+ g_hash_table_destroy (equivalencies);
+
+ return gegl_buffer_linear_new_from_data (labels,
babl_format_n (babl_type ("u32"), 1),
gegl_buffer_get_extent (line_art), 0,
g_free, NULL);
@@ -1715,22 +1717,8 @@ gimp_lineart_estimate_stroke_width (GeglBuffer* mask)
gegl_node_process (sink);
g_object_unref (graph);
- labels = gimp_lineart_get_labels (mask, TRUE);
-
- /* Check biggest label. */
- gi = gegl_buffer_iterator_new (labels, NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
- while (gegl_buffer_iterator_next (gi))
- {
- guint32 *data = (guint32*) gi->items[0].data;
- gint k;
+ labels = gimp_lineart_label (mask, &label_max);
- for (k = 0; k < gi->length; k++)
- {
- label_max = MAX (*data, label_max);
- data++;
- }
- }
if (label_max == 0)
{
g_object_unref (labels);
@@ -1738,30 +1726,6 @@ gimp_lineart_estimate_stroke_width (GeglBuffer* mask)
return 0.0;
}
- /* Make sure that stroke pixels are label 0. */
- label_max++;
- gi = gegl_buffer_iterator_new (mask, NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
- gegl_buffer_iterator_add (gi, labels, NULL, 0,
- babl_format_n (babl_type ("u32"), 1),
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
- while (gegl_buffer_iterator_next (gi))
- {
- guint8 *m = (guint8*) gi->items[0].data;
- guint32 *l = (guint32*) gi->items[1].data;
- gint k;
-
- for (k = 0; k < gi->length; k++)
- {
- if (! *m)
- *l = 0;
- else if (*l == 0)
- *l = label_max;
- m++;
- l++;
- }
- }
-
/* Create an array of max distance per label */
dmax = g_array_sized_new (FALSE, TRUE, sizeof (gfloat), label_max);
g_array_set_size (dmax, label_max);
@@ -1807,7 +1771,7 @@ gimp_lineart_estimate_stroke_width (GeglBuffer* mask)
g_object_unref (labels);
g_object_unref (distmap);
- return 2.0 * res;
+ return 1.5 * res;
}
static guint
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]