24

What's the best re-sampling algorithm I can use to divide an image into half its original size. Speed is of primary importance but it shouldn't degrade quality too bad. I'm basically trying to generate an image pyramid.

I was originally planning to skip pixels. Is this the best way to go? From what I've read the image produced by pixel skipping is too sharp. Could someone who has tried this comment. My images contain map data sort of like this.

sleeping.ninja
  • 607
  • 1
  • 10
  • 21
  • 1
    By "half the original size", do you mean halving each dimension, or halving the total area? – caf May 26 '11 at 05:14
  • 1
    As you read, skipping pixels won't be a good way to go - some of the text is one pixel thick to begin with (e.g. "+")... such a character could disappear completely if it fell in "off-" rows and columns. More generally, see http://stackoverflow.com/questions/158756/what-is-the-best-image-manipulation-library – Tony Delroy May 26 '11 at 05:21
  • 1
    @caf- Half as in half of each dimension. Meaning a 1000X1000 image would become 500X500 – sleeping.ninja May 26 '11 at 05:37
  • @Tony not really looking for a library. – sleeping.ninja May 26 '11 at 05:39

6 Answers6

39

Skipping pixels will result in aliasing, where high frequency changes (such as alternating light/dark bands) will convert to low frequencies (such as constant light or dark).

The quickest way to downsize to half without aliasing is to average 2x2 pixels into a single pixel. Better results can be had with more sophisticated reduction kernels, but they will come at the expense of speed.

Here are some examples of the techniques discussed so far.

Skipping every other pixel - you can see that the results aren't very good by looking at the legend on the left side. It's almost unreadable:

Skipping every other pixel

Averaging every 2x2 grid - The text is now sharp and readable:

Average 2x2

Gaussian blur, as suggested by R. - a little blurrier, but more readable up to a point. The amount of blur can be adjusted to give different results:

Gaussian blur followed by pixel selection

R. is also correct about the Gamma curve affecting the results, but this should only be visible in the most demanding applications. My examples were done without gamma correction.

Edit: And here is an example of a more sophisticated but slow kernel, a Lanczos-5 performed in a linear (not gamma-adjusted) color space.

Lanczos-5 in linear space

