color-temperature.c 9.36 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* 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/>.
 *
16
 * Copyright 2008 Jan Heller <jan.heller (at) matfyz.cz>
17 18
 */

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

22 23 24
#define LOWEST_TEMPERATURE     1000
#define HIGHEST_TEMPERATURE   12000

25
#ifdef GEGL_PROPERTIES
26

27 28 29 30 31
property_double (original_temperature, _("Original temperature"), 6500)
  description(_("Estimated temperature of the light source in Kelvin "
                "the image was taken with."))
  value_range (LOWEST_TEMPERATURE, HIGHEST_TEMPERATURE)
  ui_meta     ("unit", "kelvin")
32

33 34 35 36 37
property_double (intended_temperature, _("Intended temperature"), 6500)
  description(_("Corrected estimation of the temperature of the light "
                "source in Kelvin."))
  value_range (LOWEST_TEMPERATURE, HIGHEST_TEMPERATURE)
  ui_meta     ("unit", "kelvin")
38 39 40

#else

41
#define GEGL_OP_POINT_FILTER
42
#define GEGL_OP_C_SOURCE color-temperature.c
43

44
#include "gegl-op.h"
45

46
static const gfloat rgb_r55[3][12];
47 48

static void
49
convert_k_to_rgb (gfloat  temperature,
50 51
                  gfloat *rgb)
{
52
  gint channel;
53 54 55 56 57 58 59

  if (temperature < LOWEST_TEMPERATURE)
    temperature = LOWEST_TEMPERATURE;

  if (temperature > HIGHEST_TEMPERATURE)
    temperature = HIGHEST_TEMPERATURE;

60 61 62 63 64 65 66
  /* Evaluation of an approximation of the Planckian locus in linear RGB space
   * by rational functions of degree 5 using Horner's scheme
   * f(x) =  (p1*x^5 + p2*x^4 + p3*x^3 + p4*x^2 + p5*x + p6) /
   *            (x^5 + q1*x^4 + q2*x^3 + q3*x^2 + q4*x + q5)
   */
  for (channel = 0; channel < 3; channel++)
    {
67 68 69
      gfloat nomin, denom;
      gint   deg;

70 71 72
      nomin = rgb_r55[channel][0];
      for (deg = 1; deg < 6; deg++)
        nomin = nomin * temperature + rgb_r55[channel][deg];
73

74 75 76
      denom = rgb_r55[channel][6];
      for (deg = 1; deg < 6; deg++)
        denom = denom * temperature + rgb_r55[channel][6 + deg];
77 78 79

      rgb[channel] = nomin / denom;
    }
80 81
}

82

83 84
static void prepare (GeglOperation *operation)
{
85
  const Babl *format = babl_format ("RGBA float");
86 87 88 89
  gegl_operation_set_format (operation, "input", format);
  gegl_operation_set_format (operation, "output", format);
}

90 91 92
static void
finalize (GObject *object)
{
93
  GeglProperties *o = GEGL_PROPERTIES (object);
94

95
  if (o->user_data)
96
    {
97 98
      g_free (o->user_data);
      o->user_data = NULL;
99 100
    }

101
  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
102 103 104 105 106 107 108 109 110
}

static void
notify (GObject    *object,
        GParamSpec *pspec)
{
  if (strcmp (pspec->name, "original-temperature") == 0 ||
      strcmp (pspec->name, "intended-temperature") == 0)
    {
111
      GeglProperties *o = GEGL_PROPERTIES (object);
112 113 114 115

      /* one of the properties has changed,
       * invalidate the preprocessed coefficients
       */
116
      if (o->user_data)
117
        {
118 119
          g_free (o->user_data);
          o->user_data = NULL;
120 121 122
        }
    }

123 124
  if (G_OBJECT_CLASS (gegl_op_parent_class)->notify)
    G_OBJECT_CLASS (gegl_op_parent_class)->notify (object, pspec);
125 126 127 128
}


static gfloat *
129
preprocess (GeglProperties *o)
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
{
  gfloat *coeffs = g_new (gfloat, 3);
  gfloat  original_temperature_rgb[3];
  gfloat  intended_temperature_rgb[3];

  convert_k_to_rgb (o->original_temperature, original_temperature_rgb);
  convert_k_to_rgb (o->intended_temperature, intended_temperature_rgb);

  coeffs[0] = original_temperature_rgb[0] / intended_temperature_rgb[0];
  coeffs[1] = original_temperature_rgb[1] / intended_temperature_rgb[1];
  coeffs[2] = original_temperature_rgb[2] / intended_temperature_rgb[2];

  return coeffs;
}

145 146 147 148
/* GeglOperationPointFilter gives us a linear buffer to operate on
 * in our requested pixel format
 */
static gboolean
149 150 151 152
process (GeglOperation       *op,
         void                *in_buf,
         void                *out_buf,
         glong                n_pixels,
153 154
         const GeglRectangle *roi,
         gint                 level)
155
{
156
  GeglProperties   *o         = GEGL_PROPERTIES (op);
157 158
  gfloat       *in_pixel  = in_buf;
  gfloat       *out_pixel = out_buf;
159
  const gfloat *coeffs    = o->user_data;
160

161
  in_pixel = in_buf;
162 163
  out_pixel = out_buf;

164 165
  if (! coeffs)
    {
166
      coeffs = o->user_data = preprocess (o);
167
    }
168

169
  while (n_pixels--)
170
    {
171 172 173
      out_pixel[0] = in_pixel[0] * coeffs[0];
      out_pixel[1] = in_pixel[1] * coeffs[1];
      out_pixel[2] = in_pixel[2] * coeffs[2];
174
      out_pixel[3] = in_pixel[3];
175 176 177 178

      in_pixel += 4;
      out_pixel += 4;
    }
179

180 181 182
  return TRUE;
}

