Re: [Gegl-developer] Scanline processing in a GeglOperation

2008-04-24 Thread Hans Petter Jansson
On Wed, 2008-04-23 at 14:41 +0100, Øyvind Kolås wrote:
 On Tue, Apr 22, 2008 at 7:59 AM, Hans Petter Jansson [EMAIL PROTECTED] 
 wrote:
  On Mon, 2008-04-21 at 20:36 +0100, Øyvind Kolås wrote:

   http://hpjansson.org/temp/meadow-dithered.png
 
   The picture has one bit per channel for a total of 8 colors, making it a
   true retro experience. From left to right -- original, thresholding,
   Bayer, F-S, covariant random and random dithering.

 This might indeed be useful at some point, but right now I do not think the
 GEGL architecture is flexible enough to warrant compilation and installation
 by default. Thus I will drop this .c file into the workshop directory where
 various works in progress reside. It is possible to write specialized ops
 that would read the RGBA u16 and output the required file. (Using
 R'G'B'A u16) would probably give slightly better results assuming the
 displays and data involved are roughly sRGB data.

Thanks for taking it in! I'm thinking a more GEGLy approach might be to
do the quantization in an input-output filter and the dithering in an
optional input+aux - output filter that would take the quantized image
as aux and dither according to the differences from the original image.
That would make sense for error diffusion dithering, at least. And since
there are lots of ways to do the quantization step too, it would only
make sense to do it separately.

-- 
Hans Petter Jansson [EMAIL PROTECTED]

___
Gegl-developer mailing list
Gegl-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gegl-developer


Re: [Gegl-developer] Scanline processing in a GeglOperation

2008-04-23 Thread Øyvind Kolås
On Tue, Apr 22, 2008 at 7:59 AM, Hans Petter Jansson [EMAIL PROTECTED] wrote:
 On Mon, 2008-04-21 at 20:36 +0100, Øyvind Kolås wrote:

  http://hpjansson.org/temp/meadow-dithered.png

  The picture has one bit per channel for a total of 8 colors, making it a
  true retro experience. From left to right -- original, thresholding,
  Bayer, F-S, covariant random and random dithering.

This might indeed be useful at some point, but right now I do not think the
GEGL architecture is flexible enough to warrant compilation and installation
by default. Thus I will drop this .c file into the workshop directory where
various works in progress reside. It is possible to write specialized ops
that would read the RGBA u16 and output the required file. (Using
R'G'B'A u16) would probably give slightly better results assuming the
displays and data involved are roughly sRGB data.

A plan for supporting pallettized images is forming somwhere on the
horizon for GEGL. It would probably involve a specialized babl format and
the ability to attache a floating point RGBA pallette to the format.
Another similar
issue for babl is that it isn't currently capable of generating pixels
for formats where
the components are not a multiple of 8, it is due to these short comings in GEGL
I've placed color-reduction in the workshop for now.

For some ramblings about indexed/palettized images take a look at:
http://codecave.org/?weblog_id=indexed_metamers

/Øyvind K.
-- 
«The future is already here. It's just not very evenly distributed»
 -- William Gibson
http://pippin.gimp.org/ http://ffii.org/
___
Gegl-developer mailing list
Gegl-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gegl-developer


Re: [Gegl-developer] Scanline processing in a GeglOperation

2008-04-22 Thread Hans Petter Jansson
On Mon, 2008-04-21 at 20:36 +0100, Øyvind Kolås wrote:

 You need to do the following:
 [...]

Thanks for all the help! I wrote an operation to do color reduction to a
specified number of bits per channel, employing one out of a couple of
potential color compensation strategies.

I'm attaching it in case you want to use it for something, or re-use
parts of the code in a better operation.

I put a test image up at

http://hpjansson.org/temp/meadow-dithered.png

The picture has one bit per channel for a total of 8 colors, making it a
true retro experience. From left to right -- original, thresholding,
Bayer, F-S, covariant random and random dithering.

-- 
Hans Petter Jansson [EMAIL PROTECTED]
/* 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 2008 Hans Petter Jansson [EMAIL PROTECTED]
 */

#ifdef GEGL_CHANT_PROPERTIES

gegl_chant_int (red_bits, Red bits, 1, 16, 16, Number of bits for red channel)
gegl_chant_int (green_bits, Green bits, 1, 16, 16, Number of bits for green channel)
gegl_chant_int (blue_bits, Blue bits, 1, 16, 16, Number of bits for blue channel)
gegl_chant_int (alpha_bits, Alpha bits, 1, 16, 16, Number of bits for alpha channel)
gegl_chant_string (dither_type, Dither, none, Dithering strategy (none, random, random-covariant, bayer, floyd-steinberg))

#else

#define GEGL_CHANT_TYPE_FILTER
#define GEGL_CHANT_C_FILE   color-reduction.c

#include gegl-chant.h

static void
prepare (GeglOperation *operation)
{
  gegl_operation_set_format (operation, input, babl_format (RGBA u16));
  gegl_operation_set_format (operation, output, babl_format (RGBA u16));
}

static void
generate_channel_masks (guint *channel_bits, guint *channel_mask)
{
  gint i;

  for (i = 0; i  4; i++)
channel_mask [i] = ~((1  (16 - channel_bits [i])) - 1);
}

static guint
quantize_value (guint value, guint n_bits, guint mask)
{
  gint i;

  value = mask;

  for (i = n_bits; i  16; i += n_bits)
value |= value  i;

  return value;
}

static void
process_floyd_steinberg (GeglBuffer *input,
 GeglBuffer *output,
 guint *channel_bits)
{
  const GeglRectangle *input_rect;
  GeglRectangleline_rect;
  guint16 *line_buf;
  gdouble *error_buf [2];
  guintchannel_mask [4];
  gint y;

  input_rect = gegl_buffer_get_extent (input);

  line_rect.x  = input_rect-x;
  line_rect.y  = input_rect-y;
  line_rect.width  = input_rect-width;
  line_rect.height = 1;

  line_buf  = g_new  (guint16, line_rect.width * 4);
  error_buf [0] = g_new0 (gdouble, line_rect.width * 4);
  error_buf [1] = g_new0 (gdouble, line_rect.width * 4);

  generate_channel_masks (channel_bits, channel_mask);

  for (y = 0; y  input_rect-height; y++)
  {
gdouble  *error_buf_swap;
gint  step;
gint  start_x;
gint  end_x;
gint  x;

/* Serpentine scanning; reverse direction every row */

if (y  1)
{
  start_x = input_rect-width - 1;
  end_x   = -1;
  step= -1;
}
else
{
  start_x = 0;
  end_x   = input_rect-width;
  step= 1;
}

/* Pull input row */

gegl_buffer_get (input, 1.0, line_rect, babl_format (RGBA u16), line_buf, GEGL_AUTO_ROWSTRIDE);

/* Process the row */

for (x = start_x; x != end_x; x += step)
{
  guint16  *pixel = line_buf [x * 4];
  guint ch;

  for (ch = 0; ch  4; ch++)
  {
gdouble value;
gdouble value_clamped;
gdouble quantized;
gdouble qerror;

value = pixel [ch] + error_buf [0] [x * 4 + ch];
value_clamped = CLAMP (value, 0.0, 65535.0);
quantized = quantize_value ((guint) (value_clamped + 0.5), channel_bits [ch], channel_mask [ch]);
qerror= value - quantized;

pixel [ch] = (guint16) quantized;

/* Distribute the error */

error_buf [1] [x * 4 + ch] += qerror * 5.0 / 16.0;  /* Down */

if (x + step = 0  x + step  input_rect-width)
{
  error_buf [0] [(x + step) * 4 + ch] += qerror * 6.0 / 16.0;  /* Ahead */
  error_buf [1] [(x + step) * 4 + ch] += qerror * 1.0 / 16.0;  /* Down, ahead */
}

if (x - step = 0  x - step  input_rect-width)
{
  error_buf [1] [(x - step) * 4 + ch] += 

Re: [Gegl-developer] Scanline processing in a GeglOperation

2008-04-21 Thread Hans Petter Jansson
On Mon, 2008-04-21 at 11:18 +0100, Øyvind Kolås wrote:

 The floyd steinberg type of error diffusion is going a bit contrary to
 the way GEGL is designed, since effectivly to bottomright most pixel
 depends on the contents of the entire image. Thus if the upper left
 pixel in the image changes the entire image needs to change as well.
 Thus to correctly implement floyd steinberg you would actually have to
 request the processing of the entire image and not piece by piece thus
 losing the ability to handle larger than RAM images. The stretch
 contrast operation is another operation where single pixels depends on
 the entire image.

The way I did this in a GIMP 2.4 plugin is I load a row at a time from
the backing store, and keep two rows's worth of quantization error data
in memory at all times -- one for the current row and another for the
next. When a row is complete, I swap the error buffers and load the next
row of image data.

Would an approach like this be possible with GEGL, or is it outside its
scope?

 There are other digital halftoning methods that probably are a much
 better match for GEGLs processing design. Where the value for any
 given pixel only depends on it's neighbourhood and not the entire
 image.

Could you recommend one? I've already tried random dithering, which
looks terrible, especially at low resolutions, and Bayer, which is very
poor in details (but good on gradients).

Most of the halfway decent algorithms I'm familiar with are based on the
Floyd-Steinberg method of error diffusion.

 (Do also note that GEGL currently does not support 8bit
 component, so the memory use if you are going all the way down to
 1bit/component is going to be massive as you would have to use
 8bit/component to simulate it.)

That's fine -- I'm doing color reduction with dithering on images to be
used in an OS installer, which will run at low resolutions and 16-bit
(565) color. How they're stored is an implementation detail, but how
they look is important.

-- 
Hans Petter Jansson [EMAIL PROTECTED]

___
Gegl-developer mailing list
Gegl-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gegl-developer


Re: [Gegl-developer] Scanline processing in a GeglOperation

2008-04-21 Thread Hans Petter Jansson
On Mon, 2008-04-21 at 16:43 +0100, Øyvind Kolås wrote:
 On Mon, Apr 21, 2008 at 11:18 AM, Øyvind Kolås [EMAIL PROTECTED] wrote:

   Thus to correctly implement floyd steinberg you would actually have to
   request the processing of the entire image and not piece by piece thus
   losing the ability to handle larger than RAM images.

 This isn't entirely true as you can fetch and store individual
 scanlines when reading/writing from the involved GeglBuffers as the op
 is processing for the entire image, nevertheless such an op would need
 the entire image as source data to update the bottomrightmost pixel.
 And it would make future scheduling and paralellization of graphs
 involving such ops much harder than needed.

Yeah, that's what I thought. But insofar as my implementation is
concerned, I don't care about speed, as long as it can be done.

So I guess the question is: How do I request processing of the entire
image, on a row by row basis?

 I imagine either green
 noise or blue noise based halftoning/dither might have properties that
 align better with the GEGL architecture, not to mention that those
 approaches do not suffer from similar artifacts of repeating patterns
 that floyd steinberg dithering suffers from.

Well, I think the aesthetic merits of the Floyd-Steinberg class of error
diffusion algorithms is outside the scope of this discussion :)

-- 
Hans Petter Jansson [EMAIL PROTECTED]

___
Gegl-developer mailing list
Gegl-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gegl-developer


Re: [Gegl-developer] Scanline processing in a GeglOperation

2008-04-21 Thread Øyvind Kolås
On Mon, Apr 21, 2008 at 7:51 PM, Hans Petter Jansson [EMAIL PROTECTED] wrote:
 On Mon, 2008-04-21 at 16:43 +0100, Øyvind Kolås wrote:
   On Mon, Apr 21, 2008 at 11:18 AM, Øyvind Kolås [EMAIL PROTECTED] wrote:

 Thus to correctly implement floyd steinberg you would actually have to
 request the processing of the entire image and not piece by piece thus
 losing the ability to handle larger than RAM images.

   This isn't entirely true as you can fetch and store individual
   scanlines when reading/writing from the involved GeglBuffers as the op
   is processing for the entire image, nevertheless such an op would need
   the entire image as source data to update the bottomrightmost pixel.
   And it would make future scheduling and paralellization of graphs
   involving such ops much harder than needed.

  Yeah, that's what I thought. But insofar as my implementation is
  concerned, I don't care about speed, as long as it can be done.

You need to do the following:

static GeglRectangle
get_required_for_output (GeglOperation*operation,
 const gchar *input_pad,
 const GeglRectangle *roi)
{
  /* request that we have the entire bounding box to operate on */
  return *gegl_operation_source_get_bounding_box (operation, input);
}

static GeglRectangle
get_cached_region (GeglOperation   *operation,
   const GeglRectangle *roi)
{
  /* request that all of the op should be cached for this request */
  return *gegl_operation_source_get_bounding_box (operation, input);
}

As well as setting the corresponding methods on the
GeglOperationClass, see other operations for examples.

At this stage:

The input and output regions requested for processing
should be the same and you can start reading/writing linear
rectangular subregions to the input buffer as well as writing results
to the output buffer. You could at this stage choose to do the entire
buffer in one chunk, this is where an intelligent implementation
allows to still work on larger than RAM images, while a stupid one
needs the entire memory temporarily in memory as a linear buffer.

  So I guess the question is: How do I request processing of the entire
  image, on a row by row basis?

If you look at the stretch contrast operation it is operating on a
line by line basis when it is doing the actual stretching (it is slow
though and allocates a huge linear buffer for the min/max detection
instead of doing it in chunks).

/Øyvind K.
-- 
«The future is already here. It's just not very evenly distributed»
 -- William Gibson
http://pippin.gimp.org/ http://ffii.org/
___
Gegl-developer mailing list
Gegl-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gegl-developer