c2g.c 12.6 KB
Newer Older
Øyvind Kolås's avatar
Øyvind Kolås committed
1
/* STRESS, Spatio Temporal Retinex Envelope with Stochastic Sampling
2 3 4 5
 *
 * 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
6
 * version 3 of the License, or (at your option) any later version.
7 8 9 10 11 12 13
 *
 * 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
14
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
15
 *
16 17 18
 * Copyright 2007,2009 Øyvind Kolås     <pippin@gimp.org>
 *                     Ivar Farup       <ivarf@hig.no>
 *                     Allesandro Rizzi <rizzi@dti.unimi.it>
19 20
 */

21 22 23 24
#include "config.h"
#include <glib/gi18n-lib.h>


25
#ifdef GEGL_PROPERTIES
26

27 28
property_int (radius, _("Radius"), 300)
  description(_("Neighborhood taken into account, this is the radius "
29 30
                     "in pixels taken into account when deciding which "
                     "colors map to which gray values"))
31 32 33 34 35 36 37 38 39 40 41 42
  value_range (2, 3000)
  ui_range    (2, 1000)
  ui_gamma    (1.6)
  ui_meta     ("unit", "pixel-distance")

property_int  (samples, _("Samples"), 4)
  description (_("Number of samples to do per iteration looking for the range of colors"))
  value_range (1, 1000) 
  ui_range    (1, 20)

property_int (iterations, _("Iterations"), 10)
  description(_("Number of iterations, a higher number of iterations "
43
                     "provides less noisy results at a computational cost"))
44 45
  value_range (1, 1000)
  ui_range (1, 20)
46 47

/*
48
property_double (rgamma, _("Radial Gamma"), 0.0, 8.0, 2.0,
49 50
                _("Gamma applied to radial distribution"))
*/
Kevin Cozens's avatar
Kevin Cozens committed
51
#else
52

53 54
#define GEGL_OP_AREA_FILTER
#define GEGL_OP_C_FILE "c2g.c"
55

56
#include "gegl-op.h"
57
#include <math.h>
Kevin Cozens's avatar
Kevin Cozens committed
58 59 60
#include <stdlib.h>
#include "envelopes.h"

61 62
#define RGAMMA 2.0

63 64 65 66 67 68 69 70
static void c2g (GeglBuffer          *src,
                 const GeglRectangle *src_rect,
                 GeglBuffer          *dst,
                 const GeglRectangle *dst_rect,
                 gint                 radius,
                 gint                 samples,
                 gint                 iterations,
                 gdouble              rgamma)
71 72
{
  gint x,y;
Øyvind Kolås's avatar
Øyvind Kolås committed
73
  gint    dst_offset=0;
74 75
  gfloat *src_buf;
  gfloat *dst_buf;
76 77 78
  gint    inw = src_rect->width;
  gint    outw = dst_rect->width;
  gint    inh = src_rect->height;
79

80 81
  src_buf = g_new0 (gfloat, src_rect->width * src_rect->height * 4);
  dst_buf = g_new0 (gfloat, dst_rect->width * dst_rect->height * 2);
82

83 84
  gegl_buffer_get (src, src_rect, 1.0, babl_format ("RGBA float"), src_buf, GEGL_AUTO_ROWSTRIDE,
                   GEGL_ABYSS_NONE);
Øyvind Kolås's avatar
Øyvind Kolås committed
85

86
  for (y=radius; y<dst_rect->height+radius; y++)
87
    {
88 89
      gint src_offset = (inw*y+radius)*4;
      for (x=radius; x<outw+radius; x++)
90
        {
Øyvind Kolås's avatar
Øyvind Kolås committed
91
          gfloat *pixel= src_buf + src_offset;
92 93
          gfloat  min[4];
          gfloat  max[4];
94 95

          compute_envelopes (src_buf,
96
                             inw, inh,
97 98 99
                             x, y,
                             radius, samples,
                             iterations,
100
                             FALSE, /* same spray */
101
                             rgamma,
102
                             min, max);
103
          {
104 105 106 107 108 109 110
            /* this should be replaced with a better/faster projection of
             * pixel onto the vector spanned by min -> max, currently
             * computed by comparing the distance to min with the sum
             * of the distance to min/max.
             */

            gfloat nominator = 0;
111
            gfloat denominator = 0;
112
            gint c;
113 114
            for (c=0; c<3; c++)
              {
115 116
                nominator   += (pixel[c] - min[c]) * (pixel[c] - min[c]);
                denominator += (pixel[c] - max[c]) * (pixel[c] - max[c]);
117
              }
118 119 120 121

            nominator = sqrt (nominator);
            denominator = sqrt (denominator);
            denominator = nominator + denominator;
122

123
            if (denominator>0.000)
124 125 126 127 128 129 130 131 132 133 134 135
              {
                dst_buf[dst_offset+0] = nominator/denominator;
              }
            else
              {
                /* shouldn't happen */
                dst_buf[dst_offset+0] = 0.5;
              }
            dst_buf[dst_offset+1] = src_buf[src_offset+3];

            src_offset+=4;
            dst_offset+=2;
136 137 138
          }
        }
    }
139
  gegl_buffer_set (dst, dst_rect, 0, babl_format ("YA float"), dst_buf, GEGL_AUTO_ROWSTRIDE);
140 141 142
  g_free (src_buf);
  g_free (dst_buf);
}
143

