How to resize the new uploaded images using PIL before saving?

后端 未结 6 572
被撕碎了的回忆
被撕碎了的回忆 2020-12-13 20:28

I want to resize the new images in a height and width of 800px and save them. And the app mustn\'t store the real image. Any help?

This is my code, it saves the orig

相关标签:
6条回答
  • 2020-12-13 20:47
    image = image.resize(size, Image.ANTIALIAS)
    

    resize is non-destructive, it returns a new image.

    0 讨论(0)
  • 2020-12-13 20:58

    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.

    0 讨论(0)
  • 2020-12-13 21:01

    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 640x640 and generate a 150x150 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

    0 讨论(0)
  • 2020-12-13 21:05

    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
    
    0 讨论(0)
  • 2020-12-13 21:12

    I use django-resized for my projects.

    0 讨论(0)
  • 2020-12-13 21:12

    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.

    0 讨论(0)
提交回复
热议问题