-1

I have a program that does scaling and rotation on images, the algorithm works like this:

(pseudocode)
for (x, y) in dest
    ox, oy = transform(x, y)
    dest[x,y] = src[ox, oy]

I decided to implement bilinear interpolation to get better results, but I don't know how to get the four known points around x,y after the transformation.

I tried to do this:

(pseudocode)
for (x, y) in dest
    ox, oy = transform(x, y)
    dest[x,y] = (src[ox, oy] + src[ox + 1, oy] +
                 src[ox, oy + 1] + src[ox + 1, oy + 1])/4

Which gives an interpolated look, but the the pixels are still pretty sharp. By doing the same thing with 16 pixels (trying to implement bicubic) the result is even worse, a image with sharp pixels and really blurry at the same time

Result

The final result is 512x512 (dest), but looks like it was intepolated when still 32x32 (src) and then scaled using nearest neighbour. What I'm doing wrong?

Andre Augusto
  • 84
  • 1
  • 8
  • interpolation: map the point. it'll be some floating point number, not integer. round each coordinate up and down. now you have the integer coordinates of the adjacent four pixels in the source. now you need to take the *fractional parts* you chopped off, and use those in the bilinear calculation. – Christoph Rackwitz Dec 04 '21 at 12:21
  • That would result on the same thing I tried to do, rounding up and down would get points one pixel apart. Calculating like that creates the problem shown on the image – Andre Augusto Dec 04 '21 at 23:19

1 Answers1

-1

looks like it was intepolated when still 32x32 (src) and then scaled using nearest neighbour.

I think that was exactly what was happening, I was trying to reuse the nearest neighbor function but with interpolated values. Turned out that by doing this I was interpolating inside the 32x32 space

The correct algorithm would be something like:

(pseudocode)
for (ox, oy) in src
    x, y = transform_inv(ox, oy)
    dest[x, y] = src[ox, oy]
    known += (x, y)

for (x, y) in dest
    if (x, y) not in known
        dest[x, y] = interpolate(known, x, y)

Edit: The older algorithm works, and more efficiently than filling the gaps. What I needed was to take in consideration the distance between each pixel on the 512x512 space as weight on the average function

(pseudocode)
for (x, y) in dest
    ox, oy = transform(x, y)
    p1 = src[ox, oy]
    p2 = src[ox + 1, oy]
    p3 = src[ox, oy + 1]
    p4 = src[ox + 1, oy + 1]
    d1 = distance(transform_inv(ox, oy), (x, y))
    d2 = distance(transform_inv(ox + 1, oy), (x, y))
    d3 = distance(transform_inv(ox, oy + 1), (x, y))
    d4 = distance(transform_inv(ox + 1, oy + 1), (x, y))
    dt = d1 + d2 + d3 + d4
    dest[x,y] = ((p1 * d1) + (p2 * d2) + (p3 * d3) + (p4 * d4))/dt

Edit2: It's looking weird on lower resolutions (The image from the edit before I accidentally tested with 128x128 instead of 32x32), probably there's still something to improve on it

Andre Augusto
  • 84
  • 1
  • 8
  • sorry but that code is *incorrect*. you can't iterate over source pixels and map them to destination positions. that can cause arbitrarily large gaps in the output. you noticed that but the "fix" is insufficient (!) and isn't even needed, if you do it right. -- the usual way is to invert the forward transformation, and iterate over destination pixels, looking up the source position for each. – Christoph Rackwitz Dec 04 '21 at 12:23
  • The large gaps would be filled by the interpolation step, which would find the four nearest points on the `known` data structure. Iterating over destination to source is the way I was doing it and wasn't working – Andre Augusto Dec 04 '21 at 23:18