Curve - First iteration
This discussion is connected to the gegl-developer-list.gnome.org mailing list which is provided by the GIMP developers and not related to gimpusers.com.
This is a read-only list on gimpusers.com so this discussion thread is read-only, too.
Curve - First iteration | Mark Probst | 16 May 01:07 |
Curve - First iteration | Øyvind Kolås | 17 May 21:54 |
Curve - First iteration | Mark Probst | 17 May 22:06 |
Curve - First iteration | Øyvind Kolås | 17 May 23:28 |
Curve - First iteration | Mark Probst | 17 May 23:45 |
Curve - First iteration | Mark Probst | 18 May 00:12 |
Curve - First iteration | Øyvind Kolås | 18 May 10:02 |
Curve - First iteration
Hi!
I've implemented a curve class that does cubic spline interpolation (which is, as far as I could ascertain, what Photoshop uses, for example) and a contrast curve operation that uses it.
Several things are still missing, most of all chanting support and loading/saving of curves. Hints on how to implement the latter would be highly appreciated.
A question regarding the operation: Is it possible to make an operation that works on more than one format? I'd like the contrast curve to work on YA as well as RGBA. The latter because it's more general and the former because I need it for my application and using RGBA instead of YA would just slow it down.
What do I have to do to get this and the mono mixer incorporated into the official GEGL?
Mark
Index: operations/color/contrast-curve.c
===================================================================
--- operations/color/contrast-curve.c (revision 0)
+++ operations/color/contrast-curve.c (revision 0)
@@ -0,0 +1,105 @@
+/* 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 2 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2007 Mark Probst
+ */
+#if GEGL_CHANT_PROPERTIES
+
+ gegl_chant_int (sampling_points, 0, 65536, 0, "Number of curve
sampling points. 0 for exact calculation.")
+ gegl_chant_object (curve, "The contrast curve.")
+
+#else
+
+#define GEGL_CHANT_POINT_FILTER
+#define GEGL_CHANT_NAME contrast_curve
+#define GEGL_CHANT_DESCRIPTION "Adjusts the contrast of the image
according to a curve."
+#define GEGL_CHANT_SELF "contrast-curve.c"
+#define GEGL_CHANT_CATEGORIES "color"
+#define GEGL_CHANT_INIT
+#include "gegl-chant.h"
+
+static void init (GeglChantOperation *self)
+{
+ /* set the babl format this operation prefers to work on */
+ GEGL_OPERATION_POINT_FILTER (self)->format = babl_format ("YA float");
+}
+
+static gboolean
+process (GeglOperation *op,
+ void *in_buf,
+ void *out_buf,
+ glong samples)
+{
+ GeglChantOperation *self;
+ gint i;
+ gfloat *in = in_buf;
+ gfloat *out = out_buf;
+ gint num_sampling_points;
+ GeglCurve *curve;
+ gfloat *xs, *ys;
+
+ self = GEGL_CHANT_OPERATION (op);
+
+ num_sampling_points = self->sampling_points;
+ curve = GEGL_CURVE (self->curve);
+
+ if (num_sampling_points > 0)
+ {
+ xs = (gfloat*)g_malloc(sizeof(gfloat) * num_sampling_points);
+ ys = (gfloat*)g_malloc(sizeof(gfloat) * num_sampling_points);
+
+ gegl_curve_calc_values(curve, 0.0, 1.0, num_sampling_points, xs, ys);
+
+ g_free(xs);
+
+ for (i=0; i< 0)
+ y = ys[0];
+ else if (x >= num_sampling_points)
+ y = ys[num_sampling_points - 1];
+ else
+ y = ys[x];
+
+ out[0] = y;
+ out[1]=in[1];
+
+ in += 2;
+ out+= 2;
+ }
+
+ g_free(ys);
+ }
+ else
+ for (i=0; i
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "gegl-types.h"
+
+#include "gegl-curve.h"
+
+enum
+{
+ PROP_0,
+};
+
+typedef struct _GeglCurvePoint GeglCurvePoint;
+typedef struct _GeglCurvePrivate GeglCurvePrivate;
+typedef struct _CurveNameEntity CurveNameEntity;
+
+struct _GeglCurvePoint
+{
+ gfloat x;
+ gfloat y;
+ gfloat y2;
+};
+
+struct _GeglCurvePrivate
+{
+ gfloat y_min;
+ gfloat y_max;
+ GArray *points;
+ gboolean need_recalc;
+ GeglCurvePoint **indir;
+};
+
+static void finalize (GObject *self);
+static void set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+G_DEFINE_TYPE (GeglCurve, gegl_curve, G_TYPE_OBJECT);
+
+#define GEGL_CURVE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE
((o), GEGL_TYPE_CURVE, GeglCurvePrivate))
+
+static void
+gegl_curve_init (GeglCurve *self)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (self);
+
+ priv->y_min = 0.0;
+ priv->y_max = 1.0;
+ priv->need_recalc = FALSE;
+ priv->indir = NULL;
+ priv->points = g_array_new(FALSE, FALSE, sizeof(GeglCurvePoint));
+}
+
+static void
+gegl_curve_class_init (GeglCurveClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = finalize;
+ gobject_class->set_property = set_property;
+ gobject_class->get_property = get_property;
+
+ g_type_class_add_private (klass, sizeof (GeglCurvePrivate));
+}
+
+static void
+finalize (GObject *gobject)
+{
+ GeglCurve *self = GEGL_CURVE (gobject);
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (self);
+
+ g_array_free(priv->points, TRUE);
+ priv->points = NULL;
+
+ if (priv->indir != NULL)
+ {
+ g_free(priv->indir);
+ priv->indir = NULL;
+ }
+
+ G_OBJECT_CLASS (gegl_curve_parent_class)->finalize (gobject);
+}
+
+static void
+set_property (GObject *gobject,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *gobject,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+ break;
+ }
+}
+
+GeglCurve *
+gegl_curve_new (gfloat y_min,
+ gfloat y_max)
+{
+ GeglCurve *self = GEGL_CURVE (g_object_new (GEGL_TYPE_CURVE, NULL));
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (self);
+
+ gegl_curve_init(self);
+ priv->y_min = y_min;
+ priv->y_max = y_max;
+
+ return self;
+}
+
+guint
+gegl_curve_add_point (GeglCurve *self,
+ gfloat x,
+ gfloat y)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (GEGL_CURVE (self));
+ GeglCurvePoint point = { x, y };
+
+ g_array_append_val(priv->points, point);
+
+ priv->need_recalc = TRUE;
+
+ return priv->points->len - 1;
+}
+
+void
+gegl_curve_get_point (GeglCurve *self,
+ guint index,
+ gfloat *x,
+ gfloat *y)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (GEGL_CURVE (self));
+ GeglCurvePoint point;
+
+ g_assert(index < priv->points->len);
+ point = g_array_index(priv->points, GeglCurvePoint, index);
+
+ *x = point.x;
+ *y = point.y;
+}
+
+void
+gegl_curve_set_point (GeglCurve *self,
+ guint index,
+ gfloat x,
+ gfloat y)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (GEGL_CURVE (self));
+ GeglCurvePoint point = { x, y };
+
+ g_assert(index < priv->points->len);
+ g_array_index(priv->points, GeglCurvePoint, index) = point;
+
+ priv->need_recalc = TRUE;
+}
+
+guint
+gegl_curve_num_points (GeglCurve *self)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (GEGL_CURVE (self));
+
+ return priv->points->len;
+}
+
+static int
+compare_point_indirs (const void *_p1, const void *_p2)
+{
+ GeglCurvePoint *p1 = *(GeglCurvePoint**)_p1;
+ GeglCurvePoint *p2 = *(GeglCurvePoint**)_p2;
+
+ if (p1->x < p2->x)
+ return -1;
+ if (p1->x > p2->x)
+ return 1;
+ return 0;
+}
+
+#define X(i) (priv->indir[(i)]->x)
+#define Y(i) (priv->indir[(i)]->y)
+#define Y2(i) (priv->indir[(i)]->y2)
+#define YCLAMP(y) ((y)y_min ? priv->y_min :
((y)>priv->y_max ? priv->y_max : (y)))
+
+static void
+recalculate (GeglCurvePrivate *priv)
+{
+ guint len = priv->points->len;
+ guint i;
+ gint k;
+ gfloat *b;
+
+ if (!priv->need_recalc)
+ return;
+
+ priv->need_recalc = FALSE;
+
+ if (len < 2)
+ return;
+
+ if (priv->indir != NULL)
+ g_free(priv->indir);
+ priv->indir = (GeglCurvePoint**)g_malloc(sizeof(GeglCurvePoint*) * len);
+
+ for (i = 0; i < len; ++i)
+ priv->indir[i] = &g_array_index(priv->points, GeglCurvePoint, i);
+
+ qsort(priv->indir, len, sizeof(GeglCurvePoint*), compare_point_indirs);
+
+ b = (gfloat*)g_malloc(sizeof(gfloat) * (len - 1));
+
+ // lower natural boundary conditions
+ Y2(0) = b[0] = 0.0;
+
+ for (i = 1; i < len - 1; ++i)
+ {
+ gfloat sig = (X(i) - X(i-1)) / (X(i+1) - X(i-1));
+ gfloat p = sig * Y2(i-1) + 2;
+
+ Y2(i) = (sig - 1) / p;
+ b[i] = ((Y(i+1) - Y(i))
+ / (X(i+1) - X(i)) - (Y(i) - Y(i-1)) / (X(i) - X(i-1)));
+ b[i] = (6 * b[i] / (X(i+1) - X(i-1)) - sig * b[i-1]) / p;
+ }
+
+ // upper natural boundary condition
+ Y2(len-1) = 0.0;
+ for (k = len - 2; k >= 0; --k)
+ Y2(k) = Y2(k) * Y2(k+1) + b[k];
+
+ /*
+ for (i = 0; i < len; ++i)
+ {
+ printf("y2: %f ", Y2(i))
+ */
+
+ g_free(b);
+}
+
+static guint
+find_interval (GeglCurvePrivate *priv, gfloat u)
+{
+ guint len = priv->points->len;
+ guint i = 0, j = len - 1;
+
+ while (j - i > 1)
+ {
+ guint k = (i + j) / 2;
+ if (X(k) > u)
+ j = k;
+ else
+ i = k;
+ }
+
+ return i;
+}
+
+static gfloat
+apply (GeglCurvePrivate *priv, gfloat u, guint i)
+{
+ gfloat h = X(i+1) - X(i);
+ gfloat a = (X(i+1) - u) / h;
+ gfloat b = (u - X(i)) / h;
+ gfloat y = a*Y(i) + b*Y(i+1) + ((a*a*a-a)*Y2(i) +
(b*b*b-b)*Y2(i+1)) * (h*h)/6;
+
+ return YCLAMP(y);
+}
+
+gfloat
+gegl_curve_calc_value (GeglCurve *self,
+ gfloat x)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (GEGL_CURVE (self));
+
+ recalculate(priv);
+
+ if (priv->points->len >= 2)
+ return apply(priv, x, find_interval(priv, x));
+ else if (priv->points->len == 1)
+ return YCLAMP(g_array_index(priv->points, GeglCurvePoint, 0).y);
+ else
+ {
+ g_assert(priv->points->len == 0);
+ return priv->y_min;
+ }
+}
+
+void
+gegl_curve_calc_values (GeglCurve *self,
+ gfloat x_min,
+ gfloat x_max,
+ guint num_samples,
+ gfloat *xs,
+ gfloat *ys)
+{
+ GeglCurvePrivate *priv = GEGL_CURVE_GET_PRIVATE (GEGL_CURVE (self));
+ guint len = priv->points->len;
+ guint i, j;
+
+ recalculate(priv);
+
+ j = 0;
+ for (i = 0; i < num_samples; ++i)
+ {
+ gfloat u = x_min + (x_max - x_min) * (double)i / (double)(num_samples - 1);
+
+ xs[i] = u;
+
+ if (len >= 2)
+ {
+ while (j < len - 2 && X(j+1) < u)
+ ++j;
+
+ ys[i] = apply(priv, u, j);
+ }
+ else if (len == 1)
+ ys[i] = YCLAMP(g_array_index(priv->points, GeglCurvePoint, 0).y);
+ else
+ {
+ g_assert(len == 0);
+ ys[i] = priv->y_min;
+ }
+ }
+}
+
+#undef X
+#undef Y
+#undef Y2
+#undef YCLAMP
+
+/* --------------------------------------------------------------------------
+ * A GParamSpec class to describe behavior of GeglCurve as an object property
+ * follows.
+ * --------------------------------------------------------------------------
+ */
+
+#define GEGL_TYPE_PARAM_CURVE (gegl_param_curve_get_type ())
+#define GEGL_PARAM_CURVE(obj)
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_PARAM_CURVE,
GeglParamCurve))
+#define GEGL_IS_PARAM_CURVE(obj)
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_PARAM_CURVE))
+#define GEGL_IS_PARAM_CURVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE
((klass), GEGL_TYPE_PARAM_CURVE))
+
+typedef struct _GeglParamCurve GeglParamCurve;
+
+struct _GeglParamCurve
+{
+ GParamSpec parent_instance;
+
+ GeglCurve *default_curve;
+};
+
+static void
+gegl_param_curve_init (GParamSpec *self)
+{
+ GEGL_PARAM_CURVE (self)->default_curve = NULL;
+}
+
+static void
+gegl_param_curve_finalize (GParamSpec *self)
+{
+ GeglParamCurve *param_curve = GEGL_PARAM_CURVE (self);
+ GParamSpecClass *parent_class = g_type_class_peek (g_type_parent
(GEGL_TYPE_PARAM_CURVE));
+
+ if (param_curve->default_curve)
+ {
+ g_object_unref (param_curve->default_curve);
+ param_curve->default_curve = NULL;
+ }
+
+ parent_class->finalize (self);
+}
+
+static void
+value_set_default (GParamSpec *param_spec,
+ GValue *value)
+{
+ GeglParamCurve *gegl_curve = GEGL_PARAM_CURVE (param_spec);
+
+ g_object_ref (gegl_curve->default_curve); /* XXX:
+ not sure why this is needed,
+ but a reference is leaked
+ unless it his here */
+ g_value_set_object (value, gegl_curve->default_curve);
+}
+
+GType
+gegl_param_curve_get_type (void)
+{
+ static GType param_curve_type = 0;
+
+ if (G_UNLIKELY (param_curve_type == 0))
+ {
+ static GParamSpecTypeInfo param_curve_type_info = {
+ sizeof (GeglParamCurve),
+ 0,
+ gegl_param_curve_init,
+ 0,
+ gegl_param_curve_finalize,
+ value_set_default,
+ NULL,
+ NULL
+ };
+ param_curve_type_info.value_type = GEGL_TYPE_CURVE;
+
+ param_curve_type = g_param_type_register_static ("GeglParamCurve",
+ ¶m_curve_type_info);
+ }
+
+ return param_curve_type;
+}
+
+GParamSpec *
+gegl_param_spec_curve (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GeglCurve *default_curve,
+ GParamFlags flags)
+{
+ GeglParamCurve *param_curve;
+
+ param_curve = g_param_spec_internal (GEGL_TYPE_PARAM_CURVE,
+ name, nick, blurb, flags);
+
+ param_curve->default_curve = default_curve;
+
+ return G_PARAM_SPEC (param_curve);
+}
Index: gegl/gegl-curve.h
===================================================================
--- gegl/gegl-curve.h (revision 0)
+++ gegl/gegl-curve.h (revision 0)
@@ -0,0 +1,91 @@
+/* This file is part of 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 2 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2006 Mark Probst
+ */
+
+#ifndef __GEGL_CURVE_H__
+#define __GEGL_CURVE_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#ifndef GEGL_TYPE_CURVE
+#define GEGL_TYPE_CURVE (gegl_curve_get_type ())
+#endif
+#define GEGL_CURVE(obj) (G_TYPE_CHECK_INSTANCE_CAST
((obj), GEGL_TYPE_CURVE, GeglCurve))
+#define GEGL_CURVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GEGL_TYPE_CURVE, GeglCurveClass))
+#define GEGL_IS_CURVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE
((obj), GEGL_TYPE_CURVE))
+#define GEGL_IS_CURVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GEGL_TYPE_CURVE))
+#define GEGL_CURVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GEGL_TYPE_CURVE, GeglCurveClass))
+
+typedef struct _GeglCurve GeglCurve;
+typedef struct _GeglCurveClass GeglCurveClass;
+
+struct _GeglCurve
+{
+ GObject parent;
+};
+
+struct _GeglCurveClass
+{
+ GObjectClass parent;
+};
+
+GeglCurve* gegl_curve_new (gfloat y_min,
+ gfloat y_max);
+
+GType gegl_curve_get_type (void) G_GNUC_CONST;
+
+guint gegl_curve_add_point (GeglCurve *self,
+ gfloat x,
+ gfloat y);
+
+void gegl_curve_get_point (GeglCurve *self,
+ guint index,
+ gfloat *x,
+ gfloat *y);
+
+void gegl_curve_set_point (GeglCurve *self,
+ guint index,
+ gfloat x,
+ gfloat y);
+
+guint gegl_curve_num_points (GeglCurve *self);
+
+gfloat gegl_curve_calc_value (GeglCurve *self,
+ gfloat x);
+
+void gegl_curve_calc_values (GeglCurve *self,
+ gfloat x_min,
+ gfloat x_max,
+ guint num_samples,
+ gfloat *xs,
+ gfloat *ys);
+
+GParamSpec * gegl_param_spec_curve (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GeglCurve *default_curve,
+ GParamFlags flags);
+
+GType gegl_param_curve_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GEGL_CURVE_H__ */
Index: gegl/gegl-plugin.h
===================================================================
--- gegl/gegl-plugin.h (revision 1462)
+++ gegl/gegl-plugin.h (working copy)
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
#include
Index: gegl/gegl.h
===================================================================
--- gegl/gegl.h (revision 1462)
+++ gegl/gegl.h (working copy)
@@ -751,7 +751,48 @@
gfloat b,
gfloat a);
+
/***
+ * GeglCurve:
+ */
+#ifndef GEGL_INTERNAL
+typedef struct _GeglCurve GeglCurve;
+GType gegl_curve_get_type (void) G_GNUC_CONST;
+#define GEGL_TYPE_CURVE (gegl_curve_get_type ())
+#define GEGL_CURVE(obj) (G_TYPE_CHECK_INSTANCE_CAST
((obj), GEGL_TYPE_CURVE, GeglCurve))
+#endif
+
+
+GeglCurve* gegl_curve_new (gfloat y_min,
+ gfloat y_max);
+
+guint gegl_curve_add_point (GeglCurve *self,
+ gfloat x,
+ gfloat y);
+
+void gegl_curve_get_point (GeglCurve *self,
+ guint index,
+ gfloat *x,
+ gfloat *y);
+
+void gegl_curve_set_point (GeglCurve *self,
+ guint index,
+ gfloat x,
+ gfloat y);
+
+guint gegl_curve_num_points (GeglCurve *self);
+
+gfloat gegl_curve_calc_value (GeglCurve *self,
+ gfloat x);
+
+void gegl_curve_calc_values (GeglCurve *self,
+ gfloat x_min,
+ gfloat x_max,
+ guint num_samples,
+ gfloat *xs,
+ gfloat *ys);
+
+/***
* Bindings conveniences:
*
* The following functions are mostly included to make it easier
Index: gegl/Makefile.am
===================================================================
--- gegl/Makefile.am (revision 1462)
+++ gegl/Makefile.am (working copy)
@@ -13,6 +13,7 @@
gegl-color.c \
gegl-connection.c \
gegl-cr-visitor.c \
+ gegl-curve.c \
gegl-debug-rect-visitor.c \
gegl-eval-mgr.c \
gegl-eval-visitor.c \
@@ -46,6 +47,7 @@
\
gegl-color.h \
gegl-connection.h \
+ gegl-curve.h \
gegl-extension-handler.h \
gegl-graph.h \
gegl-init.h \
Curve - First iteration
On 5/16/07, Mark Probst wrote:
Hi!
I've implemented a curve class that does cubic spline interpolation (which is, as far as I could ascertain, what Photoshop uses, for example) and a contrast curve operation that uses it.
Several things are still missing, most of all chanting support and loading/saving of curves. Hints on how to implement the latter would be highly appreciated.
A question regarding the operation: Is it possible to make an operation that works on more than one format? I'd like the contrast curve to work on YA as well as RGBA. The latter because it's more general and the former because I need it for my application and using RGBA instead of YA would just slow it down.
What do I have to do to get this and the mono mixer incorporated into the official GEGL?
I have already added the mono-mixer to the workshop directory under operations,
I think we want a bit more functionality similar to the one already
existing in GIMP as well,
for instance making sure the values always add up to 1.0 to maintain
the range in the image
before moving it to the color directory where it properly will belong.
Speaking of color to gray scale, be sure to test the c2g operation
also in the workshop.
I am going to look into adding the other files as well, similarly I'll add the operation to the workshop dir first.
/Øyvind K.
Curve - First iteration
On 5/17/07, Øyvind Kolås wrote:
I think we want a bit more functionality similar to the one already existing in GIMP as well,
for instance making sure the values always add up to 1.0 to maintain the range in the image
I don't think that's a good idea. First of all, ensuring that they add up to 1.0 does not guarantee that the mono range stays withing 0.0-1.0 (if the red weight is 2.0 and green and blue are -0.5, then pure red will get a value of 2.0, while pure green and pure blue will get values of -0.5). Second, sometimes values which don't add up to 1.0 give useful ranges anyway. In practice you have to make such adjustments by looking at the resulting histogram. And limiting the weights to non-negative numbers won't do either. Here's an example of a photo that I made using negative weights:
http://www.flickr.com/photos/schani/336705437/
A question, though: Does an operation have to make sure that the values it produces are within the 0.0-1.0 range?
What other requirements would there be for making it a "proper" operation?
Speaking of color to gray scale, be sure to test the c2g operation also in the workshop.
I will.
bye
Mark
Curve - First iteration
On 5/17/07, Mark Probst wrote:
On 5/17/07, Øyvind Kolås wrote:
I think we want a bit more functionality similar to the one already existing in GIMP as well,
for instance making sure the values always add up to 1.0 to maintain the range in the imageI don't think that's a good idea. First of all, ensuring that they add up to 1.0 does not guarantee that the mono range stays withing 0.0-1.0 (if the red weight is 2.0 and green and blue are -0.5, then pure red will get a value of 2.0, while pure green and pure blue will get values of -0.5). Second, sometimes values which don't add up to 1.0 give useful ranges anyway. In practice you have to make such adjustments by looking at the resulting histogram. And limiting the weights to non-negative numbers won't do either. Here's an example of a photo that I made using negative weights:
The color mixer in gimp doesn't make sure they add up to 1.0 but makes sure the output range is 0.0-1.0. But it might be that one requires pre-normalized data for this to work correctly.
To provide full control over such a global color to gray scale mapping
is the ability to
position a grey axis within the RGB cube that the colors a projected
upon. To specify this
you would either need 6 values. Without having 6 values to do it, you
will need additional
levels adjustment to get the resulting values in the 0.0-1.0 range. It
is possible to find the
optimal orientation of this grey axis, making sure that the
distribution of the resulting histogram has the best possible spread
(not necessarily optimal esthetically, but optimal in
getting the most amount of detail out of an image).
A question, though: Does an operation have to make sure that the values it produces are within the 0.0-1.0 range?
No, it should be prepared to receive and produce values in the -inf .. inf range.
What other requirements would there be for making it a "proper" operation?
None really, but I do not want to introduce it amongst the operations in the default install until we have some confidence that it will not need to change much, since adding it to the default set of operations defines new API, there is even some existing operations I want to relocate before the next release.
When it comes to adding specialized support for grey scale formats, I
am afraid that will
have to wait. At the moment almost all operations read and produce
RGBA, and there is no addiitonal negotiation logic as to which formats
should be chosen. When such logic is added ideally the 32bit floating
point RGBA implementation should remain unchanged and additional
optimized variants using SIMD instructions, for particular pixel
formats and similar register as alternate versions (that can be
regression tested against the RGBA version). Exactly how this will be
implemented is still unclear since there are further pending changes
in the core of how GEGL does it's processing and what is demanded of
operations.
/Øyvind K.
Curve - First iteration
On 5/17/07, Øyvind Kolås wrote:
The color mixer in gimp doesn't make sure they add up to 1.0 but makes sure the output range is 0.0-1.0. But it might be that one requires pre-normalized data for this to work correctly.
That would be auto-level functionality which I'm not sure I want to put into this operation. Not sure whether it's the right place to do levels at all, actually. I think they should come in a separate step if you need them.
To provide full control over such a global color to gray scale mapping is the ability to
position a grey axis within the RGB cube that the colors a projected upon. To specify this
you would either need 6 values. Without having 6 values to do it, you will need additional
levels adjustment to get the resulting values in the 0.0-1.0 range.
Why 6? I count 3 for the direction plus 2 for the black- and white-points.
It is possible to find the
optimal orientation of this grey axis, making sure that the distribution of the resulting histogram has the best possible spread (not necessarily optimal esthetically, but optimal in getting the most amount of detail out of an image).
Can you give a reference?
regression tested against the RGBA version). Exactly how this will be implemented is still unclear since there are further pending changes in the core of how GEGL does it's processing and what is demanded of operations.
So at the moment I'll have to live with RGBA operations? Shouldn't the contrast curve be RGBA as well, then?
Btw: The file operations/workshop/envelope.h includes a file vector.h which is not in SVN.
bye
Mark
Curve - First iteration
On 5/17/07, Øyvind Kolås wrote:
Speaking of color to gray scale, be sure to test the c2g operation also in the workshop.
My first impression: With the default value of 3 samples it produces way too noisy images, even from very clean input. I'll play around with it more when I get to my application.
Am I right in assuming that it's an implementation of the color2gray algorithm?
bye Mark
Curve - First iteration
On 5/18/07, Mark Probst wrote:
On 5/17/07, Øyvind Kolås wrote:
Speaking of color to gray scale, be sure to test the c2g operation also in the workshop.
My first impression: With the default value of 3 samples it produces way too noisy images, even from very clean input. I'll play around with it more when I get to my application.
Am I right in assuming that it's an implementation of the color2gray algorithm?
The color2gray, it is a color2gray algorithm, and I would recommend increasing the number of iterations instead of increasing the number of samples. Think of running it with a low number of a iterations as a preview of the results of multiple iterations. (the method is work in progress and we're rtring to keep the implementation simple, rather than engineer ad-hoc noise reduction capabilities into it at the moment.)
/Øyvind K.