Saturday, 31 December 2011

Task of the day: resize an image with the "align centers" and "align corners" image size conventions

There are two main image size conventions. Both are based on imagining pixels of the "original" image to be squares of unit area. Each leads to different results when resizing.

Suppose that
input.img is the image to be resized. input.img has m rows and n columns.
output.img is the resized image. output.img has M rows and N columns.

Note: In this post, I won't address the issue of preserving the aspect ratio exactly when M/m is not exactly the same as N/n.

In VIPS, the center of the top left corner of any image is understood to be at position (0,0) by default, and pixels are indexed starting at 0 (the C convention). So, the top left pixel has index (0,0) and is centered at (0,0). The bottom right pixel of input.img has index (n-1,m-1) and is centered at (n-1,m-1).
In other words, positions match C-style indexing.

Align corners (generally better for reducing size): This is the image size convention which is ideal for reducing the number of pixels in each direction by an exact fraction (with box filtering, for example). With this convention, there is no extrapolation near the boundary when downsampling.  It is used by ImageMagick by default (recent versions of ImageMagick can be compiled to use the other convention instead).

The corners of input.img are located at (-.5,-.5), (-.5,m-.5), (n-.5,-.5) and (n-.5,m-.5).
The corners of output.img are located at (-.5,-.5), (-.5,M-.5), (N-.5,-.5) and (N-.5,M-.5).

The affine transformation that sends each input corner to the corresponding output corner is
X = (N/n) x + (N/n-1)/2
Y = (M/m) y + (M/m-1)/2

Consequently, you can resize with the command:

vips im_affinei input.img output.img interpolator a b c d dx dy ox oy ow oh

where
interpolator is your choice of method used to interpolate. The current options are bilinear, bicubic, lbb, nearest, nohalo or vsqbs.
a = N/n
b = 0
c = 0
d = M/m
dx = (N/n-1)/2
dy = (M/m-1)/2
ox = 0
oy = 0
ow = N
oh = M

Warning: Compute everything in floating point arithmetic unless the divisions have no remainder.
Warning: Do not use this image size convention to enlarge images with VIPS. "Outside pixels" are painted black by im_affinei, and the align corners convention pushes pixels "outward" when enlarging. Result: thin black borders.
Warning: With the current crop of interpolators, this approach will not give high quality large ratio reductions of images. Use vips_thumbnail when you reduce by more than about a factor of two in both directions.

Align centers (generally better for enlarging): With this image size convention, there is no extrapolation near the boundary when enlarging. Instead of aligning the outer corners, we align the centers of the corner pixels.

The affine transformation that does this is
X = ((N-1)/(n-1)) x
Y = ((M-1)/(m-1)) y

Consequently, you can resize with the command:

vips im_affinei input.img output.img interpolator a b c d dx dy ox oy ow oh
where
interpolator is as above
= (N-1)/(n-1)
= 0
= 0
= (M-1)/(m-1)
dx = 0
dy = 0
ox = 0
oy = 0
ow = N
oh = M

Warning: Compute everything in floating point arithmetic unless the divisions have no remainder.
Warning: With the current crop of interpolators, this approach will not give high quality large ratio reductions of images. Use vips_thumbnail when you reduce by more than about a factor of two in both directions.

1 comment:

  1. What is missing from these instructions: Generally, you will get better results if you import (convert) the image into the XYZ color space prior to resampling, converting to the color space of your choice at the end.

    ReplyDelete