Как изменить размер новых загруженных изображений с помощью 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 выполнять свою работу. Мы хотим, чтобы несколько копий изображения имели одно и то же имя файла с другим постфиксным текстом.
Кредиты
Вот ссылки на коды, на которые я ссылался, чтобы построить это решение:
- Использование ContentFile в ImageField.save, Martey: qaru.site/questions/399763/...
- Использование StringIO для чтения multipart/form, by johndoevodka: qaru.site/questions/399764/...
- Код для изменения размера: http://davedash.com/2009/02/21/resizing-image-on-upload-in-django/
Если вы хотите также проверить изображение, см. это: 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)
Я действительно не знаю, чем отличается до или после сохранения.