[gegl] noise-hsv: Add CL implementation and tests



commit be32baa1d7f6524159748c7f492cf93fb4b6f822
Author: Anton Gorenko <anton streamcomputing eu>
Date:   Thu May 11 11:26:00 2017 +0600

    noise-hsv: Add CL implementation and tests
    
    (pippin: removed test and associated (1MB!) test image, before checking in to
    GEGL repo, avoiding increasing the initial clone for a full source history of
    GEGL by 1mb - such testing is now covered by the reference compositions that
    only store the hash of the resulting raster instead of a full raster.)

 opencl/noise-hsv.cl           |  120 ++++++++++++++++++++++++++++++++++++++++
 opencl/noise-hsv.cl.h         |  122 +++++++++++++++++++++++++++++++++++++++++
 operations/common/noise-hsv.c |   80 +++++++++++++++++++++++++++
 3 files changed, 322 insertions(+), 0 deletions(-)
---
diff --git a/opencl/noise-hsv.cl b/opencl/noise-hsv.cl
new file mode 100644
index 0000000..3cff79a
--- /dev/null
+++ b/opencl/noise-hsv.cl
@@ -0,0 +1,120 @@
+/* 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/>.
+ *
+ */
+
+float
+randomize_value (__global const int        *random_data,
+                          const GeglRandom  rand,
+                                float       now,
+                                float       min_val,
+                                float       max_val,
+                                bool        wraps_around,
+                                float       rand_max,
+                                int         holdness,
+                                int         x,
+                                int         y,
+                                int         n)
+{
+  const float steps = max_val - min_val;
+  float rand_val = gegl_cl_random_float (random_data, rand, x, y, 0, n++);
+
+  for (int i = 1; i < holdness; i++)
+    {
+      float tmp = gegl_cl_random_float (random_data, rand, x, y, 0, n++);
+      rand_val = min (rand_val, tmp);
+    }
+
+  const int flag = (gegl_cl_random_float (random_data, rand, x, y, 0, n) < 0.5)
+                   ? -1 : 1;
+  float new_val = now + flag * fmod (rand_max * rand_val, steps);
+
+  if (new_val < min_val)
+    {
+      if (wraps_around)
+        new_val += steps;
+      else
+        new_val = min_val;
+    }
+
+  if (max_val < new_val)
+    {
+      if (wraps_around)
+        new_val -= steps;
+      else
+        new_val = max_val;
+    }
+
+  return new_val;
+}
+
+__kernel void cl_noise_hsv(__global const float4    *in,
+                           __global       float4    *out,
+                           __global const int       *random_data,
+                                    const GeglRandom rand,
+                                          int        x_offset,
+                                          int        y_offset,
+                                          int        roi_width,
+                                          int        whole_region_width,
+                                          int        holdness,
+                                          float      hue_distance,
+                                          float      saturation_distance,
+                                          float      value_distance)
+{
+  const int gid  = get_global_id(0);
+  const int gidy = gid / roi_width;
+  const int gidx = gid - gidy * roi_width;
+
+  const int x = gidx + x_offset;
+  const int y = gidy + y_offset;
+
+  int n = (3 * holdness + 4) * (x + whole_region_width * y);
+
+  const float4 in_v = in[gid];
+
+  float hue        = in_v.s0;
+  float saturation = in_v.s1;
+  float value      = in_v.s2;
+  float alpha      = in_v.s3;
+
+  /* there is no need for scattering hue of desaturated pixels here */
+  if ((hue_distance > 0) && (saturation > 0))
+    hue = randomize_value (random_data, rand,
+                           hue, 0.0, 1.0, true,
+                           hue_distance, holdness,
+                           x, y, n);
+
+  n += holdness + 1;
+  if (saturation_distance > 0)
+    {
+      /* desaturated pixels get random hue before increasing saturation */
+      if (saturation == 0)
+        hue = gegl_cl_random_float_range (random_data, rand,
+                                          x, y, 0, n, 0.0, 1.0);
+      saturation = randomize_value (random_data, rand,
+                                    saturation, 0.0, 1.0, false,
+                                    saturation_distance, holdness,
+                                    x, y, n + 1);
+    }
+
+  n += holdness + 2;
+  if (value_distance > 0)
+    value = randomize_value (random_data, rand,
+                             value, 0.0, 1.0, false,
+                             value_distance, holdness,
+                             x, y, n);
+
+  out[gid] = (float4)(hue, saturation, value, alpha);
+}
diff --git a/opencl/noise-hsv.cl.h b/opencl/noise-hsv.cl.h
new file mode 100644
index 0000000..528be47
--- /dev/null
+++ b/opencl/noise-hsv.cl.h
@@ -0,0 +1,122 @@
+static const char* noise_hsv_cl_source =
+"/* This file is an image processing operation for GEGL                        \n"
+" *                                                                            \n"
+" * GEGL is free software; you can redistribute it and/or                      \n"
+" * modify it under the terms of the GNU Lesser General Public                 \n"
+" * License as published by the Free Software Foundation; either               \n"
+" * version 3 of the License, or (at your option) any later version.           \n"
+" *                                                                            \n"
+" * GEGL is distributed in the hope that it will be useful,                    \n"
+" * but WITHOUT ANY WARRANTY; without even the implied warranty of             \n"
+" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU          \n"
+" * Lesser General Public License for more details.                            \n"
+" *                                                                            \n"
+" * You should have received a copy of the GNU Lesser General Public           \n"
+" * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.       \n"
+" *                                                                            \n"
+" */                                                                           \n"
+"                                                                              \n"
+"float                                                                         \n"
+"randomize_value (__global const int        *random_data,                      \n"
+"                          const GeglRandom  rand,                             \n"
+"                                float       now,                              \n"
+"                                float       min_val,                          \n"
+"                                float       max_val,                          \n"
+"                                bool        wraps_around,                     \n"
+"                                float       rand_max,                         \n"
+"                                int         holdness,                         \n"
+"                                int         x,                                \n"
+"                                int         y,                                \n"
+"                                int         n)                                \n"
+"{                                                                             \n"
+"  const float steps = max_val - min_val;                                      \n"
+"  float rand_val = gegl_cl_random_float (random_data, rand, x, y, 0, n++);    \n"
+"                                                                              \n"
+"  for (int i = 1; i < holdness; i++)                                          \n"
+"    {                                                                         \n"
+"      float tmp = gegl_cl_random_float (random_data, rand, x, y, 0, n++);     \n"
+"      rand_val = min (rand_val, tmp);                                         \n"
+"    }                                                                         \n"
+"                                                                              \n"
+"  const int flag = (gegl_cl_random_float (random_data, rand, x, y, 0, n) < 0.5)\n"
+"                   ? -1 : 1;                                                  \n"
+"  float new_val = now + flag * fmod (rand_max * rand_val, steps);             \n"
+"                                                                              \n"
+"  if (new_val < min_val)                                                      \n"
+"    {                                                                         \n"
+"      if (wraps_around)                                                       \n"
+"        new_val += steps;                                                     \n"
+"      else                                                                    \n"
+"        new_val = min_val;                                                    \n"
+"    }                                                                         \n"
+"                                                                              \n"
+"  if (max_val < new_val)                                                      \n"
+"    {                                                                         \n"
+"      if (wraps_around)                                                       \n"
+"        new_val -= steps;                                                     \n"
+"      else                                                                    \n"
+"        new_val = max_val;                                                    \n"
+"    }                                                                         \n"
+"                                                                              \n"
+"  return new_val;                                                             \n"
+"}                                                                             \n"
+"                                                                              \n"
+"__kernel void cl_noise_hsv(__global const float4    *in,                      \n"
+"                           __global       float4    *out,                     \n"
+"                           __global const int       *random_data,             \n"
+"                                    const GeglRandom rand,                    \n"
+"                                          int        x_offset,                \n"
+"                                          int        y_offset,                \n"
+"                                          int        roi_width,               \n"
+"                                          int        whole_region_width,      \n"
+"                                          int        holdness,                \n"
+"                                          float      hue_distance,            \n"
+"                                          float      saturation_distance,     \n"
+"                                          float      value_distance)          \n"
+"{                                                                             \n"
+"  const int gid  = get_global_id(0);                                          \n"
+"  const int gidy = gid / roi_width;                                           \n"
+"  const int gidx = gid - gidy * roi_width;                                    \n"
+"                                                                              \n"
+"  const int x = gidx + x_offset;                                              \n"
+"  const int y = gidy + y_offset;                                              \n"
+"                                                                              \n"
+"  int n = (3 * holdness + 4) * (x + whole_region_width * y);                  \n"
+"                                                                              \n"
+"  const float4 in_v = in[gid];                                                \n"
+"                                                                              \n"
+"  float hue        = in_v.s0;                                                 \n"
+"  float saturation = in_v.s1;                                                 \n"
+"  float value      = in_v.s2;                                                 \n"
+"  float alpha      = in_v.s3;                                                 \n"
+"                                                                              \n"
+"  /* there is no need for scattering hue of desaturated pixels here */        \n"
+"  if ((hue_distance > 0) && (saturation > 0))                                 \n"
+"    hue = randomize_value (random_data, rand,                                 \n"
+"                           hue, 0.0, 1.0, true,                               \n"
+"                           hue_distance, holdness,                            \n"
+"                           x, y, n);                                          \n"
+"                                                                              \n"
+"  n += holdness + 1;                                                          \n"
+"  if (saturation_distance > 0)                                                \n"
+"    {                                                                         \n"
+"      /* desaturated pixels get random hue before increasing saturation */    \n"
+"      if (saturation == 0)                                                    \n"
+"        hue = gegl_cl_random_float_range (random_data, rand,                  \n"
+"                                          x, y, 0, n, 0.0, 1.0);              \n"
+"      saturation = randomize_value (random_data, rand,                        \n"
+"                                    saturation, 0.0, 1.0, false,              \n"
+"                                    saturation_distance, holdness,            \n"
+"                                    x, y, n + 1);                             \n"
+"    }                                                                         \n"
+"                                                                              \n"
+"  n += holdness + 2;                                                          \n"
+"  if (value_distance > 0)                                                     \n"
+"    value = randomize_value (random_data, rand,                               \n"
+"                             value, 0.0, 1.0, false,                          \n"
+"                             value_distance, holdness,                        \n"
+"                             x, y, n);                                        \n"
+"                                                                              \n"
+"  out[gid] = (float4)(hue, saturation, value, alpha);                         \n"
+"}                                                                             \n"
+;
diff --git a/operations/common/noise-hsv.c b/operations/common/noise-hsv.c
index fc5455d..d6fe9c2 100644
--- a/operations/common/noise-hsv.c
+++ b/operations/common/noise-hsv.c
@@ -178,6 +178,84 @@ process (GeglOperation       *operation,
   return TRUE;
 }
 
