5

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!

nit29
  • 78
  • 1
  • 8
  • I'm not answering with code but with libs: https://github.com/vinyll/django-imagefit (maintained) or https://github.com/matthewwithanm/django-imagekit (abandoned?) – vinyll Mar 27 '18 at 06:45

1 Answers1

10

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.

Hugo
  • 27,885
  • 8
  • 82
  • 98
  • Thanks a ton! It does work now! I fixed the minor chinks in my code which you pointed out. But when I resize the image, I see a change in the color tone. The original image had more **Blue** in it, while the resized image is more **greener**. It would be great if you could checkout this [link](http://s27.postimg.org/6y0ux3iab/resize.png), it's a screen shot of the original image and the resized image placed adjacent to each other. What can I do so that the color tone doesn't change/is less noticeable? [link](http://s27.postimg.org/6y0ux3iab/resize.png) – nit29 May 29 '14 at 19:25
  • I can see the difference in the side-by-side pic but I'm not sure what's caused it. Interestingly, the original you uploaded also looks greenish rather than blue: https://www.dropbox.com/s/80yk6tnwt3xnoun/babu_980604.jpeg As does the one I converted: https://i.stack.imgur.com/nw9rE.jpg – Hugo May 30 '14 at 06:31
  • Just wanted to put across an 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 (http://docs.wand-py.org/en/0.3.7/index.html), to process my images. This change worked like a charm! – nit29 Aug 05 '14 at 10:20
  • Note that Pillow added the `Image.thumbnail(...)` call in v2.7.0 which makes this much simpler. http://pillow.readthedocs.org/reference/Image.html?highlight=thumbnail#PIL.Image.Image.thumbnail – Taylor D. Edmiston Jun 19 '15 at 00:46