Yet Another Image Resizer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
"""
A simple drop-in image resizing templatetag.

* Only generates the resized image when first requested.
* Handles maintaining proportions when specifying only a width or a height.
* Makes use of PIL.ImageOps.fit for cropping without reinventing the wheel.

Usage:

Resize to 200px wide maintaining ratio: {% thumbnail path_to_image 200 0 %}
Resize to 200px high maintaining ratio: {% thumbnail path_to_image 0 200 %}
Resize and crop to 200x200px {% thumbnail path_to_image 200 200 %}

Credits:
--------

Stephen McDonald <steve@jupo.org>

License:
--------

Creative Commons Attribution-Share Alike 3.0 License
http://creativecommons.org/licenses/by-sa/3.0/

When attributing this work, you must maintain the Credits
paragraph above.
"""

import os

from django import template
from django.conf import settings


register = template.Library()


@register.simple_tag
def thumbnail(image_url, width, height):
    """
    Given the url to an image, resizes the image using the given width and 
    height on the first time it is requested, and returns the url to the new 
    resized image. If width or height are zero then the original ratio is 
    maintained.
    """
    
    image_url = unicode(image_url)
    image_path = os.path.join(settings.MEDIA_ROOT, image_url)
    image_dir, image_name = os.path.split(image_path)
    thumb_name = "%s-%sx%s.jpg" % (os.path.splitext(image_name)[0], width, height)
    thumb_path = os.path.join(image_dir, thumb_name)
    thumb_url = "%s/%s" % (os.path.dirname(image_url), thumb_name)

    # abort if thumbnail exists, original image doesn't exist, invalid width or 
    # height are given, or PIL not installed
    if not image_url:
        return ""
    if os.path.exists(thumb_path):
        return thumb_url
    try:
        width = int(width)
        height = int(height)
    except ValueError:
        return image_url
    if not os.path.exists(image_path) or (width == 0 and height == 0):
        return image_url
    try:
        from PIL import Image, ImageOps
    except ImportError:
        return image_url

    # open image, determine ratio if required and resize/crop/save
    image = Image.open(image_path)
    if width == 0:
        width = image.size[0] * height / image.size[1]
    elif height == 0:
        height = image.size[1] * width / image.size[0]
    if image.mode not in ("L", "RGB"):
        image = image.convert("RGB")
    try:
        image = ImageOps.fit(image, (width, height), Image.ANTIALIAS).save(
            thumb_path, "JPEG", quality=100)
    except:
        return image_url
    return thumb_url

More like this

  1. ResizeImageField by wim 2 years, 8 months ago
  2. SearchableManager by stephen_mcd 3 years, 3 months ago
  3. Javascript constraints in admin app and fieldsets to tabs (jquery) by jpic 4 years, 3 months ago
  4. Image Standarization by garcia_marc 5 years, 2 months ago
  5. Multipart Templated Email by stephen_mcd 3 years, 3 months ago

Comments

cocbiz (on July 13, 2011):

+1 for ImageOps.fit

#

jperals (on January 4, 2012):

Looks like we are assuming that image_url is a relative URL.

If you pass an absolute URL as image_url to this function, you get as image_path something like '/path/to/local/directory/http://server/path/filename.jpg', and as a file with this name doesn't exist, the function will exit, i.e., this won't work.

I have been able to fix this by stripping the string settings.MEDIA_URL from the original image_url before calculating image_path. I also renamed 'image_url' to 'image_absolute_url', so that I have a variable called 'image_absolute_url' and another called 'image_relative_url', for clarity. Now my code starts like this:

image_absolute_url = unicode(image_url)
image_relative_url = image_absolute_url.replace(settings.MEDIA_URL, '')
image_path = os.path.join(settings.MEDIA_ROOT, image_relative_url)

And all the remaining instances of 'image_url' are replaced by 'image_absolute_url'. Of course this variable name change is not absolutely necessary, but I think that it makes the code easier to understand.

On the other hand I am a newbie in Django, so it's fairly probable that I am missing something!

#

jperals (on January 4, 2012):

Maybe an alternative would be to use as a parameter the image itself, instead of its URL.

This way, you could easily access its path as well as its URL or whatever, saving some of these string manipulations.

#

jperals (on January 4, 2012):

Oh, I guess that if we are calling the function from a templatetag, we can only pass strings?

#

(Forgotten your password?)