edge-laplace.c 12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* 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/>.
 *
 */
17

18
/*
19
 * Copyright 1997 Thorsten Schnier <thorsten@arch.usyd.edu.au>
20 21
 * Copyright 2011 Victor Oliveira <victormatheus@gmail.com>
 */
22

23 24 25 26
#include "config.h"
#include <glib/gi18n-lib.h>


27
#ifdef GEGL_PROPERTIES
28 29 30

#else

31 32
#define GEGL_OP_AREA_FILTER
#define GEGL_OP_C_FILE "edge-laplace.c"
33

34
#include "gegl-op.h"
35 36
#include <math.h>

37
#define LAPLACE_RADIUS 2
38 39
#define CHUNK_SIZE     1024
#define SQR(x)         ((x)*(x))
40 41 42 43 44

static void
edge_laplace (GeglBuffer          *src,
              const GeglRectangle *src_rect,
              GeglBuffer          *dst,
45 46 47 48
              const GeglRectangle *dst_rect,
              gfloat              *src_buf,
              gfloat              *temp_buf,
              gfloat              *dst_buf);
49

50 51
static void
prepare (GeglOperation *operation)
52 53 54 55
{
  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);

  area->left = area->right = area->top = area->bottom = LAPLACE_RADIUS;
56

57 58
  gegl_operation_set_format (operation, "input", babl_format ("R'G'B'A float"));
  gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A float"));
59 60
}

61 62 63 64 65 66
static gboolean
cl_process (GeglOperation       *operation,
            GeglBuffer          *input,
            GeglBuffer          *output,
            const GeglRectangle *result);

67 68 69 70
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
71 72
         const GeglRectangle *result,
         gint                 level)
73
{
74 75
  gint    i, j;
  gfloat *buf1, *buf2, *buf3;
76

77
  if (gegl_operation_use_opencl (operation))
78 79
    if (cl_process (operation, input, output, result))
      return TRUE;
80

81 82 83
  buf1 = g_new (gfloat, SQR (CHUNK_SIZE + LAPLACE_RADIUS * 2) * 4);
  buf2 = g_new (gfloat, SQR (CHUNK_SIZE + LAPLACE_RADIUS * 2) * 4);
  buf3 = g_new (gfloat, SQR (CHUNK_SIZE) * 4);
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

  for (j = 0; (j-1) * CHUNK_SIZE < result->height; j++)
    for (i = 0; (i-1) * CHUNK_SIZE < result->width; i++)
      {
        GeglRectangle chunked_result;
        GeglRectangle compute;

        chunked_result = *GEGL_RECTANGLE (result->x + i * CHUNK_SIZE,
                                          result->y + j * CHUNK_SIZE,
                                          CHUNK_SIZE, CHUNK_SIZE);

        gegl_rectangle_intersect (&chunked_result, &chunked_result, result);

        if (chunked_result.width < 1  || chunked_result.height < 1)
          continue;

        compute = gegl_operation_get_required_for_output (operation,
                                                          "input",
                                                          &chunked_result);

        edge_laplace (input, &compute, output, &chunked_result,
                      buf1, buf2, buf3);
      }

  g_free (buf1);
  g_free (buf2);
  g_free (buf3);
111 112 113 114 115 116

  return  TRUE;
}

static void
minmax  (gfloat  x1,
117 118 119 120 121 122
         gfloat  x2,
         gfloat  x3,
         gfloat  x4,
         gfloat  x5,
         gfloat *min_result,
         gfloat *max_result)
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
{
  gfloat min1, min2, max1, max2;

  if (x1 > x2)
    {
      max1 = x1;
      min1 = x2;
    }
  else
    {
      max1 = x2;
      min1 = x1;
    }

  if (x3 > x4)
    {
      max2 = x3;
      min2 = x4;
    }
  else
    {
      max2 = x4;
      min2 = x3;
    }

  if (min1 < min2)
    *min_result = fminf (min1, x5);
  else
    *min_result = fminf (min2, x5);

  if (max1 > max2)
    *max_result = fmaxf (max1, x5);
  else
    *max_result = fmaxf (max2, x5);
}


