web-dev-qa-db-ja.com

保存する前に、PILを使用して新しくアップロードした画像のサイズを変更するにはどうすればよいですか?

新しい画像の高さと幅を800pxに変更して、保存したいと思います。また、アプリは実際の画像を保存してはなりません。何か助けはありますか?

これは私のコードです。元の画像を保存し、サイズ変更された写真は保存しません。

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)
17
beni
image = image.resize(size, Image.ANTIALIAS)

サイズ変更は非破壊的であり、新しい画像を返します。

11
wmil

私は自分のプロジェクトに Django-resized を使用しています。

11
un1t

保存する前にアップロードした写真のサイズを変更する解決策を探しました。 (StackOverflowの)あちこちにたくさんの情報があります。しかし、完全な解決策はありません。これが私の最終的な解決策であり、それを望んでいる人々に役立つと思います。

開発ハイライト

  • 画像処理にPillowを使用する(2つのパッケージが必要:libjpeg-dev、zlib1g-dev)
  • ModelとImageFieldをストレージとして使用する
  • HTTP POSTまたはマルチパート/フォームでPUTを使用する
  • ファイルを手動でディスクに保存する必要はありません。
  • 複数の解像度を作成し、それらの寸法を保存します。
  • モデル自体を変更しませんでした

枕をインストールします

$ 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のサムネイルを生成することです。また、WebAPIで写真のサイズを返す必要があります。 image_wおよびimage_hは、テーブルにキャッシュされたディメンションです。 ImageFieldを宣言するときに、クォータを追加する必要があることに注意してください。ただし、thumbnailには幅と高さのフィールドは必要ありません(異なることがわかります)。

それがモデルです。モデルをオーバーライドしたり、モデルに関数を追加したりする必要はありません。

myapp/serializers.py

ModelSerializerを使用して、HTTPPOSTからの受信データを処理します。

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は使用しません。このコードは、複数の解像度(さまざまな目的のための低解像度と高解像度など)を保存する場合に適しています。受信メッセージのサイズを2回変更すると、品質が低下することがわかりました。

また、画像を直接ディスクに保存することもありません。 ContentFile(メモリ内のコンテンツのFileオブジェクト)を介して画像をImageFieldにプッシュし、ImageFieldにその仕事をさせます。画像の複数のコピーに同じファイル名と異なる接尾辞を付ける必要があります。

クレジット

このソリューションを構築するために参照したコードのリンクは次のとおりです。

イメージも検証する場合は、次を参照してください: https://stackoverflow.com/a/20762344/3731039

3
John Pang

これが私のために働いたもので、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
1
max4ever

python <3を使用している場合は、:の使用を検討する必要があります。

from __future__ import division

この場合、部門の結果番号は浮動小数点数になります。

0
Guillaume Cisco

私はまだこの解決策をテストしていませんが、それは役立つかもしれません!!

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

あなたのmodels.py、次の変更を加えます。

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
Pjl