изменить размер изображения при сохранении - PullRequest
15 голосов
/ 08 марта 2009

Как я могу легко изменить размер изображения после его загрузки в Django? Я использую Django 1.0.2, и я установил PIL.

Я думал о переопределении метода save () Модели, чтобы изменить его размер, но я действительно не знаю, как начать и переопределить его.

Может ли кто-нибудь указать мне правильное направление? Спасибо: -)

@ Guðmundur H: Это не будет работать, потому что пакет django-stdimage не работает в Windows: - (

Ответы [ 7 ]

23 голосов
/ 08 марта 2009

Я рекомендую использовать StdImageField из django-stdimage , он должен справиться со всей грязной работой за вас. Он прост в использовании, вы просто указываете размеры измененного изображения в определении поля:

class MyModel(models.Model):
    image = StdImageField(upload_to='path/to/img',  size=(640, 480))

Проверьте документы - он также может делать миниатюры.

12 голосов
/ 08 марта 2009

Вы должны использовать метод для обработки загруженного файла, как показано в документации Django .

В этом методе вы можете объединить фрагменты в переменной (а не записывать их непосредственно на диск), создать образ PIL из этой переменной, изменить размер изображения и сохранить его на диск.

В PIL вы должны смотреть на Image.fromstring и Image.resize.

11 голосов
/ 03 марта 2011

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

    def handle_uploaded_image(i):
        import StringIO
        from PIL import Image, ImageOps
        import os
        from django.core.files import File
        # read image from InMemoryUploadedFile
        image_str = “”
        for c in i.chunks():
            image_str += c

        # create PIL Image instance
        imagefile  = StringIO.StringIO(image_str)
        image = Image.open(imagefile)

        # if not RGB, convert
        if image.mode not in (“L”, “RGB”):
            image = image.convert(“RGB”)

        #define file output dimensions (ex 60x60)
        x = 130
        y = 130

        #get orginal image ratio
        img_ratio = float(image.size[0]) / image.size[1]

        # resize but constrain proportions?
        if x==0.0:
            x = y * img_ratio
        elif y==0.0:
            y = x / img_ratio

        # output file ratio
        resize_ratio = float(x) / y
        x = int(x); y = int(y)

        # get output with and height to do the first crop
        if(img_ratio > resize_ratio):
            output_width = x * image.size[1] / y
            output_height = image.size[1]
            originX = image.size[0] / 2 - output_width / 2
            originY = 0
        else:
            output_width = image.size[0]
            output_height = y * image.size[0] / x
            originX = 0
            originY = image.size[1] / 2 - output_height / 2

        #crop
        cropBox = (originX, originY, originX + output_width, originY + output_height)
        image = image.crop(cropBox)

        # resize (doing a thumb)
        image.thumbnail([x, y], Image.ANTIALIAS)

        # re-initialize imageFile and set a hash (unique filename)
        imagefile = StringIO.StringIO()
        filename = hashlib.md5(imagefile.getvalue()).hexdigest()+’.jpg’

        #save to disk
        imagefile = open(os.path.join(‘/tmp’,filename), ‘w’)
        image.save(imagefile,’JPEG’, quality=90)
        imagefile = open(os.path.join(‘/tmp’,filename), ‘r’)
        content = File(imagefile)

        return (filename, content)

#views.py

    form = YourModelForm(request.POST, request.FILES, instance=profile)
        if form.is_valid():
            ob = form.save(commit=False)
            try:
                t = handle_uploaded_image(request.FILES[‘icon’])
                ob.image.save(t[0],t[1])
            except KeyError:
                ob.save()
6 голосов
/ 08 марта 2009

Я настоятельно рекомендую приложение sorl-thumbnail для простого и прозрачного изменения размера изображения Это входит в каждый проект Django, который я запускаю.

2 голосов
/ 15 августа 2013

Вот полное решение для вас, используя форму. Я использовал админские представления для этого:

class MyInventoryItemForm(forms.ModelForm):

    class Meta:
        model = InventoryItem
        exclude = ['thumbnail', 'price', 'active']

    def clean_photo(self):
        import StringIO
        image_field = self.cleaned_data['photo']
        photo_new = StringIO.StringIO(image_field.read())

        try:
            from PIL import Image, ImageOps

        except ImportError:
            import Image
            import ImageOps

        image = Image.open(photo_new)

        # ImageOps compatible mode
        if image.mode not in ("L", "RGB"):
            image = image.convert("RGB")

        image.thumbnail((200, 200), Image.ANTIALIAS)

        image_file = StringIO.StringIO()
        image.save(image_file, 'png')

        image_field.file = image_file

        return image_field

Моя модель инвентаря выглядит так:

class InventoryItem(models.Model):

    class Meta:
        ordering = ['name']
        verbose_name_plural = "Items"

    def get_absolute_url(self):
        return "/products/{0}/".format(self.slug)

    def get_file_path(instance, filename):

        if InventoryItem.objects.filter(pk=instance.pk):
            cur_inventory = InventoryItem.objects.get(pk=instance.pk)
            if cur_inventory.photo:
                old_filename = str(cur_inventory.photo)
                os.remove(os.path.join(MEDIA_ROOT, old_filename))

        ext = filename.split('.')[-1]
        filename = "{0}.{1}".format(uuid.uuid4(), ext)
        return os.path.join('inventory', filename)
        #return os.path.join(filename)

    def admin_image(self):
        return '<img height="50px" src="{0}/{1}"/>'.format(MEDIA_URL, self.photo)
    admin_image.allow_tags = True

    photo = models.ImageField(_('Image'), upload_to=get_file_path, storage=fs, blank=False, null=False)
    thumbnail = models.ImageField(_('Thumbnail'), upload_to="thumbnails/", storage=fs,     blank=True, null=True)

....

Я прекратил перезаписывать функцию сохранения модели вместо того, чтобы сохранить фотографию и большой палец вместо простого изменения размера фотографии:

def save(self):

    # Save this photo instance first
    super(InventoryItem, self).save()

    from PIL import Image
    from cStringIO import StringIO
    from django.core.files.uploadedfile import SimpleUploadedFile

    # Set our max thumbnail size in a tuple (max width, max height)
    THUMBNAIL_SIZE = (200, 200)

    # Open original photo which we want to thumbnail using PIL's Image object
    image = Image.open(os.path.join(MEDIA_ROOT, self.photo.name))

    if image.mode not in ('L', 'RGB'):
        image = image.convert('RGB')

    image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)

    # Save the thumbnail
    temp_handle = StringIO()
    image.save(temp_handle, 'png')  # image stored to stringIO

    temp_handle.seek(0)  # sets position of file to 0

     # Save to the thumbnail field
     suf = SimpleUploadedFile(os.path.split(self.photo.name)[-1],
        temp_handle.read(), content_type='image/png')  # reads in the file to save it

    self.thumbnail.save(suf.name+'.png', suf, save=False)

    #Save this photo instance again to save the thumbnail
    super(InventoryItem, self).save()