Øyvind Kolås's avatar
Øyvind Kolås committed
144 145 146 147
static void prepare (GeglOperation *operation)
{
  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
  area->left = area->right = area->top = area->bottom =
148
      ceil (GEGL_PROPERTIES (operation)->radius);
149 150

  gegl_operation_set_format (operation, "input", babl_format ("RGBA float"));
151
  gegl_operation_set_format (operation, "output", babl_format ("YA float"));
Øyvind Kolås's avatar
Øyvind Kolås committed
152 153 154 155 156 157 158 159 160 161 162 163 164
}

static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
  GeglRectangle  result = {0,0,0,0};
  GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation,
                                                                     "input");
  if (!in_rect)
    return result;
  return *in_rect;
}

165 166 167
#include "opencl/gegl-cl.h"
#include "buffer/gegl-buffer-cl-iterator.h"

168
#include "opencl/c2g.cl.h"
169

170
static GeglClRunData *cl_data = NULL;
171

Victor Oliveira's avatar
Victor Oliveira committed
172
static gboolean
173
cl_c2g (cl_mem                in_tex,
174 175 176 177 178 179 180 181
        cl_mem                out_tex,
        size_t                global_worksize,
        const GeglRectangle  *src_roi,
        const GeglRectangle  *roi,
        gint                  radius,
        gint                  samples,
        gint                  iterations,
        gdouble               rgamma)
182 183
{
  cl_int cl_err = 0;
184 185 186
  cl_mem cl_lut_cos = NULL;
  cl_mem cl_lut_sin = NULL;
  cl_mem cl_radiuses = NULL;
187 188 189 190
  const size_t gbl_size[2] = {roi->width, roi->height};

  if (!cl_data)
    {
191 192
      const char *kernel_name[] ={"c2g", NULL};
      cl_data = gegl_cl_compile_and_build(c2g_cl_source, kernel_name);
193
    }
Victor Oliveira's avatar
Victor Oliveira committed
194
  if (!cl_data) return TRUE;
195 196 197 198

  compute_luts(rgamma);

  cl_lut_cos = gegl_clCreateBuffer(gegl_cl_get_context(),
199 200
                                   CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
                                   ANGLE_PRIME * sizeof(cl_float), lut_cos, &cl_err);
Victor Oliveira's avatar
Victor Oliveira committed
201
  CL_CHECK;
202 203

  cl_lut_sin = gegl_clCreateBuffer(gegl_cl_get_context(),
204 205
                                   CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
                                   ANGLE_PRIME * sizeof(cl_float), lut_sin, &cl_err);
206 207

  cl_radiuses = gegl_clCreateBuffer(gegl_cl_get_context(),
208 209
                                    CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
                                    RADIUS_PRIME * sizeof(cl_float), radiuses, &cl_err);
Victor Oliveira's avatar
Victor Oliveira committed
210
  CL_CHECK;
211 212 213 214 215 216 217 218

  {
  cl_int cl_src_width  = src_roi->width;
  cl_int cl_src_height = src_roi->height;
  cl_int cl_radius     = radius;
  cl_int cl_samples    = samples;
  cl_int cl_iterations = iterations;

Victor Oliveira's avatar
Victor Oliveira committed
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 0, sizeof(cl_mem), (void*)&in_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 1, sizeof(cl_int), (void*)&cl_src_width);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 2, sizeof(cl_int), (void*)&cl_src_height);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 3, sizeof(cl_mem), (void*)&cl_radiuses);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 4, sizeof(cl_mem), (void*)&cl_lut_cos);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 5, sizeof(cl_mem), (void*)&cl_lut_sin);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 6, sizeof(cl_mem), (void*)&out_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 7, sizeof(cl_int), (void*)&cl_radius);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 8, sizeof(cl_int), (void*)&cl_samples);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 9, sizeof(cl_int), (void*)&cl_iterations);
  CL_CHECK;
239 240 241 242 243
  }

  cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue(), cl_data->kernel[0],
                                       2, NULL, gbl_size, NULL,
                                       0, NULL, NULL);
Victor Oliveira's avatar
Victor Oliveira committed
244
  CL_CHECK;
245

Victor Oliveira's avatar
Victor Oliveira committed
246 247
  cl_err = gegl_clFinish(gegl_cl_get_command_queue ());
  CL_CHECK;
248

