14๐
image = image.resize(size, Image.ANTIALIAS)
resize is non-destructive, it returns a new image.
- [Django]-Removing 'Sites' from Django admin page
- [Django]-Django, Turbo Gears, Web2Py, which is better for what?
- [Django]-CORS error while consuming calling REST API with React
4๐
I searched for a solution to resize uploaded photo before saving. There are a lot of info bit and bit here and there (in StackOverflow). Yet, no complete solution. Here is my final solution that I think works for people who wants it.
Development Highlight
- Using Pillow for image processing (two packages required: libjpeg-dev, zlib1g-dev)
- Using Model and ImageField as storage
- Using HTTP POST or PUT with multipart/form
- No need to save the file to disk manually.
- Create multiple resolutions and stores their dimensions.
- Did not modify the Model itself
Install Pillow
$ sudo apt-get install libjpeg-dev
$ sudo apt-get install zlib1g-dev
$ pip install -I Pillow
myapp/models.py
from django.db import models
class Post(models.Model):
caption = models.CharField(max_length=100, default=None, blank=True)
image_w = models.PositiveIntegerField(default=0)
image_h = models.PositiveIntegerField(default=0)
image = models.ImageField(upload_to='images/%Y/%m/%d/', default=None,
blank=True, width_field='image_w', height_field='image_h')
thumbnail = models.ImageField(upload_to='images/%Y/%m/%d/', default=None,
blank=True)
This model saves a photo with thumbnail and an optional caption. This should be similar to the real-world use case.
Our goal is to resize the photo to 640ร640 and generate a 150ร150 thumbnail. We also need to return the dimension of the photo in our web API. image_w
and image_h
is a cached dimension in table. Note that we need to add quota โ when we declare an ImageField. However, thumbnail
does not need the width and height field (so you can see the different).
Thatโs the model. We donโt need to override or add any functions to the model.
myapp/serializers.py
We will use a ModelSerializer
to process the incoming data from HTTP POST.
from rest_framework import serializers
from myapp.models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('caption',)
We do not want the serializer to handle the uploaded photo โ we will do it ourself. So, no need to include โimageโ in fields.
myapp/views.py
@api_view(['POST'])
@parser_classes((MultiPartParser,))
def handle_uploaded_image(request):
# process images first. if error, quit.
if not 'uploaded_media' in request.FILES:
return Response({'msg': 'Photo missing.'}, status.HTTP_400_BAD_REQUEST)
try:
im = Image.open(StringIO(request.FILES['uploaded_media'].read()))
except IOError:
return Response({'msg': 'Bad image.'}, status.HTTP_400_BAD_REQUEST)
serializer = PostSerializer(data=request.DATA, files=request.FILES)
if not serializer.is_valid():
return Response({'msg': serializer.errors}, status.HTTP_400_BAD_REQUEST)
post = Post.create()
if serializer.data['caption'] is not None:
post.caption = serializer.data['caption']
filename = uuid.uuid4()
name = '%s_0.jpg' % (filename)
post.image.save(name=name, content=resize_image(im, 640))
name = '%s_1.jpg' % (filename)
post.thumbnail.save(name=name, content=resize_image(im, 150))
post.save()
return Response({'msg': 'success',
'caption': post.caption,
'image': {
'url': request.build_absolute_uri(post.image.url),
'width': post.image_w,
'height': post.image_h,
}
'thumbnail': request.build_absolute_uri(post.thumbnail.url),
}, status.HTTP_201_CREATED)
helper functions in a shared py
def resize_image(im, edge):
(width, height) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge)
content = StringIO()
im.resize((width, height), Image.ANTIALIAS).save(fp=content, format='JPEG', dpi=[72, 72])
return ContentFile(content.getvalue())
def scale_dimension(width, height, long_edge):
if width > height:
ratio = long_edge * 1. / width
else:
ratio = long_edge * 1. / height
return int(width * ratio), int(height * ratio)
Here, we do not use Image.thumbnail because it will change the original image. This code is suitable if we want to store multiple resolutions (such as low res and high res for different purpose). I found that resizing the incoming message twice will degrade the quality.
We also do not save the image directly to disk. We push the image via a ContentFile (a File object for content in memory) to ImageField and let the ImageField do its job. We wants the multiple copies of image have the same file name with different postfix.
Credits
Here are the links of codes that I had references to build this solution:
- Use of ContentFile in ImageField.save, by Martey: https://stackoverflow.com/a/7022005/3731039
- Use of StringIO for reading multipart/form, by johndoevodka: https://stackoverflow.com/a/15523422/3731039
- Code for resizing: http://davedash.com/2009/02/21/resizing-image-on-upload-in-django/
If you want to validate the image as well, see this: https://stackoverflow.com/a/20762344/3731039
- [Django]-How does django handle multiple memcached servers?
- [Django]-Error when using django.template
- [Django]-Custom django admin templates not working
1๐
here is what worked for me, inspired a little from django-resized
@receiver(pre_save, sender=MyUser)
@receiver(pre_save, sender=Gruppo)
def ridimensiona_immagine(sender, instance=None, created=False, **kwargs):
foto = instance.foto
foto.file.seek(0)
thumb = PIL.Image.open(foto.file)
thumb.thumbnail((
200,
200
), PIL.Image.ANTIALIAS)
buffer = StringIO.StringIO()
thumb.save(buffer, "PNG")
image_file = InMemoryUploadedFile(buffer, None, 'test.png', 'image/png', buffer.len, None)
instance.foto.file = image_file
- [Django]-Homepage login form Django
- [Django]-Choose test database?
- [Django]-Django simple_tag and setting context variables
0๐
If youโre using python < 3, you should consider using :
from __future__ import division
In this case the result number of your division will be float.
- [Django]-Table thumbnail_kvstore doesn't exist
- [Django]-Pytest.mark.parametrize with django.test.SimpleTestCase
- [Django]-Django logging of custom management commands
0๐
Iโve not yet test this solution, but it might help!!
#subidas.py
import PIL
from PIL import Image
def achichar_tamanho(path):
img = Image.open(path)
img = img.resize((230,230), PIL.Image.ANTIALIAS)
img.save(path)
In your models.py
, make the following changes:
from subidas import achicar_tamanho
class Productos(models.Model):
imagen = models.ImageField(default="emg_bol/productos/interrogacion.png", upload_to='emg_bol/productos/', blank=True, null=True, help_text="Image of the product")
tiempo_ultima_actualizacion = models.DateTimeField( help_text="Cuando fue la ultima vez, que se modificaron los datos de este producto", auto_now=True)
prioridad = models.IntegerField(max_length=4, default=99, help_text="Frecuencia (medida en unidad), con que el producto es despachado para la venta")
proveedor = models.ForeignKey(Proveedores, help_text="El que provello del producto'")
def save(self, *args, **kwargs):
super(Productos,self).save(*args, **kwargs)
pcod = "%s_%s" % ( re.sub('\s+', '', self.descripcion)[:3], self.id)
self.producto_cod = pcod
achichar_tamanho(self.imagen.path)
super(Productos,self).save(*args, **kwargs)
I really donโt know, what was the difference before and after implementing these changes.
- [Django]-Django.db.utils.ProgrammingError: relation "bot_trade" does not exist
- [Django]-How to add a cancel button to DeleteView in django
- [Django]-__init__() got an unexpected keyword argument 'mimetype'
0๐
A clean solution is to use this method to resize the image in the form before saving it: (you need pip install pillow
)
import os
from io import BytesIO
from PIL import Image as PilImage
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
def resize_uploaded_image(image, max_width, max_height):
size = (max_width, max_height)
# Uploaded file is in memory
if isinstance(image, InMemoryUploadedFile):
memory_image = BytesIO(image.read())
pil_image = PilImage.open(memory_image)
img_format = os.path.splitext(image.name)[1][1:].upper()
img_format = 'JPEG' if img_format == 'JPG' else img_format
if pil_image.width > max_width or pil_image.height > max_height:
pil_image.thumbnail(size)
new_image = BytesIO()
pil_image.save(new_image, format=img_format)
new_image = ContentFile(new_image.getvalue())
return InMemoryUploadedFile(new_image, None, image.name, image.content_type, None, None)
# Uploaded file is in disk
elif isinstance(image, TemporaryUploadedFile):
path = image.temporary_file_path()
pil_image = PilImage.open(path)
if pil_image.width > max_width or pil_image.height > max_height:
pil_image.thumbnail(size)
pil_image.save(path)
image.size = os.stat(path).st_size
return image
Then use it in the clean method of the image field in your form:
class ImageForm(forms.Form):
IMAGE_WIDTH = 450
IMAGE_HEIGHT = 450
image = forms.ImageField()
def clean_image(self):
image = self.cleaned_data.get('image')
image = resize_uploaded_image(image, self.IMAGE_WIDTH, self.IMAGE_HEIGHT)
return image
- [Django]-With DEBUG=False, how can I log django exceptions to a log file
- [Django]-How to implement FirebaseDB with a Django Web Application
- [Django]-How do I match the question mark character in a Django URL?
0๐
As for me Django resized does the trick and it is very easy to understand just pip install it and have a look at the docs
- [Django]-Django ignores router when running tests?
- [Django]-PyCharm: DJANGO_SETTINGS_MODULE is undefined
- [Django]-Form with CheckboxSelectMultiple doesn't validate