Cropping images is a fairly common use case in a Web application. For example, some applications let you upload a
profile picture. In addition, it usually also let you crop and resize the image for a better result. Unfortunately,
when dealing with image processing we need to install a few dependencies, both in the front-end and in the back-end.
In this tutorial I will demonstrate how to upload an image, display a preview in a modal, crop the image and finally
upload it and save in the server.
Within your project directory or virtualenv, install Pillow:
I know, I know… Sometimes installing Pillow is a nightmare. If you face any problem installing it locally, please
refer to the Pillow’s Installation Guide.
The jQuery utility to crop images in the Web browser can be
downloaded here. Or if you prefer to use it’s
pure JavaScript version, download Cropper.js here. But
please note that in the examples below I will be using the jQuery version.
Background
For this example, consider the following model:
models.py
I’m also using Bootstrap 3 in the example, so my base template looks like this:
base.html
The Image Cropping Form
In order to crop the image, we need four pieces of information: X coordinate, Y coordinate, height and width of the
cropping box the user will eventually play with in the browser.
forms.py
So far, what’s happening here: I defined a ModelForm using the Photo model. In this form, we will capture the
file field, which is our image. I also defined 4 new inputs to store the information we need to crop the image.
This example can be optimized, reducing the number of IO operations. It also need a few validations, to guarantee the
integrity of the data.
The Image Cropping View
The view is business as usual. Just form processing, nothing special. In this example, here is my basic function-based
view:
views.py
The Image Cropping Template
Here is where things get a little bit more tricky. First, check the structure of our template (please note that the
JavaScript can be extracted to an external file):
photo_list.html
Alright, let’s cover the HTML part first.
The form, simply render it as it is:
Don’t forget the enctype.
The form will render like this:
Now, the modal:
It’s just a regular Bootstrap modal with a few other options. The most important bit here is the placeholder image:
<img src="" id="image" style="max-width: 100%;">. It is very important that the image tag have a max-width of 100%.
A few css classes that will be used later on to trigger some actions:
.js-zoom-in
.js-zoom-out
.js-crop-and-upload
The modal will render like this:
(Nice picture, right? I took it with my phone in Oulu, Finland last summer.)
Finally, the container where I display the cropped images:
The final result:
The JavaScript
This is the last part. The JavaScript will put everything together.
Let’s break it down step by step, like we did in the template part.
Displaying the preview inside a modal after the user selects the image:
We’re adding a listener to the #id_file element, which is the file input from our form. When the data change (that
is, when the user select a new image), we preload it in the Web browser and open the modal.
The Cropper will be configured next, when the modal and the image is displayed to the user.
You can make further configuration inside the $image.cropper({ ... }). For further instructions check the
Cropper Docs.
I’m setting the aspect ratio to be 1:1 and the min width of the box to be 200x200. The view mode refers to limit the
image size to be at least the size of the box.
The last step now is to actually submit all this information to the server. At any time, you can grab the cropper data
by calling it like this:
That’s what we need. Let’s use this info to fill the extra information in our form:
And that’s it!
Final Template and Scripts
All the pieces together:
photo_list.html:
Conclusions
This is the do it yourself version of image cropping, so you can see how it works. It also gives you lot’s of freedom
to play with the options.
Please note that this is a preliminary implementation. Further validation of the uploaded image, the dimensions, etc is
necessary to guarantee a reliable result.
You can grab the code used in this example on GitHub: