Question

I've been having trouble trying to get PIL to nicely downsample images. The goal, in this case, is for my website to automagically downsample->cache the original image file whenever a different size is required, thus removing the pain of maintaining multiple versions of the same image. However, I have not had any luck. I've tried:

image.thumbnail((width, height), Image.ANTIALIAS)
image.save(newSource)

and

image.resize((width, height), Image.ANTIALIAS).save(newSource)

and

ImageOps.fit(image, (width, height), Image.ANTIALIAS, (0, 0)).save(newSource)

and all of them seem to perform a nearest-neighbout downsample, rather than averaging over the pixels as it should Hence it turns images like

http://www.techcreation.sg/media/projects//software/Java%20Games/images/Tanks3D%20Full.png

to

http://www.techcreation.sg/media/temp/0x5780b20fe2fd0ed/Tanks3D.png

which isn't very nice. Has anyone else bumped into this issue?

Was it helpful?

Solution

That image is an indexed-color (palette or P mode) image. There are a very limited number of colors to work with and there's not much chance that a pixel from the resized image will be in the palette, since it will need a lot of in-between colors. So it always uses nearest-neighbor mode when resizing; it's really the only way to keep the same palette.

This behavior is the same as in Adobe Photoshop.

You want to convert to RGB mode first and resize it, then go back to palette mode before saving, if desired. (Actually I would just save it in RGB mode, and then turn PNGCrush loose on the folder of resized images.)

OTHER TIPS

This is over a year old, but in case anyone is still looking:

Here is a sample of code that will see if an image is in a palette mode, and make adjustments

 import Image # or from PIL import Image
 img = Image.open(sourceFile)
 if 'P' in img.mode: # check if image is a palette type
     img = img.convert("RGB") # convert it to RGB
     img = img.resize((w,h),Image.ANTIALIAS) # resize it
     img = img.convert("P",dither=Image.NONE, palette=Image.ADAPTIVE) 
           #convert back to palette
 else:
     img = img.resize((w,h),Image.ANTIALIAS) # regular resize
 img.save(newSourceFile) # save the image to the new source
 #img.save(newSourceFile, quality = 95, dpi=(72,72), optimize = True) 
    # set quality, dpi , and shrink size

By converting the paletted version to RGB, we can resize it with the anti alias. If you want to reconvert it back, then you have to set dithering to NONE, and use an ADAPTIVE palette. If there options aren't included your result (if reconverted to palette) will be grainy. Also you can use the quality option, in the save function, on some image formats to improve the quality even more.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top