RSS/Atom feed Twitter
Site is read-only, email is disabled

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.

7 of 7 messages available
Toggle history

Please log in to manage your subscriptions.

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
Mark Probst
2007-05-16 01:07:07 UTC (over 17 years ago)

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 \

Øyvind Kolås
2007-05-17 21:54:11 UTC (over 17 years ago)

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.

Mark Probst
2007-05-17 22:06:25 UTC (over 17 years ago)

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

Øyvind Kolås
2007-05-17 23:28:43 UTC (over 17 years ago)

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 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:

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.

Mark Probst
2007-05-17 23:45:33 UTC (over 17 years ago)

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

Mark Probst
2007-05-18 00:12:58 UTC (over 17 years ago)

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

Øyvind Kolås
2007-05-18 10:02:32 UTC (over 17 years ago)

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.