LoginSignup
0
0

Djangoでアップロードされた画像を保存前に加工する

Last updated at Posted at 2023-08-20

Djangoでアップロードされた画像を保存前に加工したいと思って調べたんですが、大体普通に保存した後、また開き直して加工するパターンが多かったです。
AIの力を拝借したところ、いい感じに処理できるようになったので共有します。多分ちゃんと調べれば出てくる内容なので、二番煎じになっていると思います。

バージョン

PythonとDjangoのバージョンです。(関係ないですが、Djangoって破壊的なバージョン変更多いみたいなので、投稿するときはしっかりバージョン書いてほしいですね。)
ラズパイ4B上のDockerコンテナで動かしています。

# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

# python --version
Python 3.11.4

# python -m django --version
4.2.4

実装

つらつらとファイルを上げていますが、重要なのは、views.pyです。
説明自体は後述します。

  • モデル
models.py
from django.db import models

class UploadImage(models.Model):
    # 画像
    image = models.ImageField(upload_to='img/')
    # 保存日時
    pub_date = models.DateTimeField('date published', auto_now_add=True)

    # 画像の名称を表示
    def __str__(self):
        return self.image.name
  • 画像投稿用の静的ファイル
img_form.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>{{title}}</title>
</head>
<body>
    <h1>{{title}}</h1>
    <div>
        {% if id is not None %}
        <h2>画像が登録されました</h2>
        <p>画像のIDは {{id}} です</p>
        {% endif %}
    </div>
    <div>
        <h2>画像を登録する</h2>
        <form action="{% url 'img_form' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <p>{{ upload_form.as_p }}</p>
            <p><input type="submit" value="アップロード"></p>
        </form>
    </div>
</body>
</html>
  • 投稿用フォーム
forms.py
from django import forms
from .models import UploadImage

class UploadForm(forms.ModelForm):
    class Meta:
        model = UploadImage
        fields = ('image', )
  • 投稿時の処理
views.py
from django.shortcuts import render, get_object_or_404
from .forms import UploadForm, DisplaySettingsForm
from .models import UploadImage
from mqttlib.message import HOST, PORT, DisplayImages

from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from pathlib import Path

# 画像サイズ
IMG_SIZE: tuple[int, int] = (800, 480)

def upload_img_form(request):
    params = {
        'title': '画像のアップロード',
        'upload_form': UploadForm(),
        'id': None,
    }

    if (request.method == 'POST'):
        form: UploadForm = UploadForm(request.POST, request.FILES)
        if form.is_valid():
            # メモリ上のデータを読み込む
            mem_img: InMemoryUploadedFile = form.cleaned_data['image']
            # InMemoryUploadedFileのデータをBytesIOにコピー
            byte_io: BytesIO = BytesIO()
            for chunk in mem_img.chunks():
                byte_io.write(chunk)
            byte_io.seek(0)

            # BytesIOからPIL Imageを読み込む
            org_img: Image.Image = Image.open(byte_io)

            # 画像の加工
            tgt_img: Image.Image = org_img.resize(IMG_SIZE)
            image_io: BytesIO = BytesIO()
            tgt_img.save(image_io, format="BMP")
            bmp_path: str = str(Path(mem_img.name).with_suffix('.bmp'))

            # 画像の保存
            uploaded_file = InMemoryUploadedFile(
                image_io, None, bmp_path, mem_img.content_type,
                image_io.tell(), None
            )
            form.cleaned_data['image'] = uploaded_file
            form.instance.image = uploaded_file
            post = form.save()
            params['id'] = post.id
            pass
    return render(request, 'upload_app/img_form.html', params)

変数名が微妙に内容に即していない感がありますが気にしないでください。
ひとまとまりの処理毎にコメント行を書いているので大体わかっていただけるかと思います。
やっている処理内容としては、日本語にすると以下の流れです。

  1. POSTされたデータのInMemoryUploadedFile(この時点ではメモリ上にしか存在しない)を取り出す
  2. PIL.Imageに変換するため、BytesIOのバッファに移してPIL.Imageで開く
  3. 画像データの加工。ここではIMG_SIZEへのサイズ変更と、ファイル内容をBMPに変換
  4. PIL.ImageからInMemoryUploadedFileへの変換(さっきの逆)
  5. 加工したデータを元の奴と挿げ替えて保存

最後の挿げ替えに関して、多分form.instance.imageに加工後データのuploaded_fileを代入しさえすればよくて、form.cleaned_data['image']への代入は要らない気がしますが、何となく変数の中身がちぐはぐだと気持ち悪かったので入れています。

参考

Django で簡単な画像処理を行う(PIL 利用)
DjangoでPillowを使って登録画像を特定のサイズにリサイズする

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0