static void
edge_laplace (GeglBuffer          *src,
              const GeglRectangle *src_rect,
              GeglBuffer          *dst,
164 165 166 167
              const GeglRectangle *dst_rect,
              gfloat              *src_buf,
              gfloat              *temp_buf,
              gfloat              *dst_buf)
168 169
{

170 171 172
  gint    x, y;
  gint    offset;
  gint    src_width = src_rect->width;
173

174
  gegl_buffer_get (src, src_rect, 1.0,
175
                   babl_format ("R'G'B'A float"), src_buf, GEGL_AUTO_ROWSTRIDE,
176
                   GEGL_ABYSS_CLAMP);
177

178 179
  for (y = 0; y < dst_rect->height + LAPLACE_RADIUS; y++)
    for (x = 0; x < dst_rect->width + LAPLACE_RADIUS; x++)
180 181
      {
        gfloat *src_pix;
182 183 184
        gfloat  gradient[4] = {0.0f, 0.0f, 0.0f, 0.0f};
        gint    c;
        gfloat  minval, maxval;
185 186
        gint    i = x + LAPLACE_RADIUS - 1;
        gint    j = y + LAPLACE_RADIUS - 1;
187

188 189
        offset = i + j * src_width;
        src_pix = src_buf + offset * 4;
190

191
        for (c = 0; c < 3; c++)
192
          {
193 194 195 196 197 198 199 200 201 202
            gfloat s1 = src_pix[c - src_width * 4];
            gfloat s2 = src_pix[c + src_width * 4];
            gfloat s3 = src_pix[c - 4];
            gfloat s4 = src_pix[c + 4];
            gfloat s  = src_pix[c];
            gfloat temp_value;

            /* four-neighbourhood */
            minmax (s1, s2, s3, s4, s,
                    &minval, &maxval);
203

204
            gradient[c] = 0.5f * fmaxf ((maxval - s), (s - minval));
205

206 207 208 209
            /* nine-neighbourhood */
            temp_value = (src_pix[c - 4 - src_width * 4] +
                          s1 +
                          src_pix[c + 4 - src_width * 4] +
210

211
                          s3 - 8.0f * s + s4 +
212

213 214 215
                          src_pix[c - 4 + src_width * 4] +
                          s2 +
                          src_pix[c + 4 + src_width * 4]);
216

217

218
            if (temp_value < GEGL_FLOAT_EPSILON)
219 220 221 222
              gradient[c] *= -1.0f;
          }

        /* alpha */
223
        gradient[3] = src_pix[3];
224

225 226
        for (c = 0; c < 4; c++)
          temp_buf[offset * 4 + c] = gradient[c];
227 228
      }

229
  /* 1-pixel edges */
230 231
  offset = 0;

232 233
  for (y = 0; y < dst_rect->height; y++)
    for (x = 0; x < dst_rect->width; x++)
234
      {
235 236 237 238
        gfloat  value[4] = {0.0f, 0.0f, 0.0f, 0.0f};
        gint    c;
        gint    i = x + LAPLACE_RADIUS;
        gint    j = y + LAPLACE_RADIUS;
239
        gfloat *src_pix = temp_buf + (i + j * src_width) * 4;
240

241 242 243 244 245 246 247 248 249 250
        for (c = 0; c < 3; c++)
          {
            gfloat current = src_pix[c];

            current = ((current > 0.0f) &&
                       (src_pix[c - 4 - src_width * 4] < 0.0f ||
                        src_pix[c + 4 - src_width * 4] < 0.0f ||
                        src_pix[c     - src_width * 4] < 0.0f ||
                        src_pix[c - 4 + src_width * 4] < 0.0f ||
                        src_pix[c + 4 + src_width * 4] < 0.0f ||
251
                        src_pix[c     + src_width * 4] < 0.0f ||
252 253 254 255 256 257 258 259
                        src_pix[c - 4                ] < 0.0f ||
                        src_pix[c + 4                ] < 0.0f)) ?
              current : 0.0f;

            value[c] = current;
          }

        /* alpha */
260
        value[3] = src_pix[3];
261

262 263
        for (c = 0; c < 4; c++)
          dst_buf[offset * 4 + c] = value[c];
264

265 266 267
        offset++;
      }

268
  gegl_buffer_set (dst, dst_rect, 0, babl_format ("R'G'B'A float"), dst_buf,
269 270 271
                   GEGL_AUTO_ROWSTRIDE);
}

