diff --git a/apps/member/forms.py b/apps/member/forms.py
index a5d571b634ba7b16c28b5921ca6bc2bc5434a07e..abefdf2c2bf3dc1095ec7de97fff6c637602c4b6 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -1,7 +1,11 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+import io
+
+from PIL import Image
 from django import forms
+from django.conf import settings
 from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.models import User
 from django.forms import CheckboxSelectMultiple
@@ -77,6 +81,38 @@ class ImageForm(forms.Form):
     width = forms.FloatField(widget=forms.HiddenInput())
     height = forms.FloatField(widget=forms.HiddenInput())
 
+    def clean(self):
+        """Load image and crop"""
+        cleaned_data = super().clean()
+
+        # Image size is limited by Django DATA_UPLOAD_MAX_MEMORY_SIZE
+        image = cleaned_data.get('image')
+        if image:
+            # Let Pillow detect and load image
+            try:
+                im = Image.open(image)
+            except OSError:
+                # Rare case in which Django consider the upload file as an image
+                # but Pil is unable to load it
+                raise forms.ValidationError(_('This image cannot be loaded.'))
+
+            # Crop image
+            x = cleaned_data.get('x', 0)
+            y = cleaned_data.get('y', 0)
+            w = cleaned_data.get('width', 200)
+            h = cleaned_data.get('height', 200)
+            im = im.crop((x, y, x + w, y + h))
+            im = im.resize(
+                (settings.PIC_WIDTH, settings.PIC_RATIO * settings.PIC_WIDTH),
+                Image.ANTIALIAS,
+            )
+
+            # Save
+            image.file = io.BytesIO()
+            im.save(image.file, "PNG")
+
+        return cleaned_data
+
 
 class ClubForm(forms.ModelForm):
     def clean(self):
diff --git a/apps/member/views.py b/apps/member/views.py
index 4534c9e8be96b7eeb4c4dd850f4676f522aa99ac..2a0394ffc131b4144075216294881f1a55eba120 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -1,10 +1,8 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-import io
 from datetime import timedelta, date
 
-from PIL import Image
 from django.conf import settings
 from django.contrib.auth import logout
 from django.contrib.auth.mixins import LoginRequiredMixin
@@ -263,6 +261,7 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
         return context
 
     def get_success_url(self):
+        """Redirect to profile page after upload"""
         return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})
 
     def post(self, request, *args, **kwargs):
@@ -271,26 +270,9 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
         return self.form_valid(form) if form.is_valid() else self.form_invalid(form)
 
     def form_valid(self, form):
+        """Save image to note"""
         image_field = form.cleaned_data['image']
-        x = form.cleaned_data['x']
-        y = form.cleaned_data['y']
-        w = form.cleaned_data['width']
-        h = form.cleaned_data['height']
-        # image crop and resize
-        image_file = io.BytesIO(image_field.read())
-        # ext = image_field.name.split('.')[-1].lower()
-        # TODO: support GIF format
-        image = Image.open(image_file)
-        image = image.crop((x, y, x + w, y + h))
-        image_clean = image.resize((settings.PIC_WIDTH,
-                                    settings.PIC_RATIO * settings.PIC_WIDTH),
-                                   Image.ANTIALIAS)
-        image_file = io.BytesIO()
-        image_clean.save(image_file, "PNG")
-        image_field.file = image_file
-        # renaming
-        filename = "{}_pic.png".format(self.object.note.pk)
-        image_field.name = filename
+        image_field.name = "{}_pic.png".format(self.object.note.pk)
         self.object.note.display_image = image_field
         self.object.note.save()
         return super().form_valid(form)