convolution-matrix.c 9.28 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* 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/>.
 *
 * Copyright (C) 1997 Lauri Alanko <la@iki.fi>
 * Copyright 2011 Robert Sasu (sasu.robert@gmail.com)
 */

#include "config.h"
#include <glib/gi18n-lib.h>

23 24
#ifdef GEGL_PROPERTIES

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
property_double (a1, _("(1,1)"), 0.0)
property_double (a2, _("(1,2)"), 0.0)
property_double (a3, _("(1,3)"), 0.0)
property_double (a4, _("(1,4)"), 0.0)
property_double (a5, _("(1,5)"), 0.0)
property_double (b1, _("(2,1)"), 0.0)
property_double (b2, _("(2,2)"), 0.0)
property_double (b3, _("(2,3)"), 0.0)
property_double (b4, _("(2,4)"), 0.0)
property_double (b5, _("(2,5)"), 0.0)
property_double (c1, _("(3,1)"), 0.0)
property_double (c2, _("(3,2)"), 0.0)
property_double (c3, _("(3,3)"), 1.0)
property_double (c4, _("(3,4)"), 0.0)
property_double (c5, _("(3,5)"), 0.0)
property_double (d1, _("(4,1)"), 0.0)
property_double (d2, _("(4,2)"), 0.0)
property_double (d3, _("(4,3)"), 0.0)
property_double (d4, _("(4,4)"), 0.0)
property_double (d5, _("(4,5)"), 0.0)
property_double (e1, _("(5,1)"), 0.0)
property_double (e2, _("(5,2)"), 0.0)
property_double (e3, _("(5,3)"), 0.0)
property_double (e4, _("(5,4)"), 0.0)
property_double (e5, _("(5,5)"), 0.0)
50

51
property_double (divisor, _("Divisor"), 1.0)
52
    ui_range    (-1000.0, 1000.0)
53

54
property_double (offset, _("Offset"), 0.0)
55
    value_range (-1.0, 1.0)
56

57
property_boolean (red,   _("Red channel"),   TRUE)
58
property_boolean (green, _("Green channel"), TRUE)
59
property_boolean (blue,  _("Blue channel"),  TRUE)
60
property_boolean (alpha, _("Alpha channel"), TRUE)
61

62 63
property_boolean (normalize,    _("Normalize"),       TRUE)
property_boolean (alpha_weight, _("Alpha-weighting"), TRUE)
64

65
property_enum (border, _("Border"),
66 67
               GeglAbyssPolicy, gegl_abyss_policy,
               GEGL_ABYSS_CLAMP)
68 69 70

#else

71
#define GEGL_OP_AREA_FILTER
72
#define GEGL_OP_C_SOURCE convolution-matrix.c
73

74
#include "gegl-op.h"
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
#include <math.h>
#include <stdio.h>

#define MATRIX_SIZE   (5)
#define HALF_WINDOW   (MATRIX_SIZE/2)
#define CHANNELS      (5)

static void
prepare (GeglOperation *operation)
{
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);

  op_area->left = op_area->right = op_area->top = op_area->bottom = HALF_WINDOW;

  gegl_operation_set_format (operation, "output",
                             babl_format ("RGBA float"));
}

static void
94
make_matrix (GeglProperties  *o,
95
             gdouble        **matrix)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
{
  matrix[0][0] = o->a1;
  matrix[0][1] = o->a2;
  matrix[0][2] = o->a3;
  matrix[0][3] = o->a4;
  matrix[0][4] = o->a5;

  matrix[1][0] = o->b1;
  matrix[1][1] = o->b2;
  matrix[1][2] = o->b3;
  matrix[1][3] = o->b4;
  matrix[1][4] = o->b5;

  matrix[2][0] = o->c1;
  matrix[2][1] = o->c2;
  matrix[2][2] = o->c3;
  matrix[2][3] = o->c4;
  matrix[2][4] = o->c5;

  matrix[3][0] = o->d1;
  matrix[3][1] = o->d2;
  matrix[3][2] = o->d3;
  matrix[3][3] = o->d4;
  matrix[3][4] = o->d5;

  matrix[4][0] = o->e1;
  matrix[4][1] = o->e2;
  matrix[4][2] = o->e3;
  matrix[4][3] = o->e4;
  matrix[4][4] = o->e5;
}

128
static gboolean
129
normalize_o (GeglProperties  *o,
130
             gdouble        **matrix)
131 132 133 134 135 136 137 138 139 140 141 142 143 144
{
  gint      x, y;
  gboolean  valid = FALSE;
  gfloat    sum   = 0.0;

  for (y = 0; y < MATRIX_SIZE; y++)
    for (x = 0; x < MATRIX_SIZE; x++)
      {
        sum += matrix[x][y];
        if (matrix[x][y] != 0.0)
          valid = TRUE;
      }

  if (sum > 0)
145
    {
146 147
      o->offset  = 0.0;
      o->divisor = sum;
148
    }
149
  else if (sum < 0)
150
    {
151 152
      o->offset  = 1.0;
      o->divisor = -sum;
153
    }
154
  else
155
    {
156 157
      o->offset  = 0.5;
      o->divisor = 1;
158
    }
159 160

  return valid;
161 162 163
}

static void
164 165
convolve_pixel (GeglProperties       *o,
                gfloat               *src_buf,
166 167 168 169 170 171 172
                gfloat               *dst_buf,
                const GeglRectangle  *result,
                const GeglRectangle  *extended,
                gdouble             **matrix,
                gint                  xx,
                gint                  yy,
                gdouble               matrixsum)