+#include "opencl/gegl-cl.h"
+#include "opencl/noise-hsv.cl.h"
+
+static GeglClRunData *cl_data = NULL;
+
+static gboolean
+cl_process (GeglOperation       *operation,
+            cl_mem               in,
+            cl_mem               out,
+            size_t               global_worksize,
+            const GeglRectangle *roi,
+            gint                 level)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  GeglRectangle *wr = gegl_operation_source_get_bounding_box (operation,
+                                                              "input");
+  cl_int      cl_err           = 0;
+  cl_mem      cl_random_data   = NULL;
+  cl_int      x_offset         = roi->x;
+  cl_int      y_offset         = roi->y;
+  cl_int      roi_width        = roi->width;
+  cl_int      wr_width         = wr->width;
+  cl_ushort4  rand;
+  cl_int      holdness;
+  cl_float    hue_distance;
+  cl_float    saturation_distance;
+  cl_float    value_distance;
+
+  gegl_cl_random_get_ushort4 (o->rand, &rand);
+
+  if (!cl_data)
+    {
+      const char *kernel_name[] = { "cl_noise_hsv", NULL };
+      cl_data = gegl_cl_compile_and_build (noise_hsv_cl_source, kernel_name);
+    }
+
+  if (!cl_data)
+    return TRUE;
+
+  cl_random_data = gegl_cl_load_random_data (&cl_err);
+  CL_CHECK;
+
+  holdness = o->holdness;
+  hue_distance = o->hue_distance / 360.0;
+  saturation_distance = o->saturation_distance;
+  value_distance = o->value_distance;
+
+  gegl_cl_set_kernel_args (cl_data->kernel[0],
+                           sizeof(cl_mem),     &in,
+                           sizeof(cl_mem),     &out,
+                           sizeof(cl_mem),     &cl_random_data,
+                           sizeof(cl_ushort4), &rand,
+                           sizeof(cl_int),     &x_offset,
+                           sizeof(cl_int),     &y_offset,
+                           sizeof(cl_int),     &roi_width,
+                           sizeof(cl_int),     &wr_width,
+                           sizeof(cl_int),     &holdness,
+                           sizeof(cl_float),   &hue_distance,
+                           sizeof(cl_float),   &saturation_distance,
+                           sizeof(cl_float),   &value_distance,
+                           NULL);
+
+  CL_CHECK;
+  cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
+                                        cl_data->kernel[0], 1,
+                                        NULL, &global_worksize, NULL,
+                                        0, NULL, NULL);
+  CL_CHECK;
+
+  cl_err = gegl_clFinish (gegl_cl_get_command_queue ());
+  CL_CHECK;
+
+  return  FALSE;
+
+error:
+  return TRUE;
+}
+
 static void
 gegl_op_class_init (GeglOpClass *klass)
 {
@@ -188,7 +266,9 @@ gegl_op_class_init (GeglOpClass *klass)
   point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
 
   operation_class->prepare = prepare;
+  operation_class->opencl_support = TRUE;
   point_filter_class->process = process;
+  point_filter_class->cl_process = cl_process;
 
   gegl_operation_class_set_keys (operation_class,
     "name",       "gegl:noise-hsv",


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]