Как изменить размер новых загруженных изображений с помощью PIL перед сохранением?

13 beni [2011-11-01 20:55:00]

Я хочу изменить размеры новых изображений по высоте и ширине 800 пикселей и сохранить их. И приложение не должно сохранять реальный образ. Любая помощь?

Это мой код, он сохраняет исходное изображение и не изменяет размер фотографии:

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)

django django-models python-imaging-library


6 ответов


10 Решение wmil [2011-11-01 21:23:00]

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

изменение размера является неразрушающим, оно возвращает новое изображение.


9 un1t [2012-09-12 08:23:00]

Я использую django-resized для своих проектов.


2 John Pang [2014-08-09 22:02:00]

Я искал решение для изменения размера загруженной фотографии перед сохранением. Есть много бит информации и бит здесь и там (в StackOverflow). Но полного решения нет. Вот мое окончательное решение, которое, я думаю, работает для людей, которые этого хотят.

Выделение развития

  • Использование подушки для обработки изображений (требуется два пакета: libjpeg-dev, zlib1g-dev)
  • Использование модели и ImageField в качестве хранилища
  • Использование HTTP POST или PUT с multipart/form
  • Не нужно вручную сохранять файл на диск.
  • Создайте несколько разрешений и сохраните их размеры.
  • Не изменил сама модель

Установить подушку

$ 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)

Эта модель сохраняет фотографию с миниатюрой и дополнительной надписью. Это должно быть похоже на реальный случай использования.

Наша цель - изменить размер фотографии до 640x640 и создать миниатюру размером 150x150. Нам также нужно вернуть размер фотографии в наш веб-API. image_w и image_h - это кешированный размер в таблице. Обратите внимание, что нам нужно добавить квоту ', когда объявляем ImageField. Однако thumbnail не требуется поле ширины и высоты (так что вы можете видеть разные).

Это модель. Нам не нужно переопределять или добавлять какие-либо функции в модель.

MyApp/serializers.py

Мы будем использовать ModelSerializer для обработки входящих данных из HTTP POST.

from rest_framework import serializers
from myapp.models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('caption',)

Мы не хотим, чтобы сериализатор обрабатывал загруженную фотографию - мы сделаем это сами. Поэтому нет необходимости включать "изображение" в поля.

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)

вспомогательные функции в общей папке

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)

Здесь мы не используем Image.thumbnail, потому что он изменит исходное изображение. Этот код подходит, если мы хотим хранить несколько разрешений (например, low res и high res для разных целей). Я обнаружил, что изменение размера входящего сообщения приведет к ухудшению качества.

Мы также не сохраняем изображение непосредственно на диске. Мы нажимаем изображение через ContentFile (объект File для содержимого в памяти) на ImageField и позволяем ImageField выполнять свою работу. Мы хотим, чтобы несколько копий изображения имели одно и то же имя файла с другим постфиксным текстом.

Кредиты

Вот ссылки на коды, на которые я ссылался, чтобы построить это решение:

Если вы хотите также проверить изображение, см. это: qaru.site/questions/399767/...


1 max4ever [2014-06-09 12:28:00]

вот то, что сработало для меня, немного вдохновило 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 Guillaume Cisco [2011-11-01 21:21:00]

Если вы используете python < 3, вы должны рассмотреть возможность использования:

from __future__ import division

В этом случае число результатов вашего деления будет плавающим.


0 peter [2013-07-30 18:05:00]

Я еще не тестировал это, но это может помочь!

#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)

В моделях

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)

Я действительно не знаю, чем отличается до или после сохранения.