249 250 251 252 253 254
  cl_err = gegl_clReleaseMemObject (cl_radiuses);
  CL_CHECK_ONLY (cl_err);
  cl_err = gegl_clReleaseMemObject (cl_lut_cos);
  CL_CHECK_ONLY (cl_err);
  cl_err = gegl_clReleaseMemObject (cl_lut_sin);
  CL_CHECK_ONLY (cl_err);
255

Victor Oliveira's avatar
Victor Oliveira committed
256
  return FALSE;
257

Victor Oliveira's avatar
Victor Oliveira committed
258
error:
259 260 261 262 263 264 265
  if (cl_radiuses)
    gegl_clReleaseMemObject (cl_radiuses);
  if (cl_lut_cos)
    gegl_clReleaseMemObject (cl_lut_cos);
  if (cl_lut_sin)
    gegl_clReleaseMemObject (cl_lut_sin);

Victor Oliveira's avatar
Victor Oliveira committed
266
  return TRUE;
267 268 269
}

static gboolean
270 271 272 273
cl_process (GeglOperation       *operation,
            GeglBuffer          *input,
            GeglBuffer          *output,
            const GeglRectangle *result)
274 275 276 277 278 279
{
  const Babl *in_format  = babl_format("RGBA float");
  const Babl *out_format = gegl_operation_get_format (operation, "output");
  gint err;

  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
280
  GeglProperties *o = GEGL_PROPERTIES (operation);
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
  GeglBufferClIterator *i = gegl_buffer_cl_iterator_new (output,
                                                         result,
                                                         out_format,
                                                         GEGL_CL_BUFFER_WRITE);

  gint read = gegl_buffer_cl_iterator_add_2 (i,
                                             input,
                                             result,
                                             in_format,
                                             GEGL_CL_BUFFER_READ,
                                             op_area->left,
                                             op_area->right,
                                             op_area->top,
                                             op_area->bottom,
                                             GEGL_ABYSS_NONE);

298 299
  while (gegl_buffer_cl_iterator_next (i, &err))
    {
Victor Oliveira's avatar
Victor Oliveira committed
300
      if (err) return FALSE;
301 302 303 304 305 306 307 308 309 310 311 312

      err = cl_c2g(i->tex[read],
                   i->tex[0],
                   i->size[0],
                   &i->roi[read],
                   &i->roi[0],
                   o->radius,
                   o->samples,
                   o->iterations,
                   RGAMMA);

      if (err) return FALSE;
313
    }
314

315 316 317
  return TRUE;
}

Øyvind Kolås's avatar
Øyvind Kolås committed
318 319 320 321
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
322 323
         const GeglRectangle *result,
         gint                 level)
Øyvind Kolås's avatar
Øyvind Kolås committed
324
{
325
  GeglProperties *o = GEGL_PROPERTIES (operation);
326 327 328
  GeglRectangle compute;
  compute = gegl_operation_get_required_for_output (operation, "input",result);

329
  if (o->radius < 500 && gegl_operation_use_opencl (operation))
330 331 332
    if(cl_process(operation, input, output, result))
      return TRUE;

333 334 335 336 337
  c2g (input, &compute, output, result,
       o->radius,
       o->samples,
       o->iterations,
       /*o->rgamma*/RGAMMA);
Øyvind Kolås's avatar
Øyvind Kolås committed
338 339 340 341

  return  TRUE;
}

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
static const gchar *composition =
    "<?xml version='1.0'             encoding='UTF-8'?>"
    "<gegl>"
    "<node operation='gegl:c2g'>"
    "  <params>"
    "    <param name='radius'>200</param>"
    "    <param name='iterations'>90</param>"
    "  </params>"
    "</node>"
    "<node operation='gegl:load'>"
    "  <params>"
    "    <param name='path'>standard-input.png</param>"
    "  </params>"
    "</node>"
    "</gegl>";
Kevin Cozens's avatar
Kevin Cozens committed
357 358

static void
359
gegl_op_class_init (GeglOpClass *klass)
360
{
Kevin Cozens's avatar
Kevin Cozens committed
361 362 363 364 365 366
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);

367 368
  filter_class->process    = process;
  operation_class->prepare = prepare;
369

Øyvind Kolås's avatar
Øyvind Kolås committed
370 371 372 373 374 375
  /* we override defined region to avoid growing the size of what is defined
   * by the filter. This also allows the tricks used to treat alpha==0 pixels
   * in the image as source data not to be skipped by the stochastic sampling
   * yielding correct edge behavior.
   */
  operation_class->get_bounding_box = get_bounding_box;
Kevin Cozens's avatar
Kevin Cozens committed
376

377 378
  operation_class->opencl_support = TRUE;

379
  gegl_operation_class_set_keys (operation_class,
380 381
    "name",        "gegl:c2g",
    "categories",  "enhance",
382
    "reference-composition", composition,
383 384 385 386 387
    "description",
    _("Color to grayscale conversion, uses envelopes formed from spatial "
      "color differences to perform color-feature preserving grayscale "
      "spatial contrast enhancement"),
    NULL);
388 389 390
}

#endif