Re: [Gegl-developer] Scanline processing in a GeglOperation
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
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
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
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
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
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