【Python】Pillowを使ったサムネイル画像作成

2024年2月10日

やりたいこと

Pythonを使って画像をリサイズして下画像のような正方形、円形のサムネイルを作ります。

正方形のサムネイル
円形のサムネイル

正方形サムネイル作成処理の概要

正方形のサムネイルを作成する処理の手順は以下の通りです。元画像が正方形とは限らないため、縦横比を保って縮小した後に正方形にトリミングします。

  1. 元画像の縦と横の短い方がサムネイルのサイズになるまで縦横比を保ったまま縮小する
  2. 縮小した画像の中心から正方形にトリミング
正方形サムネイルの作成手順

円形サムネイル作成処理の概要

画像ファイルの形は必ず四角ですので、元画像にPNGの透明画像をPillowのcompositeメソッドでマスクを指定し重ねることで円形に表示されるようにします。

  • 元画像と同じサイズの透明画像を作成する
  • 元画像を残す円形部分を白、その他を黒にしたマスクを作成する
  • 透明画像と元画像をマスクを使って合成する
円形サムネイルの作成手順

マスク中の白部分は元画像、黒部分は透明画像となるため円形に表示される画像を作ることができます。

Pythonによる実装

下記のディレクトリ構成でimgディレクトリ内のorig_image.jpgから正方形サムネイル(square_thumbnail.jpg)と円形サムネイル(square_thumbnail.jpg)を作成しthumbnailディレクトに保存します。

.
├── generate_thumbnail.py
├── img
│   └── orig_image.jpg
└── thumbnail
    ├── circle_thumbnail.png
    └── square_thumbnail.jpg

Pythonのソースコードは以下の通りです。検証用に作成しているため元画像や保存先は固定値、エラー処理なども省略しています。

from PIL import Image
from PIL import ImageDraw

THUMBNAIL_SIZE = 150

def create_square_thumbnail(src_image):

    width, height = src_image.size

    # アスペクト比を保ってリサイズするために元画像とサムネイルのサイズから縮小率を計算する
    resize_ratio = THUMBNAIL_SIZE/ min(width, height) 

    new_width = int(width * resize_ratio)
    new_height = int(height * resize_ratio)
    
    new_center_x = new_width // 2
    new_center_y = new_height // 2

    # 元画像をリサイズ
    image_resized = src_image.resize((int(width * resize_ratio), int(height * resize_ratio)))

    # 正方形にトリミング
    return image_resized.crop((new_center_x - THUMBNAIL_SIZE / 2, new_center_y - THUMBNAIL_SIZE / 2, new_center_x + THUMBNAIL_SIZE / 2, new_center_y + THUMBNAIL_SIZE / 2))


def create_circle_thumbnail(src_image):

    # 透明画像
    transparent_image = Image.new("RGBA", (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (0, 0, 0, 0))

    # 円形のマスク
    mask = Image.new("L", (THUMBNAIL_SIZE, THUMBNAIL_SIZE), 0)
    draw = ImageDraw.Draw(mask)
    draw.ellipse((0, 0, THUMBNAIL_SIZE - 1, THUMBNAIL_SIZE -1), fill=255)
    del draw

    # 元画像と透明画像を合成
    return Image.composite(src_image, transparent_image, mask)


def main():
    # 元画像を開く
    src_image = Image.open("img/orig_image1.jpg")

    # 正方形サムネイルを作成
    square_thumbnail = create_square_thumbnail(src_image)
    square_thumbnail.save("thumbnail/square_thumbnail.jpg")

    # 正方形サムネイルから円形サムネイルを作成
    circle_thumbnail = create_circle_thumbnail(square_thumbnail)
    circle_thumbnail.save("thumbnail/circle_thumbnail.png")


if __name__ == "__main__": 
    main()