173
{
174
  gfloat  color[4];
175 176 177
  gint    d_offset;
  gint    s_offset;
  gint    i;
178

179 180
  d_offset = (yy - result->y) * result->width * 4 +
             (xx - result->x) * 4;
181

182
  for (i = 0; i < 4; i++)
183
    {
184 185 186 187 188 189 190
      gdouble sum      = 0.0;
      gdouble alphasum = 0.0;

      if ((i == 0 && o->red)   ||
          (i == 1 && o->blue)  ||
          (i == 2 && o->green) ||
          (i == 3 && o->alpha))
191
        {
192 193 194 195
          gint x, y;

          for (x = 0; x < MATRIX_SIZE; x++)
            for (y = 0; y < MATRIX_SIZE; y++)
196
              {
197 198 199 200 201 202
                gint s_x = x + xx - HALF_WINDOW;
                gint s_y = y + yy - HALF_WINDOW;

                s_offset = (s_y - extended->y) * extended->width * 4 +
                           (s_x - extended->x) * 4;

203
                if (i != 3 && o->alpha_weight)
204
                  sum += matrix[x][y] * src_buf[s_offset + i] * src_buf[s_offset + 3];
205
                else
206 207 208 209
                  sum += matrix[x][y] * src_buf[s_offset + i];

                if (i == 3)
                  alphasum += fabs (matrix[x][y] * src_buf[s_offset + i]);
210
              }
211

212
          sum = sum / o->divisor;
213

214
          if (i == 3 && o->alpha_weight)
215 216 217
            {
              if (alphasum != 0)
                sum = sum * matrixsum / alphasum;
218 219
              else
                sum = 0.0;
220
            }
221

222
          sum += o->offset;
223 224 225 226

          color[i] = sum;
        }
      else
227
        {
228 229 230
          s_offset = (yy - result->y + HALF_WINDOW) * extended->width * 4 +
                     (xx - result->x + HALF_WINDOW) * 4;

231 232
          color[i] = src_buf[s_offset + i];
        }
233
    }
234

235
  for (i = 0; i < 4; i++)
236
    dst_buf[d_offset + i] = color[i];
237 238 239 240 241 242
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
243 244
         const GeglRectangle *result,
         gint                 level)
245
{
246
  GeglProperties          *o       = GEGL_PROPERTIES (operation);
247
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
248 249 250 251 252 253 254
  const Babl              *format  = babl_format ("RGBA float");
  GeglRectangle            rect;
  gfloat                  *src_buf;
  gfloat                  *dst_buf;
  gdouble                **matrix;
  gdouble                  matrixsum = 0.0;
  gint                     x, y;
255

256
  matrix = g_new0 (gdouble *, MATRIX_SIZE);
257

258
  for (x = 0; x < MATRIX_SIZE ;x++)
259
    matrix[x] = g_new0 (gdouble, MATRIX_SIZE);
260 261 262

  make_matrix (o, matrix);

263
  if (o->normalize)
264 265
    normalize_o (o, matrix);

266 267
  for (x = 0; x < MATRIX_SIZE; x++)
    for (y = 0; y < MATRIX_SIZE; y++)
268
      matrixsum += fabs (matrix[x][y]);
269 270 271 272 273 274 275 276 277

  rect.x      = result->x - op_area->left;
  rect.width  = result->width + op_area->left + op_area->right;
  rect.y      = result->y - op_area->top;
  rect.height = result->height + op_area->top + op_area->bottom;

  src_buf = g_new0 (gfloat, rect.width * rect.height * 4);
  dst_buf = g_new0 (gfloat, result->width * result->height * 4);

278
  gegl_buffer_get (input, &rect, 1.0, format, src_buf,
279
                   GEGL_AUTO_ROWSTRIDE, o->border);
280

281
  if (o->divisor != 0)
282
    {
283 284
      for (y = result->y; y < result->height + result->y; y++)
        for (x = result->x; x < result->width + result->x; x++)
285 286
          convolve_pixel (o, src_buf, dst_buf, result, &rect,
                          matrix, x, y, matrixsum);
287

288
      gegl_buffer_set (output, result, 0, format,
289
                       dst_buf, GEGL_AUTO_ROWSTRIDE);
290 291
    }
  else
292 293 294 295
    {
      gegl_buffer_set (output, &rect, 0, format,
                       src_buf, GEGL_AUTO_ROWSTRIDE);
    }
296

297 298 299 300 301 302 303 304 305
  g_free (src_buf);
  g_free (dst_buf);

  return TRUE;
}

static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
306
  GeglRectangle  result = { 0, };
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
  GeglRectangle *in_rect;

  in_rect = gegl_operation_source_get_bounding_box (operation, "input");
  if (!in_rect)
    return result;

  return *in_rect;
}

static GeglRectangle
get_required_for_output (GeglOperation       *operation,
                         const gchar         *input_pad,
                         const GeglRectangle *roi)
{
  return get_bounding_box (operation);
}

static void
325
gegl_op_class_init (GeglOpClass *klass)
326 327 328 329 330 331 332
{
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

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

333 334
  filter_class->process                    = process;
  operation_class->prepare                 = prepare;
335 336 337
  operation_class->get_bounding_box        = get_bounding_box;
  operation_class->get_required_for_output = get_required_for_output;

338
  gegl_operation_class_set_keys (operation_class,
339 340 341
    "categories",  "generic",
    "name",        "gegl:convolution-matrix",
    "description", _("Apply a generic 5x5 convolution matrix"),
342
    NULL);
343 344 345
}

#endif