[gimp] app: fix line art labellization.



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]