Re: How to get bounding box from inside a sampler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Øyvind:

Thank you for the quick response.

I wrote:

> > I (believe that) I already know that the relevant pixel values
> > start at indices 0,0. Requested values to the left and above those
> > are created by the (currently 0,0,0,0) abyss policy.  Which is to
> > say that 0 and 0 are the smallest "x" and "y" for which I have
> > "real" data.

You replied:

> Not true, the valid pixel values can be in any rectangular sub
> region of the buffer, negative values should also be
> supported. GeglBuffer supports dynamically growing (will be useful
> for GIMP having auto-growing layers when painting.)

I'll clean up the YAFR code this weekend, and submit a patch for
gegl-sampler-yafr which makes accessible, through an otherwise
inactive #define, a version of the code which carefully implements the
boundary conditions making the assumption that valid pixels start at
0,0. (Obviously, this inactive version is not to be used by
non-developers).

When not toggled, the code will use the default abyss policy, without
implementing careful boundary conditions (in this respect, it will act
like, say, gegl-sampler-cubic).

I'll briefly discuss the "toggle careful boundary conditions" version:

When used to, say, rotate the default image which shows up when typing
gegl in a terminal, it reasonably seamlessly fills in values to the
left and top (prior to rotation) of the unrotated image. With the
"extent of valid pixel" info, I could make it do this all around.

The treatment of the boundary is equivalent to having a bilinear abyss
policy, plus a correction to handle "far values."

Let me first describe the bilinear abyss policy, assuming that the
abyss starts at -1 and extends to the left/top, and ignoring the fact
that one needs to have an abyss policy at the bottom and
right. Suppose that the stencil needs values which are to the left of
0. Find the two closest image pixels which are on the same horizontal
line (if there are any; otherwise, it does not matter what values are
used, because they will be overwritten in the vertical pass; this is
not quite how it's really done but I don't want to be too long). On an
horizontal line, bilinear extrapolation boils down to linear
extrapolation. So, get the abyss value by linear extrapolation.
Formula-wise, if the indices of the missing pixel are [i][j], this
gives:

abyss_pixel[i][j] = pixel[i][0] + ( pixel[i][0] - pixel[i][1] ) * i;

This being done, we can now fix the abyss which is above the image, using:

abyss_pixel[i][j] = pixel[0][j] + ( pixel[0][j] - pixel[1][j] ) * j;

Although this may not immediately obvious, this computes properly the
abyss_values which are in the "top left wedge," because bilinear
extrapolation can be performed by a sequence of two linear
extrapolations along an horizontal/vertical pair of lines, provided
that the proper tile is loaded at the get go (the "proper" tile is
easily computed, as explained below). Consequently, it does not matter
if I fix left first, then fix top, or vice versa.

This abyss policy has a very attractive property:

If an (interior) scheme is exact on bilinear data---as are bilinear,
all the cubic samplers, lanczos, YAFR and, for example, gaussian
blur---meaning that if the data corresponds to a bilinear function,
then the operation recovers the values of the bilinear function (in
the case of gaussian blur it means that the blur operator is the
identity, which should be for bilinear data), then extending the
scheme using this abyss policy makes the "exact on bilinears" property
hold everywhere, not only far enough in the interior of the image.

One consequence of this is that as the scheme traverses the boundary,
the "seam" is fairly "smooth." The other is that "second order
accuracy" is preserved without having to implement careful boundary
conditions. You may think of this abyss policy as a second order
improvement of the first order approach used, for example, by
ImageMagick (programmed slightly differently than as described here):
Fill the abyss with zeros, but normalize the coefficients having to do
with non-zero values so they sum to 1. This last approach preserves
the property of being exact on constants (as opposed to bilinear).

(I'll actually do a lit search when I have a minute and see if the
bilinear abyss trick is published. If not, I'll write an article.)

I would be more than happy to help in implementing this abyss policy
in Gegl (but the abyss code is quite complicated, so I'm not sure I'd
want to do it completely alone).

This abyss policy has a very unattractive property:

If the abyss pixel abyss_pixel[i][j] which is to be computed is far
from the boundary, then bilinear extrapolation amplifies noise in
boundary values by a factor which is basically i*j. I programmed yet
another version of the scheme, and this manifests itself as "smooth
streaks" which "radiate" from the rotated image. If anyone asks me for
the version of that code, I'll gladly oblige.

This problem can be fixed as follows:

When the sampler---not the abyss policy---is asked for a value outside
of the convex hull of the non-abyss pixels, return the computed
sampled value which corresponds to the closest point of the convex
hull. 

This sounds complicated, so I'll explain:

Consider the currently used approach (from gegl-sampler-linear.c). x
and y are the coordinates of the requested sample.

  ...

  dx = (gint) x;
  dy = (gint) y;

  ...

  sampler_bptr = gegl_sampler_get_ptr (self, dx, dy);

  ...

Suppose that x and y are such that x<0 and y>0, so that the requested
sampled value is outside of the "image."

Instead of using x and y directly, use this:

  ...

  local_x = x < (gfloat) 0. ? (gfloat) 0. : x;
  local_y = y < (gfloat) 0. ? (gfloat) 0. : y;

  dx = (gint) local_x;
  dy = (gint) local_y;

  ...

  sampler_bptr = gegl_sampler_get_ptr (self, dx, dy);

  ...

(This is the "proper" tile. See above.)

As a side effect, abyss values are now only only computed for stencils
relevant to coordinates which are in the convex hull of the input
pixel positions.

-----

In order to know these "closest points of the convex hull," one must
know the positions. This is why I think that it would be nice if the
extent of the "valid pixel rectangles" was passed as an object to
samplers.

But this "replace far positions by the closest positions for which the
sampler interpolates" trick could also be implemented in the calling
function.

If you've made it here, thanks: this was long.

Again, I'm willing to help with abyss policy business, but given my
limited c++ I would appreciate some assistance.

Nicolas Robidoux
Laurentian University/Universite Laurentienne.


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


[Index of Archives]     [Yosemite News]     [Yosemite Photos]     [gtk]     [GIMP Users]     [KDE]     [Gimp's Home]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux