Как изменить размер новых загруженных изображений с помощью PIL перед сохранением? - PullRequest
15 голосов
/ 01 ноября 2011

Я хочу изменить размеры новых изображений в высоту и ширину 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)

Ответы [ 6 ]

11 голосов
/ 12 сентября 2012

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

11 голосов
/ 01 ноября 2011
image = image.resize(size, Image.ANTIALIAS)

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

2 голосов
/ 09 августа 2014

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

Highlight Development

  • Использование подушки для обработки изображений (требуется два пакета: libjpeg-dev, zlib1g-dev)
  • Использование Model и 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',)

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

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)

вспомогательные функции в общем 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)

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

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

Кредиты

Вот ссылки на коды, на которые я ссылался при создании этого решения:

Если вы также хотите проверить изображение, посмотрите это: https://stackoverflow.com/a/20762344/3731039

1 голос
/ 09 июня 2014

вот что у меня сработало, немного вдохновило 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 голосов
/ 30 июля 2013

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

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

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

0 голосов
/ 01 ноября 2011

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

from __future__ import division

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

...