Оба отлично работают, хотя в зависимости от того, что вы хотите сделать:)

2 голосов
/ 11 апреля 2013

Я знаю, что это старый, но для любого, кто наткнулся на него, есть пакет, django-thumbs at Django-thumbs - Легкие мощные эскизы для Django, интегрированные с StorageBackend , который автоматически генерирует миниатюры в размерах Вы указываете, или нет, если вы этого не сделаете. Затем вы вызываете нужную миниатюру с нужными размерами.

Например, если вы хотите, чтобы изображение имело миниатюры 64x64 и 128x128, вы просто импортируете thumbs.models.ImageWithThumbsField и используете его вместо ImageField. Добавьте параметр sizes=((64,64),(128,128)) в определение поля, затем из вашего шаблона вы можете вызвать:

{{ ClassName.field_name.url_64x64 }}

и

{{ ClassName.field_name.url_128x128 }}

для отображения миниатюр. Вуаля! Вся работа выполнена в этом пакете для вас.

0 голосов
/ 28 февраля 2019

Если вы используете Django Rest Framework, это может использовать:

Сначала определите функцию для сжатия и изменения размера изображения

def compress_image(photo):
# start compressing image
image_temporary = Image.open(photo)
output_io_stream = BytesIO()
image_temporary.thumbnail((1250, 1250), Image.ANTIALIAS)

# change orientation if necessary
for orientation in ExifTags.TAGS.keys():
    if ExifTags.TAGS[orientation] == 'Orientation':
        break
exif = dict(image_temporary._getexif().items())
# noinspection PyUnboundLocalVariable
if exif.get(orientation) == 3:
    image_temporary = image_temporary.rotate(180, expand=True)
elif exif.get(orientation) == 6:
    image_temporary = image_temporary.rotate(270, expand=True)
elif exif.get(orientation) == 8:
    image_temporary = image_temporary.rotate(90, expand=True)

# saving output
image_temporary.save(output_io_stream, format='JPEG', quality=75, optimize=True, progressive=True)
output_io_stream.seek(0)
photo = InMemoryUploadedFile(output_io_stream, 'ImageField', "%s.jpg" % photo.name.split('.')[0],
                             'image/jpeg', getsizeof(output_io_stream), None)
return photo

Во-вторых, теперь вы можете использовать функцию в сериализаторах:

class SomeSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
    # сжимаем рисунок
    if 'photo' in validated_data:           
        validated_data.update({'photo': compress_image(validated_data['photo'])})

    return super(SomeSerializer, self).update(instance, validated_data)

def create(self, validated_data):
    # сжимаем рисунок
    if 'photo' in validated_data:
        validated_data.update({'photo': compress_image(validated_data['photo'])})

    return super(SomeSerializer, self).create(validated_data)
...