If you benefit from web2py hope you feel encouraged to pay it forward by contributing back to society in whatever form you choose!

If in your image gallery or website home page, or even for a user profile image you have a fixed dimensions box to show thumbnails for the uploaded image.

consider this:

<div style="width:200px; height:200px; max-width:200px; overflow:hidden;">
   <img src="{{=URL('default', 'download', args=article.image)}}" />
</div>

You will have a fixed 200x200 box to show the image, but what if the uploaded image is 1024x768 ?

1024x768 image

An image with 1024x768

 

Obviously if you resize that to fit the 200x200 box, you will end with a streched image:

The same image as above, but redimensioned to 200x200, aspect is streched.

 

How to solve this using PIL

For this example I am going to use a fixed, hardcoded centered position. But with a cropper Javascript plugin you can pass coordinates to crop in another position.

 

1. Install PIL 

pip install pil

or

sudo apt-get install python-imaging

 

2. In a module create a function to crop the image.

yourapp/modules/smarthumb.py

 

from gluon import current
import os
try:
    from PIL import Image
except:
    import Image

def SMARTHUMB(image, box, fit=True, name="thumb"):
    '''Downsample the image.
     @param img: Image -  an Image-object
     @param box: tuple(x, y) - the bounding box of the result image
     @param fit: boolean - crop the image to fill the box
    '''
    if image:
        request = current.request
        img = Image.open(request.folder + 'uploads/' + image)
        #preresize image with factor 2, 4, 8 and fast algorithm
        factor = 1
        while img.size[0] / factor > 2 * box[0] and img.size[1] * 2 / factor > 2 * box[1]:
            factor *= 2
        if factor > 1:
            img.thumbnail((img.size[0] / factor, img.size[1] / factor), Image.NEAREST)

        #calculate the cropping box and get the cropped part
        if fit:
            x1 = y1 = 0
            x2, y2 = img.size
            wRatio = 1.0 * x2 / box[0]
            hRatio = 1.0 * y2 / box[1]
            if hRatio > wRatio:
                y1 = int(y2 / 2 - box[1] * wRatio / 2)
                y2 = int(y2 / 2 + box[1] * wRatio / 2)
            else:
                x1 = int(x2 / 2 - box[0] * hRatio / 2)
                x2 = int(x2 / 2 + box[0] * hRatio / 2)
            img = img.crop((x1, y1, x2, y2))

        #Resize the image with best quality algorithm ANTI-ALIAS
        img.thumbnail(box, Image.ANTIALIAS)

        root, ext = os.path.splitext(image)
        thumb = '%s_%s%s' % (root, name, ext)
        img.save(request.folder + 'uploads/' + thumb)
        return thumb

 

3. Now take this sample model.

yourapp/models/db.py

db = DAL('youconnectionstring')

Article = db.define_table('article',
    Field("title"),
    Field("article_text", "text"),
    Field("picture", "upload"),
    Field("thumbnail", "upload")
)

Note that in the model we defined an Article object, which points us to a dal Table db.article, this table has two fields of type "upload", the idea is that the user will upload the file in "picture" fields and we are going to create the thumbnail authomatically.

 

4. Define the computation for "thumbnail" field in the same file, right after the table definition

from smarthumb import SMARTHUMB

box = (200, 200)
Article.thumbnail.compute = lambda row: SMARTHUMB(row.picture, box)

In the above code we are defining that when the FORM gets processed, DAL will compute the value for "thumbnail" field. The computed value will be the result of the "lambda' function we passed to field's compute attribute, in that case we are taking the submitted row and passing the uploaded picture (note that web2py will pass in the path to uploaded image) and also we are passing a box tuple of 200x200 to fit our thumbs div.

 

5. Create a form and test the result

yourapp/controllers/default.py

def addarticle():
    form = SQLFORM(Article).process()
    return dict(form=form)

def showarticle():
   id = request.args(0) or redirect(URL('default', 'index'))
    article = Article[id] 
    return dict(article=article)

 

6. Now you can create a view or just use the generic view for that.

yourapp/views/default/addarticle.html

<h1> Add an article </1>

{{=form}}

 

7.  A view to show the article

yourapp/views/default/showarticle.html

<article>
<h1> {{=article.title}}</h1>

<div style="width:200px; height:200px; max-width:200px; overflow:hidden;">
   <img src="{{=URL('default', 'download', args=article.image)}}" />
</div>

<p>{{=MARKMIN(article.article_text)}}</p>

</article>

 

Now the end result will be:

 

Original uploaded 1024x768 image

 

The generated cropped and centered 200x200 thumbnail keeping the aspect of image.

 

So much better than the streched version no?

 

** also you can use http://src.sencha.io to improve the image exhibition

** web2py now includes gluon/contrib/imagetools.py maybe that function will go there also

 

Does anybody wants to contribute creating a plugin using JqueryCropper or anither JavaScript Crop plugin?

 

REFERENCE: http://united-coders.com/christian-harms/image-resizing-tips-general-and-for-python

 

Related slices

Comments (12)

  • Login to post



  • 0
    fernando-vieira-10469 9 years ago

    Muito bom Bruno, vai ajudar bastante smiley


  • 0
    edwin-ven-11518 10 years ago

    Thank you, this was very useful!


  • 0
    mlrichardvezina 10 years ago

    What do pre-resize?? We found that it cause wrong resize with certain .jpg or .png file and if we remove the pre-resize step it solves our issue... Thanks for provide explanation.

     

    Richard


  • 0
    mlrichardvezina 10 years ago

    In book it says this is bad : request = request.current


  • 0
    bo 11 years ago

    i have been trying this with the following and i just cant get it to work. i am not sure what i am doing wrong.

    
    
    1. auth.settings.extra_fields['auth_user']= [
    2. Field('company_name'),
    3. Field('street'),
    4. Field('city'),
    5. Field('country'),
    6. Field('zip'),
    7. Field('phone'),
    8. Field('bio', 'text'),
    9. Field('avatar', 'upload', autodelete=True),
    10. Field('thumb', 'upload',writable=False,readable=False, autodelete=True)]
    11.  
    12. from smarthumb import SMARTHUMB
    13.  
    14.  
    15. box = (200, 200)
    16. db.auth_user.thumb.compute = lambda row: SMARTHUMB(row.avatar, box)

     

     

     

show more comments

Hosting graciously provided by:
Python Anywhere