183
#include "opencl/gegl-cl.h"
184
#include "opencl/color-temperature.cl.h"
185

186
static GeglClRunData *cl_data = NULL;
187 188

/* OpenCL processing function */
Victor Oliveira's avatar
Victor Oliveira committed
189
static gboolean
190
cl_process (GeglOperation       *op,
191 192 193 194 195
            cl_mem               in_tex,
            cl_mem               out_tex,
            size_t               global_worksize,
            const GeglRectangle *roi,
            int                  level)
196
{
197
  /* Retrieve a pointer to GeglProperties structure which contains all the
198 199 200
   * chanted properties
   */

201 202
  GeglProperties   *o         = GEGL_PROPERTIES (op);
  const gfloat *coeffs    = o->user_data;
203 204 205 206 207

  cl_int cl_err = 0;

  if (! coeffs)
    {
208
      coeffs = o->user_data = preprocess (o);
209 210 211 212
    }

  if (!cl_data)
    {
213 214
      const char *kernel_name[] = {"gegl_color_temperature", NULL};
      cl_data = gegl_cl_compile_and_build (color_temperature_cl_source, kernel_name);
215 216 217 218
    }

  if (!cl_data) return 1;

Victor Oliveira's avatar
Victor Oliveira committed
219 220 221 222 223 224 225 226 227 228
  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_mem),    (void*)&out_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 2, sizeof(cl_float),  (void*)&coeffs[0]);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 3, sizeof(cl_float),  (void*)&coeffs[1]);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 4, sizeof(cl_float),  (void*)&coeffs[2]);
  CL_CHECK;
229 230 231 232 233

  cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue (),
                                        cl_data->kernel[0], 1,
                                        NULL, &global_worksize, NULL,
                                        0, NULL, NULL);
Victor Oliveira's avatar
Victor Oliveira committed
234 235 236
  CL_CHECK;

  return FALSE;
237

Victor Oliveira's avatar
Victor Oliveira committed
238 239
error:
  return TRUE;
240 241 242
}


243 244

static void
245
gegl_op_class_init (GeglOpClass *klass)
246
{
247
  GObjectClass                  *object_class;
248 249
  GeglOperationClass            *operation_class;
  GeglOperationPointFilterClass *point_filter_class;
250 251 252 253 254 255 256 257 258 259 260 261 262
  gchar                         *composition = "<?xml version='1.0' encoding='UTF-8'?>"
    "<gegl>"
    "<node operation='gegl:color-temperature'>"
    "  <params>"
    "    <param name='intended-temperature'>12000</param>"
    "  </params>"
    "</node>"
    "<node operation='gegl:load'>"
    "  <params>"
    "    <param name='path'>standard-input.png</param>"
    "  </params>"
    "</node>"
    "</gegl>";
263

264
  object_class       = G_OBJECT_CLASS (klass);
265 266 267
  operation_class    = GEGL_OPERATION_CLASS (klass);
  point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);

268 269
  object_class->finalize = finalize;
  object_class->notify   = notify;
270

271 272
  operation_class->prepare = prepare;

273
  point_filter_class->process    = process;
274
  point_filter_class->cl_process = cl_process;
275

Victor Oliveira's avatar
Victor Oliveira committed
276 277
  operation_class->opencl_support = TRUE;

278
  gegl_operation_class_set_keys (operation_class,
279
    "name",        "gegl:color-temperature",
280
    "title",       _("Color Temperature"),
281
    "categories",  "color",
282
    "description", _("Change the color temperature of the image, from an assumed original color temperature to an intended one."),
283
    "reference-composition", composition,
284
    NULL);
285 286
}

287 288
/* Coefficients of rational functions of degree 5 fitted per color channel to
 * the linear RGB coordinates of the range 1000K-12000K of the Planckian locus
289
 * with the 20K step. Original CIE-xy data from
290 291 292 293 294
 *
 * http://www.aim-dtp.net/aim/technology/cie_xyz/k2xy.txt
 *
 * converted to the linear RGB space assuming the ITU-R BT.709-5/sRGB primaries
 */
295
static const gfloat rgb_r55[3][12] =
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
{
  {
     6.9389923563552169e-01,  2.7719388100974670e+03,
     2.0999316761104289e+07, -4.8889434162208414e+09,
    -1.1899785506796783e+07, -4.7418427686099203e+04,
     1.0000000000000000e+00,  3.5434394338546258e+03,
    -5.6159353379127791e+05,  2.7369467137870544e+08,
     1.6295814912940913e+08,  4.3975072422421846e+05
  },
  {
     9.5417426141210926e-01,  2.2041043287098860e+03,
    -3.0142332673634286e+06, -3.5111986367681120e+03,
    -5.7030969525354260e+00,  6.1810926909962016e-01,
     1.0000000000000000e+00,  1.3728609973644000e+03,
     1.3099184987576159e+06, -2.1757404458816318e+03,
    -2.3892456292510311e+00,  8.1079012401293249e-01
  },
  {
    -7.1151622540856201e+10,  3.3728185802339764e+16,
    -7.9396187338868539e+19,  2.9699115135330123e+22,
    -9.7520399221734228e+22, -2.9250107732225114e+20,
     1.0000000000000000e+00,  1.3888666482167408e+16,
     2.3899765140914549e+19,  1.4583606312383295e+23,
     1.9766018324502894e+22,  2.9395068478016189e+18
  }
};
322 323

#endif