14

What is the best way to scale a 2D image array? For instance, suppose I have an image of 1024 x 2048 bytes, with each byte being a pixel. Each pixel is a grayscale level from 0 to 255. I would like to be able to scale this image by an arbitrary factor and get a new image. So, if I scale the image by a factor of 0.68, I should get a new image of size 0.68*1024 x 0.68*2048. some pixels will be collapsed onto each other. And, if I scale by a factor of say 3.15, I would get a larger image with pixels being duplicated. So, what's the best way to accomplish this?

Next, I would like to be able to rotate an image by an arbitrary angle, in the range of 0 to 360 degrees (0 - 2Pi). Cropping of the image after rotating isn't an issue. What would be the best way to do this?

Juha Syrjälä
  • 33,425
  • 31
  • 131
  • 183
kafuchau
  • 5,573
  • 7
  • 33
  • 38

10 Answers10

18

There are many ways to scale and rotate images. The simplest way to scale is:

dest[dx,dy] = src[dx*src_width/dest_width,dy*src_height/dest_height]

but this produces blocky effects when increasing the size and loss of detail when reducing the size. There are ways to produce better looking results, for example, bilinear filtering.

For rotating, the src pixel location can be calculated using a rotation matrix:

sx,sy = M(dx,dy)

where M is a matrix that maps destination pixels to the source image. Again, you'll need to do interpolation to produce non-blocky results.

But there are plenty of libraries available if you don't want to get into the mathematics of image processing.

Skizz
  • 69,698
  • 10
  • 71
  • 108
12

What you're doing is mapping a set of input points to a set of output points. The first part of the problem is to determine the mapping for your resizing or rotation; the second part is to handle points that don't lie exactly on a pixel boundary.

Mapping for a resize is easy:

x' = x * (width' / width)
y' = y * (height' / height)

Mapping for rotation is only a little bit harder.

x' = x * cos(a) + y * sin(a)
y' = y * cos(a) - x * sin(a)

The technique for determining the value of pixels that lie off the grid is called interpolation. There are many such algorithms, ranging widely in speed and final image quality. A few of them in increasing order of quality/time are nearest neighbor, bilinear, bicubic, and Sinc filter.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
9

There is no simple way of accomplishing this. Neither scaling nor rotating are trivial processes.

It is therefore advisable to use a 2d imaging library. Magick++ can be an idea as divideandconquer.se point out, but there are others.

Tim Nieradzik
  • 112
  • 1
  • 6
Pablo Herrero
  • 1,724
  • 5
  • 18
  • 23
8

Do you want to do the dirty work yourself or can ImageMagick do it for you?

activout.se
  • 6,058
  • 4
  • 27
  • 37
5

Duplicating or discarding pixels is not the best method or image resizing, as the results will show pixelation and jagginess. For the best results, you should resample the image, which will give the resulting image a much smoother look. There are lots of methods for resampling, like bilinear, bicubic, lanczos etc.

Take a look at the ResampleBicubic function from wxWidgets. It works will all kinds of images, not only greyscale, but you should be able to adapt it to your needs. Then there's also resampling code from VirtualDub. Google Codesearch may reveal more related code.

EDIT: the links look fine in the preview, but are broken when posted. This is strange. Go to google codesearch and query for "wxwidgets resamplebicubic" and "virtualdub resample" respectively to get the same results.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
user38329
  • 719
  • 4
  • 17
3

It has not been mentioned yet, so I will point out that OpenCV has functions for scaling and rotating images, as well as an enormous number of other utilities. It may contain many features that are not relevant to the question, but it is very easy to setup and use for a library of its kind.

You can try to implement transformations like this manually, but the simple approach to scaling and rotating will generally result in a significant loss of detail.

Using OpenCV, scaling can be done like so:

float scaleFactor = 0.68f;
cv::Mat original = cv::imread(path);
cv::Mat scaled;
cv::resize(original, scaled, cv::Size(0, 0), scaleFactor, scaleFactor, cv::INTER_LANCZOS4);
cv::imwrite("new_image.jpg", scaled);

This scales the image down by a factor of 0.68 using Lanczos interpolation.

I am not as familiar with rotations, but here's part of an example from one of the tutorials on the OpenCV website that I have edited down to the relevant parts. (The original had skew and translation in it also...)

/// Compute a rotation matrix with respect to the center of the image
Point center = Point(original.size().width / 2, original.size().height / 2);
double angle = -50.0;
double scale = 0.6;

/// Get the rotation matrix with the specifications above
Mat rot_mat( 2, 3, CV_32FC1 );
rot_mat = getRotationMatrix2D(center, angle, scale);

/// Rotate the image
Mat rotated_image;
warpAffine(src, rotated_image, rot_mat, src.size());

OpenCV Website

They have some very nice documentation too.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Meta
  • 121
  • 6
2

CxImage is a free library for handling images, which can do what you want. I haven't personally used it except for trivial stuff, but I've seen it recommended repeatedly.

peterchen
  • 40,917
  • 20
  • 104
  • 186
  • Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Toby Speight Aug 31 '16 at 13:43
0

CxImage resizing methods produce strange result. I used Resample and Resample2 functions with all available variations of interpolation methods with same result. For example, try to resize 1024 x 768 image filled with white color to size 802 x 582. You'll find that there are pixels on the image that have color different to white! You can check this: open resized image in Windows Paint and try to fill it with black color. Result will surely amuse you.

izogfif
  • 125
  • 1
  • 5
0

Check out the Intel Performance Primitives. I have used it before and it produces near optimal performance on x86. There is also a test program that lets to play with the various algorithms.

Jon Clegg
  • 3,870
  • 4
  • 25
  • 22
  • Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Toby Speight Aug 31 '16 at 13:43
-1
point scaling(point p,float sx,float sy) {
    point s;

    int c[1][3];
    int a[1][3]={p.x,p.y,1};
    int b[3][3]={sx,0,0,0,sy,0,0,0,1};

    multmat(a,b,c);

    s.x=c[0][0];
    s.y=c[0][1];

    return s;
}
thkala
  • 84,049
  • 23
  • 157
  • 201