Proposition : GeglInterpolator
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.
Proposition : GeglInterpolator | Geert Jordaens | 11 Oct 21:33 |
Proposition : GeglInterpolator | Daniel Rogers | 12 Oct 00:38 |
Proposition : GeglInterpolator | Geert Jordaens | 12 Oct 08:18 |
Proposition : GeglInterpolator | Øyvind Kolås | 12 Oct 11:03 |
Proposition : GeglInterpolator | Daniel Rogers | 12 Oct 16:32 |
Proposition : GeglInterpolator | Øyvind Kolås | 13 Oct 16:29 |
Proposition : GeglInterpolator | Daniel Rogers | 13 Oct 20:46 |
Proposition : GeglInterpolator | Geert Jordaens | 15 Oct 17:11 |
Proposition : GeglInterpolator | Øyvind Kolås | 16 Oct 00:24 |
Proposition : GeglInterpolator | Øyvind Kolås | 16 Oct 00:27 |
Proposition : GeglInterpolator | geert.jordaens-CNXmb7IdZIWZIoH1IeqzKA@public.gmane.org | 16 Oct 16:48 |
Proposition : GeglInterpolator | Øyvind Kolås | 16 Oct 17:55 |
Proposition : GeglInterpolator | geert.jordaens-CNXmb7IdZIWZIoH1IeqzKA@public.gmane.org | 17 Oct 09:11 |
Proposition : GeglInterpolator | Øyvind Kolås | 17 Oct 09:27 |
Proposition : GeglInterpolator | Daniel Rogers | 17 Oct 19:08 |
Proposition : GeglInterpolator | Øyvind Kolås | 18 Oct 13:01 |
Proposition : GeglInterpolator | Geert Jordaens | 18 Oct 20:27 |
Proposition : GeglInterpolator
In order not to rewrite interpolation fnctionality for every operation I would like to propose following API.
GeglInterpolator
+- GeglInterpolatorLinear
|
+ GeglInterpolatorCubic
|
+ GeglInterpolatorLanczos
usage :
/* first create the interpolator */
GeglInterpolatorLinear *interpolator = g_object_new
(GEGL_TYPE_INTERPLATOR_LINEAR,
"source", buffer,
"format",
babl_format ("RaGaBaA float"),
NULL);
gegl_interpolator_prepare(interpolator); /* prepares a linear buffer in the format suitable for the interpolation routine. */
gegl_interpolator_get(interpolator, x, y, dst); /* returns a interpolated pixel in dst */
x,y = source coördinates type double dst = interpolate pixel in format specified by "format" property.
Please comment on this, untested code can be found at : http://users.telenet.be/geert.jordaens/gegl_interpolator
Geert
Proposition : GeglInterpolator
On Wed, 2006-10-11 at 21:33 +0200, Geert Jordaens wrote:
In order not to rewrite interpolation fnctionality for every operation I would like to propose following API.
GeglInterpolator +- GeglInterpolatorLinear
|
+ GeglInterpolatorCubic
|
+ GeglInterpolatorLanczosusage :
/* first create the interpolator */ GeglInterpolatorLinear *interpolator = g_object_new (GEGL_TYPE_INTERPLATOR_LINEAR,
"source", buffer, "format", babl_format ("RaGaBaA float"),
NULL);gegl_interpolator_prepare(interpolator); /* prepares a linear buffer in the format suitable for the interpolation routine. */
gegl_interpolator_get(interpolator, x, y, dst); /* returns a interpolated pixel in dst */
sllooooooooooow. I've encountered this oh-so-wonderful abstraction before (JAI uses it, among others). Making a method call in the inner loop of your pixel calculation routine is a big-nono. The first optimization you will make is to remove that. Typically, the common case will not use the abstraction, and an operator will be provided that can accept the general interpolation object, but the operator will be so slow no one will use it. It is wonderful when developing your foundations, to be sure, but it will eventually be ripped out as soon as someone tries to speed it up.
If you can provide versions that accept arrays as input, then the general case can be used everywhere. Actually that can be made a general rule. Anything that manipulates pixels much accept arrays as input (possibly masks as well) or they will be ripped out at the earliest opportunity.
Proposition : GeglInterpolator
Daniel Rogers wrote:
On Wed, 2006-10-11 at 21:33 +0200, Geert Jordaens wrote:
In order not to rewrite interpolation fnctionality for every operation I would like to propose following API.
GeglInterpolator +- GeglInterpolatorLinear
|
+ GeglInterpolatorCubic
|
+ GeglInterpolatorLanczosusage :
/* first create the interpolator */ GeglInterpolatorLinear *interpolator = g_object_new (GEGL_TYPE_INTERPLATOR_LINEAR,
"source", buffer, "format", babl_format ("RaGaBaA float"),
NULL);gegl_interpolator_prepare(interpolator); /* prepares a linear buffer in the format suitable for the interpolation routine. */
gegl_interpolator_get(interpolator, x, y, dst); /* returns a interpolated pixel in dst */
sllooooooooooow. I've encountered this oh-so-wonderful abstraction before (JAI uses it, among others). Making a method call in the inner loop of your pixel calculation routine is a big-nono. The first optimization you will make is to remove that. Typically, the common case will not use the abstraction, and an operator will be provided that can accept the general interpolation object, but the operator will be so slow no one will use it. It is wonderful when developing your foundations, to be sure, but it will eventually be ripped out as soon as someone tries to speed it up.
If you can provide versions that accept arrays as input, then the general case can be used everywhere. Actually that can be made a general rule. Anything that manipulates pixels much accept arrays as input (possibly masks as well) or they will be ripped out at the earliest opportunity.
If i understand it correctly it is the GObject that is causing the slowdown?
I don't see any difference with the pixel_iterator in Gimp.
In your last paragraph, i assume you mean writing a function that can be used on a linear buffer :
interpolate_buffer_linear ( srcbuf, x0, y0, width, height, x, y, destbuf );
srcbuf = linear source buffer (array)
x0,y0 = integer origin
width, height = integer size of buffer
x,y = double position to be interpolated.
destbuf = destination pixel.
Then it is up to the operation to make sure the srcbuf/dstbuf is in a suitable format?
Proposition : GeglInterpolator
On 10/12/06, Daniel Rogers wrote:
sllooooooooooow. I've encountered this oh-so-wonderful abstraction before (JAI uses it, among others). Making a method call in the inner loop of your pixel calculation routine is a big-nono. The first optimization you will make is to remove that. Typically, the common case will not use the abstraction, and an operator will be provided that can accept the general interpolation object, but the operator will be so slow no one will use it. It is wonderful when developing your foundations, to be sure, but it will eventually be ripped out as soon as someone tries to speed it up.
Any such optimizations are premature optimizations at the moment. At the current stage making the source code of the included operations general as well as easy to read, understand and debug is more important than efficient code.
I guess the overhead of a function call is smaller in C than in Java, it might also be possible to inline the actual interpolating sampling functions.
The operation that geert is currently working on is a displacement map, what you are suggesting would lead to writing a separate displacement map operation for each interpolation method. This would lead to very unwieldy code.
If you can provide versions that accept arrays as input, then the general case can be used everywhere.
The proposed solution is the closest you will get to operate directly on a linear buffer.
Actually that can be made a
general rule. Anything that manipulates pixels much accept arrays as input (possibly masks as well) or they will be ripped out at the earliest opportunity.
My guess is that the proposed solution is a good compromise, at a later stage more optimized version can be added. And then I actually mean adding code, not replacing the current code. The reference version of the base operation set int floating point should remain readable code, and optimized version of 8bit/16bit/floating point that are regression tested against the reference version.
Proposition : GeglInterpolator
On Oct 12, 2006, at 2:03 AM, Øyvind Kolås wrote:
On 10/12/06, Daniel Rogers wrote:
sllooooooooooow. I've encountered this oh-so-wonderful abstraction before (JAI uses it, among others). Making a method call in the inner loop of your pixel calculation routine is a big-nono. The first optimization you will make is to remove that. Typically, the common case will not use the abstraction, and an operator will be provided that
can accept the general interpolation object, but the operator will be so
slow no one will use it. It is wonderful when developing your foundations, to be sure, but it will eventually be ripped out as soon as
someone tries to speed it up.Any such optimizations are premature optimizations at the moment. At the
current stage making the source code of the included operations general as well
as easy to read, understand and debug is more important than efficient code.I guess the overhead of a function call is smaller in C than in Java, it might
also be possible to inline the actual interpolating sampling functions.
In this particular case it will not be possible to inline. Since the relevant GObject function must be virtual it won't be able to be inlined. You can actually manage to inline it anyway in a more managable language, since you can declare a class final and then specify the final class as the concrete type.
The operation that geert is currently working on is a displacement map, what
you are suggesting would lead to writing a separate displacement map operation
for each interpolation method. This would lead to very unwieldy code.
Yeah, the IP libraries I've worked with all do something similar, unfortunately.
If you can provide versions that accept arrays as input, then the general case can be used everywhere.
The proposed solution is the closest you will get to operate directly on a linear buffer.
I'm not going to be pushy about this. Just declare my experience.
Proposition : GeglInterpolator
On 10/12/06, Daniel Rogers wrote:
On Oct 12, 2006, at 2:03 AM, Øyvind Kolås wrote:
On 10/12/06, Daniel Rogers wrote:
sllooooooooooow. I've encountered this oh-so-wonderful abstraction before (JAI uses it, among others). Making a method call in the inner loop of your pixel calculation routine is a big-nono. The first optimization you will make is to remove that. Typically, the common
Any such optimizations are premature optimizations at the moment. At the
current stage making the source code of the included operations general as well
as easy to read, understand and debug is more important than efficient code.
The operation that geert is currently working on is a displacement map, what
you are suggesting would lead to writing a separate displacement map operation
for each interpolation method. This would lead to very unwieldy code.
Yeah, the IP libraries I've worked with all do something similar, unfortunately.
Something similar to what? Having a proxy object for doing interpolation or implement separate displacement etc. operation per interpolator?
If you mean the first, I do not find readable code unfortunate, a problem with the current GIMP code base IMHO is that some algorithms are optimized beyond recognition.
/Øyvind K.
Proposition : GeglInterpolator
On Fri, 2006-10-13 at 16:29 +0200, Øyvind Kolås wrote:
On 10/12/06, Daniel Rogers wrote:
On Oct 12, 2006, at 2:03 AM, Øyvind Kolås wrote:
On 10/12/06, Daniel Rogers wrote:
sllooooooooooow. I've encountered this oh-so-wonderful
abstraction
before (JAI uses it, among others). Making a method call in the
inner
loop of your pixel calculation routine is a big-nono. The first optimization you will make is to remove that. Typically, the
common
Any such optimizations are premature optimizations at the moment. At the
current stage making the source code of the included operations general as well
as easy to read, understand and debug is more important than efficient code.
The operation that geert is currently working on is a displacement map, what
you are suggesting would lead to writing a separate displacement map operation
for each interpolation method. This would lead to very unwieldy
code.
Yeah, the IP libraries I've worked with all do something similar, unfortunately.
Something similar to what? Having a proxy object for doing
interpolation or
implement separate displacement etc. operation per interpolator?
If you mean the first, I do not find readable code unfortunate, a problem with the current GIMP code base IMHO is that some algorithms are optimized beyond recognition.
No, I meant the later. I am all in favor of readable code, actually. I like the particular interface we are discussing. Too slow is still too slow, however. My experience has shown that when performance matters, that abstraction is the first to go. I'm not saying it's a bad idea, I am just bringing up what I've found to be a thorny issue.
Proposition : GeglInterpolator
I've added the implementation to bug report : Bug 360888 – Interpolation for operations
GeglInterpolator
+ GeglInterpolatorNearest
|
+ GeglInterpolatorLinear
|
+ GeglInterpolatorCubic
|
+ GeglInterpolatorLanczos
usage :
void displacementmap(GeglBuffer *src,
GeglBuffer *aux,
GeglBuffer *dst,
gdouble scale,
gint cx,
gint cy)
{
gint i, j, k;
gint dst_pos;
gdouble dx, dy, xc, yc;
gfloat *buf = g_malloc0 (4 * 4 * 4);
gfloat *aux_buf = g_malloc0 (aux->width * aux->height * 4 * 4);
gfloat *dst_buf = g_malloc0 (dst->width * dst->height * 4 * 4);
gegl_buffer_get_fmt (aux, aux_buf, babl_format ("RGBA float"));
gegl_buffer_get_fmt (dst, dst_buf, babl_format ("RaGaBaA float"));
* GeglInterpolatorCubic *interpolator = g_object_new
(GEGL_TYPE_INTERPOLATOR_CUBIC,
"input", src,
"format", babl_format ("RaGaBaA float"),
NULL);
gegl_interpolator_prepare(GEGL_INTERPOLATOR(interpolator)) ; *
for (i = 0; i < src->height; i++)
for (j = 0; j < src->width; j++)
{
/* Calculate x/y - offset relative to origin */
dx = (gdouble) j / (src->width-1.0);
dy = (gdouble) i / (src->height-1.0);
xc = j + (gint) (displacement (aux, aux_buf, dx, dy, cx) * scale);
yc = i + (gint) (displacement (aux, aux_buf, dx, dy, cy) * scale);
* gegl_interpolator_get(GEGL_INTERPOLATOR(interpolator), xc, yc, buf);
/* returns a interpolated pixel in dst */ *
dst_pos = (i * dst->width + j )* 4;
for (k = 0 ; k < 4 ; k++)
dst_buf[dst_pos+k]= buf[k];
}
gegl_buffer_set_fmt (dst, dst_buf, babl_format ("RaGaBaA float"));
g_object_unref (interpolator);
g_free (aux_buf);
g_free (dst_buf);
}
Proposition : GeglInterpolator
On 10/15/06, Geert Jordaens wrote:
I've added the implementation to bug report : Bug 360888 – Interpolation
snip
usage :
snip
Maybe something along the following lines would be a better API?
/Øyvind K.
void
displacementmap (GeglBuffer *src,
GeglBuffer *aux,
GeglBuffer *dst,
gdouble scale,
gint cx,
gint cy)
{
gint i, j;
gint dst_pos=0;
gfloat *buf = g_malloc0 (4 * 4 * 4);
gfloat *aux_buf = g_malloc0 (aux->width * aux->height * 4 * 4);
gfloat *dst_buf = g_malloc0 (dst->width * dst->height * 4 * 4);
gegl_buffer_get_fmt (aux, aux_buf, babl_format ("RGBA float"));
gegl_buffer_get_fmt (dst, dst_buf, babl_format ("RaGaBaA float"));
*GeglSampler *sampler = g_object_new (GEGL_TYPE_SAMPLER_CUBIC,
"input", src,
"format", babl_format ("RaGaBaA float"),
"x", 50,
"y", 50,
"width", 200, /* or a rect could be passed, or a subbuffer of
"height", 200, buffer expected to be created before making a
sampler */
NULL);
for (i = 0; i < src->height; i++)
for (j = 0; j < src->width; j++)
{
gdouble dx, dy, xc, yc;
/* Calculate x/y - offset relative to origin */ dx = (gdouble) j / (src->width-1.0); dy = (gdouble) i / (src->height-1.0); xc = j + (gint) (displacement (aux, aux_buf, dx, dy, cx) * scale); yc = i + (gint) (displacement (aux, aux_buf, dx, dy, cy) * scale);
/* returns a freshly sampled pixel in dst */ gegl_sampler_get (sampler, xc, yc, buf);
#if 0
/* using this form, might make more sense, since it would allow accurate
sampling as needed by affine/perspective transforms that are scaling
down as well as when scaling up/interpolating
*/
gegl_sampler_get_quad (sampler, xc-0.5, yc-0.5,
xc+0.5, yc-0.5,
xc+0.5, yc+0.5,
xc-0.5, yc+0.5,
buf);
#endif
{
int component;
for (component = 0 ; component < 4 ; component++)
dst_buf[dst_pos+component]= buf[component];
}
dst_pos+=4;
}
gegl_buffer_set_fmt (dst, dst_buf, babl_format ("RaGaBaA float"));
g_object_unref (sampler);
g_free (aux_buf);
g_free (dst_buf);
}
Proposition : GeglInterpolator
On 10/16/06, Øyvind Kolås wrote:
#if 0
/* using this form, might make more sense, since it would allow accurate sampling as needed by affine/perspective transforms that are scaling down as well as when scaling up/interpolating */
gegl_sampler_get_quad (sampler, xc-0.5, yc-0.5, xc+0.5, yc-0.5, xc+0.5, yc+0.5, xc-0.5, yc+0.5, buf); #endif
I think this is wrong, each of the seperate corners of the output pixel should probably have their coordinates computed separatly, before being passed in.
/Øyvind K.
Proposition : GeglInterpolator
The change below i think would complicate the usage. Since every time accessing the interpolator the coordinates for accessing should be ajusted by the value of x, y.
*GeglSampler *sampler = g_object_new (GEGL_TYPE_SAMPLER_CUBIC, "input", src, "format", babl_format ("RaGaBaA float"), "x", 50, "y", 50, "width", 200, /* or a rect could be passed, or a subbuffer of "height", 200, buffer expected to be created before making a sampler */ NULL);
The api below would introduce more complexity because each implementation would need other arguments.
gegl_sampler_get_quad (sampler, xc-0.5, yc-0.5,
xc+0.5, yc-0.5,
xc+0.5, yc+0.5,
xc-0.5, yc+0.5,
buf);
The curren API proposal works on the complete passed buffer and should alos work for scaling and transforming.
Since the fractional part of x,y determines the offset.
void
affine_cubic (GeglBuffer *dest,
GeglBuffer *src,
Matrix3 matrix)
{
gdouble arecip;
gint x, y,
i, j,
pos,
src_w = src->width,
src_h = src->height,
u,
u1 = src->x,
u2 = src_w - 1,
pu,
v,
v1 = src->y,
v2 = src_h - 1,
pv;
gfloat *src_buf,
*dest_buf,
*src_ptr,
*dest_ptr,
abyss = 0.;
gdouble newval[4];
gdouble data[64];
gdouble du, dv, fu, fv;
Matrix3 inverse;
if (gegl_buffer_pixels (src) == 0 ||
gegl_buffer_pixels (dest) == 0)
return;
dest_buf = g_new (gfloat, gegl_buffer_pixels (dest) << 2);
g_assert (src_buf && dest_buf);
GeglInterpolatorCubic sampler = g_object_new (GEGL_TYPE_INTERPOLATOR_CUBIC,
"input", src,
"format", babl_format ("RaGaBaA float"),
NULL);
matrix3_copy (inverse, matrix); matrix3_invert (inverse);
fu = du = inverse[0][0] * dest->x + inverse[0][1] * dest->y + inverse[0][2] - src->x; fv = dv = inverse[1][0] * dest->x + inverse[1][1] * dest->y + inverse[1][2] - src->y;
for (dest_ptr = dest_buf, y = 0; y < dest->height; y++)
{
for (x = 0; x < dest->width; x++)
{
gegl_interpolator_get (interpolator, fu, fv,dest_buf);
fu += inverse [0][0];
fv += inverse [1][0];
}
du += inverse [0][1];
dv += inverse [1][1];
fu = du;
fv = dv;
}
gegl_buffer_set_fmt (dest, dest_buf, babl_format ("RaGaBaA float"));
g_free (dest_buf);
}
Proposition : GeglInterpolator
On 10/16/06, geert.jordaens-CNXmb7IdZIWZIoH1IeqzKA@public.gmane.org wrote:
The change below i think would complicate the usage. Since every time accessing the interpolator the coordinates for accessing should be ajusted by the value of x, y.
*GeglSampler *sampler = g_object_new (GEGL_TYPE_SAMPLER_CUBIC, "input", src, "format", babl_format ("RaGaBaA float"), "x", 50, "y", 50, "width", 200, /* or a rect could be passed, or a subbuffer of "height", 200, buffer expected to be created before making a sampler */ NULL);
The purpose of this change would be to indicate which window within the buffer needs to be prepared for access (the specific sampler would then need to know exactly which data must be prepared). But as I wrote in my comment to those API additions relying on the buffer to be pre-clipped is probably better.
The api below would introduce more complexity because each implementation would need other arguments.
gegl_sampler_get_quad (sampler, xc-0.5, yc-0.5, xc+0.5, yc-0.5, xc+0.5, yc+0.5, xc-0.5, yc+0.5, buf);
The curren API proposal works on the complete passed buffer and should alos work for scaling and transforming. Since the fractional part of x,y determines the offset.
Nope, the point is that interpolation is wrong when scaling down you as will happen when scaling down using an affine transform or a perspective transform. By transforming the corners of a pixel, one would get an idea about the size needed for the resampling kernel.
If you scale a image to 10% of the original size using cubic, you have a situation where the data for each destination pixel is taken from a region of 4x4pixel, whilst it should at least be taken from a region of 10x10pixels, 84% of the image data is thrown away.
/Øyvind K.
Proposition : GeglInterpolator
Nope, the point is that interpolation is wrong when scaling down you as will happen when scaling down using an affine transform or a perspective transform. By transforming the corners of a pixel, one would get an idea about the size needed for the resampling kernel.
If you scale a image to 10% of the original size using cubic, you have a situation where the data for each destination pixel is taken from a region of 4x4pixel, whilst it should at least be taken from a region of 10x10pixels, 84% of the image data is thrown away.
OK, the handling of scaling down is not yet in the proposition
Could we not just add the scale factor to the API ?
Proposition : GeglInterpolator
On 10/17/06, geert.jordaens-CNXmb7IdZIWZIoH1IeqzKA@public.gmane.org wrote:
If you scale a image to 10% of the original size using cubic, you have a situation where the data for each destination pixel is taken from a region of 4x4pixel, whilst it should at least be taken from a region of 10x10pixels, 84% of the image data is thrown away.
OK, the handling of scaling down is not yet in the proposition
Could we not just add the scale factor to the API ?
The scale factor is not enough, if we scale it to 10% horizontally and 70% vertically (or add some kind of rotation as well). A fixed scale factor would no longer be correct. Doing a reverse transform of the "corners" of the destination pixel would give us all the information we need, and work for perspective transforms as well, hence the method I suggested.
/Øyvind K.
Proposition : GeglInterpolator
On Tue, 2006-10-17 at 09:27 +0200, Øyvind Kolås wrote:
On 10/17/06, geert.jordaens-CNXmb7IdZIWZIoH1IeqzKA@public.gmane.org wrote:
If you scale a image to 10% of the original size using cubic, you have a situation where the data for each destination pixel is taken from a region of 4x4pixel, whilst it should at least be taken from a region of 10x10pixels, 84% of the image data is thrown away.
OK, the handling of scaling down is not yet in the proposition
Could we not just add the scale factor to the API ?
The scale factor is not enough, if we scale it to 10% horizontally and 70% vertically (or add some kind of rotation as well). A fixed scale factor would no longer be correct. Doing a reverse transform of the "corners" of the destination pixel would give us all the information we need, and work for perspective transforms as well, hence the method I suggested.
I think you are mixing up interpolation and resampling. They are similar (abstractly anyway), but separate operations.
Interpolation is designed to add points between two or more nearby points using linear or cubic interpolating curves. Translating by fractional coordinates uses interpolation. Rotation (without scaling) also uses interpolation.
Resampling is designed to remove high frequency components while down sampling an image. Without resampling, when you downscale you get "jaggies" (aliasing) throughout the image. Examples of resampling are taking the average of the input area (box filter), gaussian weighted average, or applying a sync filter. Resampling is a convolution.
These operations are best implemented seperately. First interpolate to get a set of pixels that can be input into a resampling function to complete your transformation.
For example, suppose your x-scale is .1 (matrix X), your y scale is 2 (matrix Y) and your rotation angle is 45 degrees (matrix R). They are combined like so: YXR. First the rotation, do the rotation by 45 degrees by interpolating from nearby points. Then do the X scale by resampling 10 or more of the interpolated pixels (say, with a sync function, which is the best filter). Then complete the transformation by interpolating one extra Y point between every existing Y point using an interpolation function to complete your Y scale.
Geert's interpolation API is sufficient. Interpolation rarely takes or needs more than 16 pixels. A rescaling API needs to be able to declare the size of the neighborhood around the pixel (an interpolation API could probably use this). They probably both need another object for extending the region at the edges (copy, reflect, zeros, wrap, etc).
Both these operations are similar in that they need a neighborhood and produce a new value based on that neighborhood, but mathematicaly they are acomplishing two very different goals. Interpolation doesn't affect resolution and is used for shifting and scaling up. Resampling is for downsampling (aka decimating) a signal and is a type of anti-aliasing. They are used in very different cases, and should probably have different interfaces to reflect that.
Proposition : GeglInterpolator
On 10/17/06, Daniel Rogers wrote:
I think you are mixing up interpolation and resampling. They are similar (abstractly anyway), but separate operations.
My claim is that interpolation and decimation both are resampling operations, and thus shuld be grouped together.
Geert's interpolation API is sufficient. Interpolation rarely takes or needs more than 16 pixels. A rescaling API needs to be able to declare the size of the neighborhood around the pixel (an interpolation API could probably use this). They probably both need another object for extending the region at the edges (copy, reflect, zeros, wrap, etc).
As I see it this needs to be handled by the buffer implementation and not the resamplers, each operation will only operate on a smaller region of the image any abyss behavior should already be applied to the data outside when data is mapped from storage to a linear buffer to be operated on. (currently the behavior is always zeroed out data)
Both these operations are similar in that they need a neighborhood and produce a new value based on that neighborhood, but mathematicaly they are acomplishing two very different goals. Interpolation doesn't affect resolution and is used for shifting and scaling up. Resampling is for downsampling (aka decimating) a signal and is a type of anti-aliasing. They are used in very different cases, and should probably have different interfaces to reflect that.
Both scaling up and scaling down is resampling, changing the spatial sampling rate of the image raster. Increasing the samling rate is interpolation, decreasing it is decimation, both of them are resampling.
One place in the GIMP code where these are used is the transformation tools, the transformation code in GIMP uses a matrix to represent the entire transformation. In the case of a perspective transform you need to do both interpolation and decimation with the same resampler. What I was suggesting is to keep the same cache of pixels available both for the interpolation and decimation and use different functions for retrieving the data.
/OEyvind K.
Proposition : GeglInterpolator
What I tried to do with the gegl_interpolator was in fact what gimp is
doing in gimpdrawable-transform,
Gimp only downsamples in the scale-funcs not during the transformation .
I'm not sure if it is possible to corectly downsample during transformation.
So the resampler should use a transformation matrix, and take source
coördinates.
instead of :
matrix3_copy (inverse, matrix); matrix3_invert (inverse);
fu = du = inverse[0][0] * dest->x + inverse[0][1] * dest->y + inverse[0][2] - src->x; fv = dv = inverse[1][0] * dest->x + inverse[1][1] * dest->y + inverse[1][2] - src->y;
for (dest_ptr = dest_buf, y = 0; y < dest->height; y++)
{
for (x = 0; x < dest->width; x++)
{
gegl_interpolator_get (interpolator, fu, fv, dest_buf);
fu += inverse [0][0];
fv += inverse [1][0];
}
du += inverse [0][1];
dv += inverse [1][1];
fu = du;
fv = dv;
...
}
The transformed coordinates should be calculated within the resampler. gegl_resampler_set_matrix(resampler, matrix);
for (y = 0; y < dest->height; y++)
{
for (x = 0; x < dest->width; x++)
{
gegl_resampler_get (resampler, x, y, dest_buf);
}
...
}
The gegl_resampler_get should then calculate the number of transformed
coordinates needed by the
resampling method (lanczos/bicubic/bilinear/nearest) as in
gimpdrawable-transform.
ca= cos a
sa = sin a
. = don't touch
gegl_resampler_set_matrix_identity()
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
gegl_resampler_set_scale(x, y)
| *sy . 0 |
| . *sy 0 |
| . . 1 |
gegl_resampler_set_translscale(tx, ty)
| . . 0 |
| . . 0 |
| tx ty 1 |
gegl_resampler_set_rotate(a)
| *ca *sa 0 |
| *-sa *ca 0 |
| . . 1 |
gegl_resampler_set_matrix(a,b,c,d,e,f )
| a d 0 |
| b e 0 |
| c f 1 |
Geert