Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pillow resize pixelating images - Django/Pillow

I am developing an image uploader in Django. After the image has been uploaded and saved on disk, I'm trying to resize the saved image while maintaing its aspect ratio. I am using Pillow for image processing/resizing. The problem arises when I try to resize the image, it's getting pixelated even though the resized image's aspect ratio is same as that of the original image.

Original Saved Image : https://www.dropbox.com/s/80yk6tnwt3xnoun/babu_980604.jpeg

Resized Pixelated Image: https://www.dropbox.com/s/bznodpk4t4xlyqp/babu_736302.large.jpeg

I've tried googling this problem and have checked out other related links on stackoverflow as well,

like

How do I resize an image using PIL and maintain its aspect ratio?

Resize image maintaining aspect ratio AND making portrait and landscape images exact same size?

but the problem still persists.

Versions:

Django=1.6.4

Pillow=2.4.0

Everything has been setup inside virtualenv. Please help!

PS : I'm a newbie to the world of Python/Django

Here's my code snippet :

import json
import os
import hashlib
from datetime import datetime
from operator import itemgetter
import random
from random import randint
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.http import (HttpResponse, HttpResponseRedirect)
from django.core.context_processors import csrf
from django.core.files.images import get_image_dimensions
from django.shortcuts import render, redirect
from django.forms.models import model_to_dict
from django.views.decorators.csrf import csrf_exempt
from PIL import Image, ImageOps
from django.views.decorators.csrf import csrf_exempt, csrf_protect
import settings

from hashlib import md5
from django import forms

from beardedavenger.models import *

from django.views.decorators.http import require_POST

import pdb
import requests

def imagehandler(requests):
if requests.method == 'POST':
    filename = requests.FILES['file'].name
    file_extension = filename.split('.')[len(filename.split('.')) - 1].lower()
    errors = []

    username = 'nit'

    global random

    #allowed image types are png, jpg, jpeg, gif
    if file_extension not in settings.IMAGE_FILE_TYPES:
        errors.append('The image file you provided is not valid. Only the following extensions are allowed: %s' % ', '.join(settings.IMAGE_FILE_TYPES))
    else:
        image = requests.FILES['file']
        image_w, image_h = get_image_dimensions(image)
        rand = str(random.randint(100000,999999))
        with open(settings.MEDIA_ROOT + username + '_' + rand + '.jpeg', 'wb+') as destination:
            for chunk in requests.FILES['file'].chunks():
                destination.write(chunk)

        large_size = (1920, 1200)

        infile = settings.MEDIA_ROOT + username + '_' + rand + ".jpeg"

        large_file = settings.MEDIA_ROOT + username + '_' + rand +".large"

        try:
            im = Image.open(infile)

            base_width = large_size[0]

            aspect_ratio = float(image_w / float(image_h))
            new_height = int(base_width / aspect_ratio)

            if new_height < 1200:
                final_width = base_width
                final_height = new_height
            else:
                final_width = int(aspect_ratio * large_size[1])
                final_height = large_size[1]

            final_size = (final_width, final_height)

            imaged = im.resize((final_width, final_height), Image.ANTIALIAS)
            # imaged = ImageOps.fit(im, final_size, Image.ANTIALIAS, centering = (0.5,0.5))
            imaged.save(large_file, "JPEG", quality=90)

        except IOError:
            errors.append('error while resizing image')

    if not errors:
        response = HttpResponse(json.dumps({'status': 'success','filename': filename }),
        mimetype="application/json")
    else:
        response = HttpResponse(json.dumps({'status': 'failure','errors': errors,'message': 'Error uploading Picture. '}),
        mimetype="application/json")
    return response
else:
    return render(requests, 'upload.html')

Update :

I was using Pillow to resize and compress my images. Even though the aspect ratio was maintained, there was a certain amount of dullness introduced in the images upon resizing [had more anti-aliasing than required as compared to original images]. I switched my processing library to ImageMagick(against numerous posts suggesting not to!) along with Wand API (docs.wand-py.org/en/0.3.7/index.html), to process my images. This change worked like a charm!

like image 736
nit29 Avatar asked May 28 '14 20:05

nit29


People also ask

How do I resize an image in a Pillow Python?

To resize an image, you call the resize() method on it, passing in a two-integer tuple argument representing the width and height of the resized image. The function doesn't modify the used image; it instead returns another Image with the new dimensions.

How do you scale an image on a Pillow?

The Image module from pillow library has an attribute size. This tuple consists of width and height of the image as its elements. To resize an image, you call the resize() method of pillow's image class by giving width and height.


1 Answers

With this code I get this image (Python 2.7, Pillow 2.4.0) which has no pixelating.

from PIL import Image

large_size = (1920, 1200)

im = Image.open("babu_980604.jpeg")

image_w, image_h = im.size
aspect_ratio = image_w / float(image_h)
new_height = int(large_size[0] / aspect_ratio)

if new_height < 1200:
    final_width = large_size[0]
    final_height = new_height
else:
    final_width = int(aspect_ratio * large_size[1])
    final_height = large_size[1]

imaged = im.resize((final_width, final_height), Image.ANTIALIAS)

imaged.show()
imaged.save("out.jpg", quality=90)

The main difference between this and your code is it gets image_w and image_h directly from the opened image instead of get_image_dimensions(image), whose implementation is not shown.

output image

Some minor things in your code:

  • You could set infile before the with open(...) and use it there too.

  • final_size isn't used and can be removed, or otherwise use it in im.resize().

  • base_width could be replaced by large_size[0], as you also use large_size[1] elsewhere.

  • image is set to requests.FILES['file'] but you also use requests.FILES['file'] directly. You could reuse image.

  • global random probably isn't needed.

like image 73
Hugo Avatar answered Sep 23 '22 00:09

Hugo