如何在保存前使用PIL调整新上传的图像大小?

时间:2022-08-13 00:24:22

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?

我想以800px的高度和宽度调整新图像的大小并保存它们。应用程序不得存储真实图像。有帮助吗?

This is my code, it saves the original image and don't the resized photo:

这是我的代码,它保存原始图像,而不是调整大小的照片:

models.py:

models.py:

class Photo(models.Model):        
    photo = models.ImageField(upload_to='photos/default/')


    def save(self):

        if not self.id and not self.photo:
            return            

        super(Photo, self).save()

        image = Image.open(self.photo)
        (width, height) = image.size

        "Max width and height 800"        
        if (800 / width < 800 / height):
            factor = 800 / height
        else:
            factor = 800 / width

        size = ( width / factor, height / factor)
        image.resize(size, Image.ANTIALIAS)
        image.save(self.photo.path)

6 个解决方案

#1


10  

image = image.resize(size, Image.ANTIALIAS)

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

调整大小是非破坏性的,它会返回一个新图像。

#2


10  

I use django-resized for my projects.

我为我的项目使用django-resized。

#3


2  

I searched for a solution to resize uploaded photo before saving. There are a lot of info bit and bit here and there (in *). 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)
  • 使用Pillow进行图像处理(需要两个包:libjpeg-dev,zlib1g-dev)
  • Using Model and ImageField as storage
  • 使用Model和ImageField作为存储
  • Using HTTP POST or PUT with multipart/form
  • 使用HTTP POST或PUT与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

的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).

我们的目标是将照片大小调整为640x640并生成150x150缩略图。我们还需要在我们的Web API中返回照片的尺寸。 image_w和image_h是表中的缓存维度。请注意,我们在声明ImageField时需要添加配额。但是,缩略图不需要宽度和高度字段(因此您可以看到不同的)。

That's the model. We don't need to override or add any functions to the model.

那就是模特。我们不需要覆盖或添加任何函数到模型。

myapp/serializers.py

MYAPP / serializers.py

We will use a ModelSerializer to process the incoming data from HTTP POST.

我们将使用ModelSerializer处理来自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

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

共享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.

在这里,我们不使用Image.thumbnail,因为它会改变原始图像。如果我们想要存储多个分辨率(例如用于不同目的的低分辨率和高分辨率),则此代码是合适的。我发现两次调整传入消息的大小会降低质量。

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.

我们也不会将图像直接保存到磁盘。我们通过ContentFile(内存中的内容的File对象)将图像推送到ImageField,让ImageField完成它的工作。我们希望图像的多个副本具有相同的文件名和不同的后缀。

Credits

积分

Here are the links of codes that I had references to build this solution:

以下是我参考构建此解决方案的代码链接:

If you want to validate the image as well, see this: https://*.com/a/20762344/3731039

如果您还要验证图像,请参阅:https://*.com/a/20762344/3731039

#4


1  

here is what worked for me, inspired a little from django-resized

这对我有用,从django调整了一点灵感

@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

#5


0  

If you're using python < 3, you should consider using :

如果您使用的是python <3,则应考虑使用:

from __future__ import division

In this case the result number of your division will be float.

在这种情况下,您的部门的结果编号将是浮动的。

#6


0  

I'm not test this yet, but it may 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 you models

在你的模特

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 realy don't know, what the diferent before or after saving.

我真的不知道,保存之前或之后有什么不同。

#1


10  

image = image.resize(size, Image.ANTIALIAS)

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

调整大小是非破坏性的,它会返回一个新图像。

#2


10  

I use django-resized for my projects.

我为我的项目使用django-resized。

#3


2  

I searched for a solution to resize uploaded photo before saving. There are a lot of info bit and bit here and there (in *). 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)
  • 使用Pillow进行图像处理(需要两个包:libjpeg-dev,zlib1g-dev)
  • Using Model and ImageField as storage
  • 使用Model和ImageField作为存储
  • Using HTTP POST or PUT with multipart/form
  • 使用HTTP POST或PUT与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

的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).

我们的目标是将照片大小调整为640x640并生成150x150缩略图。我们还需要在我们的Web API中返回照片的尺寸。 image_w和image_h是表中的缓存维度。请注意,我们在声明ImageField时需要添加配额。但是,缩略图不需要宽度和高度字段(因此您可以看到不同的)。

That's the model. We don't need to override or add any functions to the model.

那就是模特。我们不需要覆盖或添加任何函数到模型。

myapp/serializers.py

MYAPP / serializers.py

We will use a ModelSerializer to process the incoming data from HTTP POST.

我们将使用ModelSerializer处理来自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

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

共享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.

在这里,我们不使用Image.thumbnail,因为它会改变原始图像。如果我们想要存储多个分辨率(例如用于不同目的的低分辨率和高分辨率),则此代码是合适的。我发现两次调整传入消息的大小会降低质量。

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.

我们也不会将图像直接保存到磁盘。我们通过ContentFile(内存中的内容的File对象)将图像推送到ImageField,让ImageField完成它的工作。我们希望图像的多个副本具有相同的文件名和不同的后缀。

Credits

积分

Here are the links of codes that I had references to build this solution:

以下是我参考构建此解决方案的代码链接:

If you want to validate the image as well, see this: https://*.com/a/20762344/3731039

如果您还要验证图像,请参阅:https://*.com/a/20762344/3731039

#4


1  

here is what worked for me, inspired a little from django-resized

这对我有用,从django调整了一点灵感

@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

#5


0  

If you're using python < 3, you should consider using :

如果您使用的是python <3,则应考虑使用:

from __future__ import division

In this case the result number of your division will be float.

在这种情况下,您的部门的结果编号将是浮动的。

#6


0  

I'm not test this yet, but it may 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 you models

在你的模特

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 realy don't know, what the diferent before or after saving.

我真的不知道,保存之前或之后有什么不同。