The contrast in the lettering is lower, probably because of the conversion of color spaces. But look at the coastline detail.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    +1 love the examples. Note that while both the area-averaging and gaussian are legible, but the font shape is much more correct in the gaussian one. – R.. GitHub STOP HELPING ICE Jul 12 '12 at 23:15
  • @Mark Ransom: And what if i have a b/w image? What is a good algorithm for b/w images? – Sukhanov Niсkolay Mar 04 '14 at 21:16
  • @SukhanovNiсkolay the only difference between color and b/w is how you do the arithmetic on the pixel. The algorithm and results are the same. – Mark Ransom Mar 04 '14 at 21:21
  • @Mark Ransom so if i have a b/w (1 bit color depth) average algorithm will be OK? – Sukhanov Niсkolay Mar 04 '14 at 21:47
  • @SukhanovNiсkolay, you probably want to have a grayscale output so convert each pixel to 0/255 but otherwise yes. If you need a b/w output then I'm not sure what the best algorithm would be. – Mark Ransom Mar 04 '14 at 22:24
  • @Mark Ransom Unfortunately, I need a 1 bit for pixel in output. Thanks. – Sukhanov Niсkolay Mar 05 '14 at 09:53
  • @SukhanovNiсkolay There is always the possibility of using [dithering](https://en.wikipedia.org/wiki/Dither), as a visual approximation of grayscale, even with 1 bpp. – Pablo H May 24 '18 at 20:30
  • @PabloH dithering is a great technique, but it's not relevant to the original question. I'm also not sure it's the best solution for Sukhanov because of the drop in quality. One thing to be careful of when dithering is to account for display gamma, most implementations I've seen ignore it completely and get incorrect brightness. – Mark Ransom May 24 '18 at 20:42
6

For downscaling, area-averaging (see Mark's answer) is close to the best you'll get.

The main other contender is gaussian, with a slightly larger radius. This will increase blurring a little bit, which could be seen as a disadvantage, but would make the blurring more uniform rather than dependent on the alignment of pixels mod 2.

In case it's not immediately clear what I mean, consider the pixel patterns 0,0,2,2,0,0 and 0,0,0,2,2,0. With area-averaging, they'd downscale to 0,2,0 and 0,1,1, respectively - that is, one will be sharp and bright while the other will be blurred and dim. Using a longer filter, both will be blurred, but they'll appear more similar, which presumably matters to human observers.

Another issue to consider is gamma. Unless gamma is linear, two pixels of intensity k will have much less total intensity than a single pixel of intensity 2*k. If your filter performs sufficient blurring, it might not matter so much, but with the plain area-average filter it can be a major issue. The only work-around I know is to apply and reverse the gamma curve before and after scaling...

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
5

If speed is an issue, as mentioned, I recommend to take a 2x2 Block and calculate the average as the resulting pixel. The quality is not the best that can be achieved, but close to. You can provoke this algorithm to show its weaknesses, but on most images you won't see a difference that would justify the many times higher computation time. You also dont have any memory overhead. If color resolution can be lowered to 6bit per channel, here is a pretty fast way that prevents you from decomposing the ARGB channels (here assuming 32bit ARGB):

destPixel[x,y] = ((sourcePixel[2*x  ,2*y  ]>>2)&0x3f3f3f3f) +
                 ((sourcePixel[2*x+1,2*y  ]>>2)&0x3f3f3f3f) +
                 ((sourcePixel[2*x  ,2*y+1]>>2)&0x3f3f3f3f) +
                 ((sourcePixel[2*x+1,2*y+1]>>2)&0x3f3f3f3f);

Side effect of this alogrithm is, that if saved as PNG, the file size gets smaller. This is how it looks like: Test image downscaled with the above algorithm

Thilo Köhler
  • 3,631
  • 2
  • 18
  • 10
2

I tried to generalise Thilo Köhler's solution (but in Python):

STRIDE = 2
MASK = 0x3F3F3F3F
color = 0
for Δx, Δy in itertools.product(range(STRIDE), repeat=2):
    color += (get_pixel(x + Δx, y + Δy) // STRIDE) & MASK

This works fine for scaling by 2 (quarter size result), but doesn't work for scaling by 3 or 4 or other int values. Is it possible to generalise this?

BTW for non-Pythonistas the for loop above is equivalent to this (except that the first version is scalable by changing the STRIDE):

for Δx, Δy in [(0, 0), (0, 1), (1, 0), (1, 1)]:
    color += (get_pixel(x + Δx, y + Δy) // STRIDE) & MASK

I'm using 32-bit ARGB values.

1

The NetPBM suite includes a utility called pamscale, which provides a few options for downsampling. It is open source, so you can try the various options and then copy the algorithm you like best (or just use libnetpbm).

Nemo
  • 70,042
  • 10
  • 116
  • 153
0

http://www.cs.ubc.ca/nest/imager/tr/2011/BlurAwareDownsize/ap-resizing-validation/index.html

http://www.cs.ubc.ca/nest/imager/tr/2011/BlurAwareDownsize/

Perceptual Image Downsampling

Community
  • 1
  • 1
Seth Robertson
  • 30,608
  • 7
  • 64
  • 57
  • That's a very cool paper, and way overkill for his application. If you haven't already, look at the sample image he provided. – andrewdski May 26 '11 at 05:16
  • It is simply a set of links, not the answer. – Kirill V. Lyadvinsky May 26 '11 at 05:16
  • @Kirill: You would prefer I retype the answers? Besides, he did not even search StackOverflow for the answer to a question that has been asked before. – Seth Robertson May 26 '11 at 05:18
  • 1
    @Seth Robertson, It would be nice to have a little review of these articles - what's offered to do, the pros and cons of the method. Or you could mark the question as a duplicate if it is. – Kirill V. Lyadvinsky May 26 '11 at 05:21
  • 1
    I did go through Perceptual Image Downsampling post before asking. I specifically wanted to know if they was a fast algorithm for halving the image (I do not need other resolutions). I even provided a sample image to show that my image does not contain a lot of details, instead contains blocks/regions of different colors. – sleeping.ninja May 26 '11 at 05:35
  • @sleeping.ninja: Cutting in half doesn't give you a major win, for other algorithms they often recommend that you expand/pad to a multiple first. Basically you know the fast but bad algorithm, the decent but pricey algorithm, and the excellent but slow algorithm. Run each, compare visually and execution time wise and choose. – Seth Robertson May 26 '11 at 05:43
  • The purpose of the question was that if someone had already tried it out, they could share their experiences, saving me time and effort. Image Pyramiding is something extremely commonly done in any GIS. I'm sure that there's atleast one guy on SO that has already tried all this. – sleeping.ninja May 26 '11 at 06:10