[gegl] workshop: add slic operation
- From: Thomas Manni <tmanni src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] workshop: add slic operation
- Date: Mon, 30 Jan 2017 15:37:48 +0000 (UTC)
commit 2963fd522c0e8307622fbc71f0593c1d28fa4572
Author: Thomas Manni <thomas manni free fr>
Date: Sat Jan 21 12:35:24 2017 +0100
workshop: add slic operation
operations/workshop/Makefile.am | 7 +-
operations/workshop/slic.c | 459 +++++++++++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
3 files changed, 464 insertions(+), 3 deletions(-)
---
diff --git a/operations/workshop/Makefile.am b/operations/workshop/Makefile.am
index 85279aa..7569c28 100644
--- a/operations/workshop/Makefile.am
+++ b/operations/workshop/Makefile.am
@@ -25,8 +25,9 @@ op_LTLIBRARIES = \
median-blur.la \
rawbayer-load.la \
segment-kmeans.la \
+ slic.la \
unpremul.la \
- vhsfix.la \
- waterpixels.la \
- watershed-transform.la
+ vhsfix.la \
+ waterpixels.la \
+ watershed-transform.la
diff --git a/operations/workshop/slic.c b/operations/workshop/slic.c
new file mode 100644
index 0000000..f8ab204
--- /dev/null
+++ b/operations/workshop/slic.c
@@ -0,0 +1,459 @@
+/* This file is an image processing operation for GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SLIC (Simple Linear Iterative Clustering)
+ * Superpixels based on k-means clustering
+ *
+ * Copyright 2017 Thomas Manni <thomas manni free fr>
+ *
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+property_int (cluster_size, _("Regions size"), 32)
+ description (_("Size of a region side"))
+ value_range (2, G_MAXINT)
+ ui_range (2, 1024)
+
+property_int (compactness, _("Compactness"), 20)
+ description (_("Cluster size"))
+ value_range (1, 40)
+ ui_range (1, 40)
+
+property_int (iterations, _("Iterations"), 1)
+ description (_("Number of iterations"))
+ value_range (1, 30)
+ ui_range (1, 15)
+
+#else
+
+#define GEGL_OP_FILTER
+#define GEGL_OP_NAME slic
+#define GEGL_OP_C_SOURCE slic.c
+
+#include "gegl-op.h"
+#include <math.h>
+
+#define POW2(x) ((x)*(x))
+
+typedef struct
+{
+ gfloat center[5];
+ gfloat sum[5];
+ glong n_pixels;
+ GeglRectangle search_window;
+} Cluster;
+
+
+static inline gfloat
+get_distance (gfloat *c1,
+ gfloat *c2,
+ GeglProperties *o)
+{
+ gfloat color_dist = sqrtf (POW2(c2[0] - c1[0]) +
+ POW2(c2[1] - c1[1]) +
+ POW2(c2[2] - c1[2]));
+
+ gfloat spacial_dist = sqrtf (POW2(c2[3] - c1[3]) +
+ POW2(c2[4] - c1[4]));
+
+ return sqrtf (POW2(color_dist) +
+ POW2(o->compactness) * POW2(spacial_dist / o->cluster_size));
+}
+
+static GArray *
+init_clusters (GeglBuffer *input,
+ GeglProperties *o,
+ gint level)
+{
+ GeglSampler *sampler;
+ GArray *clusters;
+ gint n_clusters;
+ gint i, x, y;
+ gint cx, cy;
+ gint h_offset, v_offset;
+ gint width = gegl_buffer_get_width (input);
+ gint height = gegl_buffer_get_height (input);
+
+ gint n_h_clusters = width / o->cluster_size;
+ gint n_v_clusters = height / o->cluster_size;
+
+ if (width % o->cluster_size)
+ n_h_clusters++;
+
+ if (height % o->cluster_size)
+ n_v_clusters++;
+
+ h_offset = (width % o->cluster_size) ? (width % o->cluster_size) / 2 : o->cluster_size / 2;
+ v_offset = (height % o->cluster_size) ? (height % o->cluster_size) / 2 : o->cluster_size / 2;
+
+ n_clusters = n_h_clusters * n_v_clusters;
+
+ clusters = g_array_sized_new (FALSE, TRUE, sizeof (Cluster), n_clusters);
+
+ sampler = gegl_buffer_sampler_new_at_level (input,
+ babl_format ("CIE Lab float"),
+ GEGL_SAMPLER_NEAREST, level);
+ x = y = 0;
+
+ for (i = 0; i < n_clusters; i++)
+ {
+ gfloat pixel[3];
+ Cluster c;
+
+ cx = x * o->cluster_size + h_offset;
+ cy = y * o->cluster_size + v_offset;
+
+ gegl_sampler_get (sampler, cx, cy, NULL,
+ pixel, GEGL_ABYSS_CLAMP);
+
+ c.center[0] = pixel[0];
+ c.center[1] = pixel[1];
+ c.center[2] = pixel[2];
+ c.center[3] = (gfloat) cx;
+ c.center[4] = (gfloat) cy;
+
+ c.sum[0] = 0.0;
+ c.sum[1] = 0.0;
+ c.sum[2] = 0.0;
+ c.sum[3] = 0.0;
+ c.sum[4] = 0.0;
+
+ c.n_pixels = 0;
+
+ c.search_window.x = cx - o->cluster_size;
+ c.search_window.y = cy - o->cluster_size;
+ c.search_window.width =
+ c.search_window.height = o->cluster_size * 2 + 1;
+
+ g_array_append_val (clusters, c);
+
+ x++;
+ if (x >= n_h_clusters)
+ {
+ x = 0;
+ y++;
+ }
+ }
+
+ g_object_unref (sampler);
+
+ return clusters;
+}
+
+static void
+assign_labels (GeglBuffer *labels,
+ GeglBuffer *input,
+ GArray *clusters,
+ GeglProperties *o)
+{
+ GeglBufferIterator *iter;
+ GArray *clusters_index;
+
+ clusters_index = g_array_sized_new (FALSE, FALSE, sizeof (gint), 9);
+
+ iter = gegl_buffer_iterator_new (input, NULL, 0,
+ babl_format ("CIE Lab float"),
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ gegl_buffer_iterator_add (iter, labels, NULL, 0,
+ babl_format_n (babl_type ("u32"), 1),
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *pixel = iter->data[0];
+ guint32 *label = iter->data[1];
+ glong n_pixels = iter->length;
+ gint x, y, i;
+
+ x = iter->roi->x;
+ y = iter->roi->y;
+
+ /* construct an array of clusters index for which search_window
+ * intersect with the current roi
+ */
+
+ for (i = 0; i < clusters->len ; i++)
+ {
+ Cluster *c = &g_array_index (clusters, Cluster, i);
+
+ if (gegl_rectangle_intersect (NULL, &c->search_window, iter->roi))
+ g_array_append_val (clusters_index, i);
+ }
+
+ if (!clusters_index->len)
+ {
+ g_printerr ("no clusters for roi %d,%d,%d,%d\n", iter->roi->x, iter->roi->y, iter->roi->width,
iter->roi->height);
+ continue;
+ }
+
+ while (n_pixels--)
+ {
+ Cluster *c;
+ gfloat feature[5] = {pixel[0], pixel[1], pixel[2],
+ (gfloat) x, (gfloat) y};
+
+ /* find the nearest cluster */
+
+ gfloat min_distance = G_MAXFLOAT;
+ gint best_cluster = *label;
+
+ for (i = 0; i < clusters_index->len ; i++)
+ {
+ gfloat distance;
+ gint index = g_array_index (clusters_index, gint, i);
+ Cluster *tmp = &g_array_index (clusters, Cluster, index);
+
+ if (x < tmp->search_window.x ||
+ y < tmp->search_window.y ||
+ x >= tmp->search_window.x + tmp->search_window.width ||
+ y >= tmp->search_window.y + tmp->search_window.height)
+ continue;
+
+ distance = get_distance (tmp->center, feature, o);
+
+ if (distance < min_distance)
+ {
+ min_distance = distance;
+ best_cluster = index;
+ }
+ }
+
+ c = &g_array_index (clusters, Cluster, best_cluster);
+ c->sum[0] += pixel[0];
+ c->sum[1] += pixel[1];
+ c->sum[2] += pixel[2];
+ c->sum[3] += (gfloat) x;
+ c->sum[4] += (gfloat) y;
+ c->n_pixels++;
+
+ g_assert (best_cluster != -1);
+
+ *label = best_cluster;
+
+ pixel += 3;
+ label++;
+
+ x++;
+ if (x >= iter->roi->x + iter->roi->width)
+ {
+ y++;
+ x = iter->roi->x;
+ }
+ }
+
+ clusters_index->len = 0;
+ }
+
+ g_array_free (clusters_index, TRUE);
+}
+
+static gboolean
+update_clusters (GArray *clusters,
+ GeglProperties *o)
+{
+ gint i;
+
+ for (i = 0; i < clusters->len; i++)
+ {
+ Cluster *c = &g_array_index (clusters, Cluster, i);
+
+ c->center[0] = c->sum[0] / c->n_pixels;
+ c->center[1] = c->sum[1] / c->n_pixels;
+ c->center[2] = c->sum[2] / c->n_pixels;
+ c->center[3] = c->sum[3] / c->n_pixels;
+ c->center[4] = c->sum[4] / c->n_pixels;
+
+ c->sum[0] = 0.0f;
+ c->sum[1] = 0.0f;
+ c->sum[2] = 0.0f;
+ c->sum[3] = 0.0f;
+ c->sum[4] = 0.0f;
+
+ c->n_pixels = 0;
+
+ c->search_window.x = (gint) c->center[3] - o->cluster_size;
+ c->search_window.y = (gint) c->center[4] - o->cluster_size;
+ }
+
+ return TRUE;
+}
+
+static void
+set_output (GeglBuffer *output,
+ GeglBuffer *labels,
+ GArray *clusters)
+{
+ GeglBufferIterator *iter;
+
+ iter = gegl_buffer_iterator_new (output, NULL, 0,
+ babl_format ("CIE Lab float"),
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ gegl_buffer_iterator_add (iter, labels, NULL, 0,
+ babl_format_n (babl_type ("u32"), 1),
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *pixel = iter->data[0];
+ guint32 *label = iter->data[1];
+ glong n_pixels = iter->length;
+
+ while (n_pixels--)
+ {
+ Cluster *c = &g_array_index (clusters, Cluster, *label);
+
+ pixel[0] = c->center[0];
+ pixel[1] = c->center[1];
+ pixel[2] = c->center[2];
+
+ pixel += 3;
+ label++;
+ }
+ }
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+ const Babl *format = babl_format ("CIE Lab float");
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static GeglRectangle
+get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
+
+ /* Don't request an infinite plane */
+ if (gegl_rectangle_is_infinite_plane (&result))
+ return *roi;
+
+ return result;
+}
+
+static GeglRectangle
+get_cached_region (GeglOperation *operation,
+ const GeglRectangle *roi)
+{
+ GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
+
+ if (gegl_rectangle_is_infinite_plane (&result))
+ return *roi;
+
+ return result;
+}
+
+static gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+
+ const GeglRectangle *src_region = gegl_buffer_get_extent (input);
+ GeglBuffer *labels;
+ GArray *clusters;
+ gint i;
+
+ labels = gegl_buffer_new (src_region, babl_format_n (babl_type ("u32"), 1));
+
+ /* clusters initialization */
+
+ clusters = init_clusters (input, o, level);
+
+ /* perform segmentation */
+
+ for (i = 0; i < o->iterations; i++)
+ {
+ assign_labels (labels, input, clusters, o);
+
+ update_clusters (clusters, o);
+ }
+
+ /* apply clusters colors to output */
+
+ set_output (output, labels, clusters);
+
+ g_object_unref (labels);
+ g_array_free (clusters, TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+operation_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level)
+{
+ GeglOperationClass *operation_class;
+
+ const GeglRectangle *in_rect =
+ gegl_operation_source_get_bounding_box (operation, "input");
+
+ operation_class = GEGL_OPERATION_CLASS (gegl_op_parent_class);
+
+ if (in_rect && gegl_rectangle_is_infinite_plane (in_rect))
+ {
+ gpointer in = gegl_operation_context_get_object (context, "input");
+ gegl_operation_context_take_object (context, "output",
+ g_object_ref (G_OBJECT (in)));
+ return TRUE;
+ }
+
+ return operation_class->process (operation, context, output_prop, result,
+ gegl_operation_context_get_level (context));
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+ GeglOperationClass *operation_class;
+ GeglOperationFilterClass *filter_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+ filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ filter_class->process = process;
+ operation_class->prepare = prepare;
+ operation_class->process = operation_process;
+ operation_class->get_required_for_output = get_required_for_output;
+ operation_class->get_cached_region = get_cached_region;
+ operation_class->opencl_support = FALSE;
+ operation_class->threaded = FALSE;
+ operation_class->want_in_place = FALSE;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gegl:slic",
+ "title", _("Simple Linear Iterative Clustering"),
+ "categories", "color:segmentation",
+ "description", "Superpixels based on k-means clustering",
+ NULL);
+}
+
+#endif
\ No newline at end of file
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e8b3a3b..4044518 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -244,6 +244,7 @@ operations/workshop/mandelbrot.c
operations/workshop/median-blur.c
operations/workshop/rawbayer-load.c
operations/workshop/segment-kmeans.c
+operations/workshop/slic.c
operations/workshop/vhsfix.c
operations/workshop/waterpixels.c
operations/workshop/watershed-transform.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]