272 273 274
#include "opencl/gegl-cl.h"
#include "buffer/gegl-buffer-cl-iterator.h"

275
#include "opencl/edge-laplace.cl.h"
276

277
static GeglClRunData *cl_data = NULL;
278

279
static gboolean
280 281 282 283 284 285 286 287
cl_edge_laplace (cl_mem                in_tex,
                 cl_mem                aux_tex,
                 cl_mem                out_tex,
                 const GeglRectangle  *src_rect,
                 const GeglRectangle  *roi,
                 gint                  radius)
{
  cl_int cl_err = 0;
288 289
  size_t global_ws_in[2];
  size_t global_ws_aux[2];
290

291 292 293 294 295 296
  if (!cl_data)
    {
      const char *kernel_name[] = {"pre_edgelaplace", "knl_edgelaplace", NULL};
      cl_data = gegl_cl_compile_and_build (edge_laplace_cl_source, kernel_name);
    }
  if (!cl_data) return TRUE;
297

298 299 300 301 302
  global_ws_in[0] = roi->width  + LAPLACE_RADIUS;
  global_ws_in[1] = roi->height + LAPLACE_RADIUS;

  global_ws_aux[0] = roi->width;
  global_ws_aux[1] = roi->height;
303

304 305 306 307
  cl_err = gegl_cl_set_kernel_args (cl_data->kernel[0],
                                    sizeof (cl_mem), &in_tex,
                                    sizeof (cl_mem), &aux_tex,
                                    NULL);
308
  CL_CHECK;
309

310 311
  cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
                                        cl_data->kernel[0], 2,
312
                                        NULL, global_ws_in, NULL,
313
                                        0, NULL, NULL);
314
  CL_CHECK;
315

316 317 318 319 320

  cl_err = gegl_cl_set_kernel_args (cl_data->kernel[1],
                                    sizeof (cl_mem), &aux_tex,
                                    sizeof (cl_mem), &out_tex,
                                    NULL);
321
  CL_CHECK;
322

323 324
  cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
                                        cl_data->kernel[1], 2,
325
                                        NULL, global_ws_aux, NULL,
326
                                        0, NULL, NULL);
327 328 329 330
  CL_CHECK;

  return FALSE;

331
 error:
332
  return TRUE;
333 334 335 336 337 338 339 340 341 342
}

static gboolean
cl_process (GeglOperation       *operation,
            GeglBuffer          *input,
            GeglBuffer          *output,
            const GeglRectangle *result)
{
  const Babl *in_format  = gegl_operation_get_format (operation, "input");
  const Babl *out_format = gegl_operation_get_format (operation, "output");
343
  gint err = 0;
344 345 346

  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);

347 348 349 350 351 352 353 354 355 356 357 358 359 360
  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,
361
                                             GEGL_ABYSS_CLAMP);
362

363 364 365 366 367 368 369
  gint aux = gegl_buffer_cl_iterator_add_aux (i,
                                              result,
                                              in_format,
                                              op_area->left   - 1,
                                              op_area->right  - 1,
                                              op_area->top    - 1,
                                              op_area->bottom - 1);
370

371
  while (gegl_buffer_cl_iterator_next (i, &err) && !err)
372
    {
373 374 375 376 377 378
      err = cl_edge_laplace (i->tex[read],
                             i->tex[aux],
                             i->tex[0],
                             &i->roi[read],
                             &i->roi[0],
                             LAPLACE_RADIUS);
379

380 381 382 383 384
      if (err)
        {
          gegl_buffer_cl_iterator_stop (i);
          break;
        }
385
    }
386

387
  return !err;
388
}
389 390

static void
391
gegl_op_class_init (GeglOpClass *klass)
392 393 394 395
{
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

396 397
  operation_class = GEGL_OPERATION_CLASS (klass);
  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
398

399
  filter_class->process    = process;
400 401
  operation_class->prepare = prepare;

402 403
  operation_class->opencl_support = TRUE;

404
  gegl_operation_class_set_keys (operation_class,
405 406 407
    "name",        "gegl:edge-laplace",
    "categories",  "edge-detect",
    "description", _("High-resolution edge detection"),
408
    NULL);
409 410 411
}

#endif