[gegl] apply-lens: some improvements
- From: Thomas Manni <tmanni src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] apply-lens: some improvements
- Date: Tue, 8 Sep 2015 15:00:12 +0000 (UTC)
commit 8cbdef878f568266b5adde37a80c58757f27d762
Author: Thomas Manni <thomas manni free fr>
Date: Thu Aug 6 19:24:39 2015 +0200
apply-lens: some improvements
* work with float instead of uint8
* do not allocate twice the size of the whole input
* do not use the whole input extent as cached region
* use sampler to fetch projected input pixels
* use iterator on the input/output buffers roi
operations/common/apply-lens.c | 326 ++++++++++++--------------
tests/compositions/reference/apply-lens.png | Bin 311382 -> 1091577 bytes
tests/compositions/reference/apply-lens2.png | Bin 212757 -> 933426 bytes
tests/compositions/reference/apply-lens3.png | Bin 454266 -> 1138856 bytes
4 files changed, 148 insertions(+), 178 deletions(-)
---
diff --git a/operations/common/apply-lens.c b/operations/common/apply-lens.c
index e4d28fb..6394dfd 100644
--- a/operations/common/apply-lens.c
+++ b/operations/common/apply-lens.c
@@ -21,6 +21,11 @@
* Copyright 2013 Stephan Seifermann <stephan seifermann student kit edu>
* Copyright 2013 Bastian Pirk <bastian pirk student kit edu>
* Copyright 2013 Pascal Giessler <pascal giessler student kit edu>
+ * Copyright 2015 Thomas Manni <thomas manni free fr>
+ */
+
+/* TODO: Find some better algorithm to calculate the roi for each dest
+ * rectangle. Right now it simply asks for the entire image...
*/
#include "config.h"
@@ -35,7 +40,7 @@ property_boolean (keep_surroundings, _("Keep original surroundings"), FALSE)
description(_("Keep image unchanged, where not affected by the lens."))
property_color (background_color, _("Background color"), "none")
- //ui_meta ("role", "color-secondary")
+ ui_meta ("role", "color-secondary")
#else
@@ -44,224 +49,185 @@ property_color (background_color, _("Background color"), "none")
#include "gegl-op.h"
#include <math.h>
-#include <stdio.h>
+
+typedef struct
+{
+ gfloat bg_color[4];
+ gdouble a, b, c;
+ gdouble asqr, bsqr, csqr;
+} AlParamsType;
/**
- * Computes the projected position (projx, projy) of the
- * original point (x, y) after passing through the lens
+ * Computes the original position (ox, oy) of the
+ * distorted point (x, y) after passing through the lens
* which is given by its center and its refraction index.
* See: Ellipsoid formula: x^2/a^2 + y^2/b^2 + z^2/c^2 = 1.
- *
- * @param a2 semiaxis a
- * @param b2 semiaxis b
- * @param c2 semiaxis c
- * @param x coordinate x
- * @param y coordinate y
- * @param refraction refraction index
- * @param projy inout of the projection x
- * @param projy inout of the projection y
*/
static void
-find_projected_pos (gfloat a2,
- gfloat b2,
- gfloat c2,
- gfloat x,
- gfloat y,
- gfloat refraction,
- gfloat *projx,
- gfloat *projy)
+find_undistorted_pos (gdouble x,
+ gdouble y,
+ gdouble refraction,
+ AlParamsType *params,
+ gdouble *ox,
+ gdouble *oy)
{
- gfloat z;
- gfloat nxangle, nyangle, theta1, theta2;
- gfloat ri1 = 1.0;
- gfloat ri2 = refraction;
+ gdouble z;
+ gdouble nxangle, nyangle, theta1, theta2;
+ gdouble ri1 = 1.0;
+ gdouble ri2 = refraction;
- z = sqrt ((1 - x * x / a2 - y * y / b2) * c2);
+ z = sqrt ((1 - x * x / params->asqr - y * y / params->bsqr) * params->csqr);
nxangle = acos (x / sqrt(x * x + z * z));
- theta1 = G_PI / 2 - nxangle;
+ theta1 = G_PI / 2.0 - nxangle;
theta2 = asin (sin (theta1) * ri1 / ri2);
- theta2 = G_PI / 2 - nxangle - theta2;
- *projx = x - tan (theta2) * z;
+ theta2 = G_PI / 2.0 - nxangle - theta2;
+ *ox = x - tan (theta2) * z;
nyangle = acos (y / sqrt (y * y + z * z));
- theta1 = G_PI / 2 - nyangle;
+ theta1 = G_PI / 2.0 - nyangle;
theta2 = asin (sin (theta1) * ri1 / ri2);
- theta2 = G_PI / 2 - nyangle - theta2;
- *projy = y - tan (theta2) * z;
+ theta2 = G_PI / 2.0 - nyangle - theta2;
+ *oy = y - tan (theta2) * z;
}
-/**
- * Prepare function of gegl filter.
- * @param operation given Gegl operation
- */
static void
prepare (GeglOperation *operation)
{
- const Babl *format = gegl_operation_get_source_format (operation, "input");
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ const Babl *format = babl_format ("RGBA float");
+
+ GeglRectangle *whole_region;
+ AlParamsType *params;
+
+ if (! o->user_data)
+ o->user_data = g_slice_new0 (AlParamsType);
+
+ params = (AlParamsType *) o->user_data;
+
+ whole_region = gegl_operation_source_get_bounding_box (operation, "input");
+
+ if (whole_region)
+ {
+ params->a = 0.5 * whole_region->width;
+ params->b = 0.5 * whole_region->height;
+ params->c = MIN (params->a, params->b);
+ params->asqr = params->a * params->a;
+ params->bsqr = params->b * params->b;
+ params->csqr = params->c * params->c;
+ }
+
+ gegl_color_get_pixel (o->background_color, format, params->bg_color);
gegl_operation_set_format (operation, "input", format);
gegl_operation_set_format (operation, "output", format);
}
-/**
- * Returns the cached region. This is an area filter, which acts on the whole image.
- * @param operation given Gegl operation
- * @param roi the rectangle of interest
- * @return result the new rectangle
- */
+static void
+finalize (GObject *object)
+{
+ GeglOperation *op = (void*) object;
+ GeglProperties *o = GEGL_PROPERTIES (op);
+
+ if (o->user_data)
+ {
+ g_slice_free (AlParamsType, o->user_data);
+ o->user_data = NULL;
+ }
+
+ G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
+}
+
static GeglRectangle
-get_cached_region (GeglOperation *operation,
- const GeglRectangle *roi)
+get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
{
- GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
- return result;
+ GeglRectangle result = {0,0,0,0};
+ GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+
+ if (!in_rect)
+ return result;
+ else
+ return *in_rect;
}
-/**
- * Process the gegl filter
- * @param operation the given Gegl operation
- * @param input the input buffer.
- * @param output the output buffer.
- * @param result the region of interest.
- * @param level the level of detail
- * @return True, if the filter was successfull applied.
- */
static gboolean
process (GeglOperation *operation,
GeglBuffer *input,
GeglBuffer *output,
- const GeglRectangle *result,
+ const GeglRectangle *roi,
gint level)
{
- GeglProperties *o = GEGL_PROPERTIES (operation);
- const Babl *input_format = gegl_buffer_get_format (input);
- const int bytes_per_pixel = babl_format_get_bytes_per_pixel (input_format);
-
- /**
- * src_buf, dst_buf:
- * Input- and output-buffers, containing the whole selection,
- * the filter should be applied on.
- *
- * src_pixel, dst_pixel:
- * pointers to the current source- and destination-pixel.
- * Using these pointers, the reading and writing can be delayed till the
- * end of the loop. Hence src_pixel can also point to background_color if
- * necessary.
- */
- guint8 *src_buf, *dst_buf, *src_pixel, *dst_pixel, background_color[bytes_per_pixel];
-
- /**
- * (x, y): Position of the pixel we compute at the moment.
- * (width, height): Dimensions of the lens
- * pixel_offset: For calculating the pixels position in src_buf / dst_buf.
- */
- gint x, y,
- width, height,
- pixel_offset, projected_pixel_offset;
-
- /**
- * Further parameters that are needed to calculate the position of a projected
- * further pixel at (x, y).
- */
- gfloat a, b, c,
- asqr, bsqr, csqr,
- dx, dy, dysqr,
- projected_x, projected_y,
- refraction_index;
-
- gboolean keep_surroundings;
-
- width = result->width;
- height = result->height;
-
- refraction_index = o->refraction_index;
- keep_surroundings = o->keep_surroundings;
- gegl_color_get_pixel (o->background_color, input_format, background_color);
-
- a = 0.5 * width;
- b = 0.5 * height;
- c = MIN (a, b);
- asqr = a * a;
- bsqr = b * b;
- csqr = c * c;
-
- /**
- * Todo: We might want to change the buffers sizes, as the memory consumption
- * could be rather large.However, due to the lens, it might happen, that one pixel from the
- * images center gets stretched to the whole image, so we will still need
- * at least a src_buf of size (width/2) * (height/2) * 4 to be able to
- * process one quarter of the image.
- */
- src_buf = gegl_malloc (width * height * bytes_per_pixel);
- dst_buf = gegl_malloc (width * height * bytes_per_pixel);
-
- gegl_buffer_get (input, result, 1.0, input_format, src_buf,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
-
- for (y = 0; y < height; y++)
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ AlParamsType *params = (AlParamsType *) o->user_data;
+ const Babl *format = babl_format ("RGBA float");
+
+ GeglSampler *sampler;
+ GeglBufferIterator *iter;
+ gint x, y;
+
+ sampler = gegl_buffer_sampler_new_at_level (input, format,
+ GEGL_SAMPLER_CUBIC, level);
+
+ iter = gegl_buffer_iterator_new (output, roi, level, format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ gegl_buffer_iterator_add (iter, input, roi, level, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
{
- /* dy is the current pixels distance (in y-direction) of the lenses center */
- dy = -((gfloat)y - b + 0.5);
-
- /**
- * To determine, whether the pixel is within the elliptical region affected
- * by the lens, we furthermore need the squared distance. So we calculate it
- * once for each row.
- */
- dysqr = dy * dy;
- for (x = 0; x < width; x++)
- {
- /* Again we need to calculate the pixels distance from the lens dx. */
- dx = (gfloat)x - a + 0.5;
+ gfloat *out_pixel = iter->data[0];
+ gfloat *in_pixel = iter->data[1];
- /* Given x and y we can now determine the pixels offset in our buffer. */
- pixel_offset = (x + y * width) * bytes_per_pixel;
+ for (y = iter->roi->y; y < iter->roi->y + iter->roi->height; y++)
+ {
+ gdouble dy, dysqr;
- /* As described above, we only read and write image data once. */
- dst_pixel = &dst_buf[pixel_offset];
+ dy = -((gdouble) y - params->b + 0.5);
+ dysqr = dy * dy;
- if (dysqr < (bsqr - (bsqr * dx * dx) / asqr))
+ for (x = iter->roi->x; x < iter->roi->x + iter->roi->width; x++)
{
- /**
- * If (x, y) is inside the affected region, we can find its projected
- * position, calculate the projected_pixel_offset and set the src_pixel
- * to where we want to copy the pixel-data from.
- */
- find_projected_pos (asqr, bsqr, csqr, dx, dy, refraction_index,
- &projected_x, &projected_y);
-
- projected_pixel_offset = ( (gint)(-projected_y + b) * width +
- (gint)( projected_x + a) ) * bytes_per_pixel;
-
- src_pixel = &src_buf[projected_pixel_offset];
- }
- else
- {
- /**
- * Otherwise (that is for pixels outside the lens), we could either leave
- * the image data unchanged, or set it to a specified 'background_color',
- * depending on the user input.
- */
- if (keep_surroundings)
- src_pixel = &src_buf[pixel_offset];
+ gdouble dx, dxsqr;
+
+ dx = (gdouble) x - params->a + 0.5;
+ dxsqr = dx * dx;
+
+ if (dysqr < (params->bsqr - (params->bsqr * dxsqr) / params->asqr))
+ {
+ /**
+ * If (x, y) is inside the affected region, we can find its original
+ * position and fetch the pixel with the sampler
+ */
+ gdouble ox, oy;
+ find_undistorted_pos (dx, dy, o->refraction_index, params,
+ &ox, &oy);
+
+ gegl_sampler_get (sampler, ox + params->a, params->b - oy,
+ NULL, out_pixel, GEGL_ABYSS_NONE);
+ }
else
- src_pixel = background_color;
+ {
+ /**
+ * Otherwise (that is for pixels outside the lens), we could either leave
+ * the image data unchanged, or set it to a specified 'background_color',
+ * depending on the user input.
+ */
+ if (o->keep_surroundings)
+ memcpy (out_pixel, in_pixel, sizeof (gfloat) * 4);
+ else
+ memcpy (out_pixel, params->bg_color, sizeof (gfloat) * 4);
+ }
+
+ out_pixel += 4;
+ in_pixel += 4;
}
-
- /**
- * At the end, we can copy the src_pixel (which was determined above), to
- * dst_pixel.
- */
- memcpy (dst_pixel, src_pixel, bytes_per_pixel);
}
- }
-
- gegl_buffer_set (output, result, 0, gegl_buffer_get_format (output), dst_buf,
- GEGL_AUTO_ROWSTRIDE);
+ }
- gegl_free (dst_buf);
- gegl_free (src_buf);
+ g_object_unref (sampler);
return TRUE;
}
@@ -269,6 +235,7 @@ process (GeglOperation *operation,
static void
gegl_op_class_init (GeglOpClass *klass)
{
+ GObjectClass *object_class;
GeglOperationClass *operation_class;
GeglOperationFilterClass *filter_class;
gchar *composition =
@@ -288,12 +255,14 @@ gegl_op_class_init (GeglOpClass *klass)
"</node>"
"</gegl>";
+ object_class = G_OBJECT_CLASS (klass);
operation_class = GEGL_OPERATION_CLASS (klass);
filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+ object_class->finalize = finalize;
operation_class->threaded = FALSE;
operation_class->prepare = prepare;
- operation_class->get_cached_region = get_cached_region;
+ operation_class->get_required_for_output = get_required_for_output;
filter_class->process = process;
gegl_operation_class_set_keys (operation_class,
@@ -301,7 +270,8 @@ gegl_op_class_init (GeglOpClass *klass)
"title", _("Apply Lens"),
"categories", "map",
"license", "GPL3+",
- "description", _("Simulates the optical distoration caused by having an elliptical lens over the image"),
+ "description", _("Simulates the optical distortion caused by having "
+ "an elliptical lens over the image"),
"reference-composition", composition,
NULL);
}
diff --git a/tests/compositions/reference/apply-lens.png b/tests/compositions/reference/apply-lens.png
index 045284d..5e65ee3 100644
Binary files a/tests/compositions/reference/apply-lens.png and b/tests/compositions/reference/apply-lens.png
differ
diff --git a/tests/compositions/reference/apply-lens2.png b/tests/compositions/reference/apply-lens2.png
index f560a14..13b4566 100644
Binary files a/tests/compositions/reference/apply-lens2.png and
b/tests/compositions/reference/apply-lens2.png differ
diff --git a/tests/compositions/reference/apply-lens3.png b/tests/compositions/reference/apply-lens3.png
index 088ab76..37ffe37 100644
Binary files a/tests/compositions/reference/apply-lens3.png and
b/tests/compositions/reference/apply-lens3.png differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]