Pythonで複数の画像を結合する方法

今日は複数の画像を結合する方法を紹介します。

以前ご紹介した方法は、NumPy の np.vstack( ) や np.hstack( ) を使って画像を結合するものでした。ただ、画像枚数が多くなると np.hstack( ) の部分を都度修正しなければならないため、汎用性に乏しかったです。

今回はもっと簡単に複数の画像が結合できるプログラムを作ってみました。

画像枚数を設定するだけで何枚でも結合させることができます。

興味のある方は是非参考にしてください。

本プログラムの特徴

  • 縦横の画像枚数を自由に設定できる
  • 画像と画像の間に隙間を開けることができる
  • 画像を何枚でも結合できる

プログラム

以下のコードをみてください。

import glob
#import math
import datetime
from PIL import Image
import PySimpleGUI as sg
from natsort import natsorted


### パラメータ設定 ###
# 結合する画像枚数
num_w = 3    # 横の枚数
num_h = 3    # 縦の枚数

# 画像と画像の間隔(ゼロは隙間なし)
margin_w = 0
margin_h = 0

### 画像選択、保存先 ###
fname = sg.popup_get_folder('画像が入ったフォルダを指定してください')
save_path = sg.popup_get_folder('画像の保存先フォルダを指定してください')

# 画像が入ったフォルダのパス
images = fname + '/' + '*.*'

# オリジナル画像のパスを読み込んでリスト化
files = [i for i in natsorted(glob.glob(images))]

# 画像を貼り付けるキャンバスを作成
img_1 = Image.open(files[0])
w, h = img_1.size
canvas = Image.new("RGBA", ((w + margin_w) * num_w, (h + margin_h) * num_h))

# 画像を読み込んでcanvasに貼付け、一枚の画像にする
for i in range(len(files)):

    img = Image.open(files[i])

    # 切り上げ計算
    #m = math.ceil((i+1) / num_w)   # math関数を使って切り上げを行う場合
    m = -(-(i+1) // num_w)          # 処理速度優先の場合(おすすめ)

    if i < num_w:
        canvas.paste(img, ((w + margin_w) * i, (h + margin_h) * (m-1)))
    else:
        canvas.paste(img, ((w + margin_w) * (i % num_w), (h + margin_h) * (m-1)))

# 画像を表示
canvas.show()

# 画像の縮小率(1は縮小無し。リサイズしたいときは#を除去し縮小率を設定する)
#img_size = 1

# 画像リサイズ(リサイズしたいときは#を除去する)
#canvas = canvas.resize((int(w//img_size), int(h//img_size)), Image.LANCZOS)

# 画像の保存(日付と時間をファイル名にして保存)
d_today = datetime.date.today()   # 日付の取得
dt_now = datetime.datetime.now()  # 時間の取得

canvas.save(save_path + '/' +
                str(d_today) + str("_") +
                str(dt_now.hour) + str("_") +
                str(dt_now.minute) + str("_") +
                str(dt_now.second) + '.png')

ポイント解説

1.横に並べる画像枚数と縦に並べる画像枚数を設定

コードのパラメータ設定の部分で、横に並べる画像枚数と縦に並べる画像枚数を設定します。

なお、横一列に並べる場合は、num_h = 1 として、num_w = 並べたい画像枚数とします。縦一列に並べる場合は、num_w = 1 として num_h = 並べたい画像枚数とします。

また、画像と画像の間に隙間を開けたいときは、margin_w や margin_h に適度な値を設定してください。

2.画像選択と保存先指定はPySimpleGUIの機能を使用

以下のコードで画像選択と保存先指定が簡単にできます。

fname = sg.popup_get_folder('画像が入ったフォルダを指定してください')
save_path = sg.popup_get_folder('画像の保存先フォルダを指定してください')

3.画像を張付けるcanvasを作成

下記コードでcanvas(台紙のようなもの)を作成しておき、そこに画像を張付けます。イメージとしては、台紙に画像というシールを貼って行くと考えると分かり易いと思います。

canvas = Image.new("RGBA", ((w + margin_w) * num_w, (h + margin_h) * num_h))

4.for文で画像のパスを一つずつ読み出しcanvasに張付ける

下記のコードをみてください。

for i in range(len(files)):
  img = Image.open(files[i])
  # 切り上げ計算
  #m = math.ceil((i+1) / num_w)   # math関数を使って切り上げを行う場合
  m = -(-(i+1) // num_w)          # 処理速度優先の場合(おすすめ)

  if i < num_w:
      canvas.paste(img, ((w + margin_w) * i, (h + margin_h) * (m-1)))
  else:
      canvas.paste(img, ((w + margin_w) * (i % num_w), (h + margin_h) * (m-1)))

ポイントは、if 文です。

初めに横方向に画像を張付けます。横方向の画像貼付けが終わったら、縦方向に画像の縦長さ一枚分ずれて、横方向に画像を張付けて行きます。

コードをみただけではピンとこないかもしれません。

興味のある方は、i の部分に 0、1、2・・・と順番に数字を入れて、どのような判定を行っているか確認してみてください。

5.最終画像をリサイズすることも可能

下記コードで最終画像をリサイズすることができます。

canvas = canvas.resize((int(w//img_size), int(h//img_size)), Image.LANCZOS)

複数の画像を結合すると、元の画像の縦横サイズがそのまま足し合わされたサイズになります。

例えば、元の画像サイズが 640 X 480 だとすると、横方向に3枚、縦方向に3枚結合すると最終画像のサイズは、1920 X 1440 になります。

枚数が増えれば増える程、最終画像のサイズは大きくなるためリサイズしたいときは上記コードを使用してください。

6.最終画像を日付を付けたファイル名で保存

日付で保存するのは最終画像の上書きを避けるためです。

後からリネイムした方が予期せぬ上書きが避けられ安全です。

# 画像の保存(日付と時間をファイル名にして保存)
d_today = datetime.date.today()   # 日付の取得
dt_now = datetime.datetime.now()  # 時間の取得

canvas.save(save_path + '/' +
                str(d_today) + str("_") +
                str(dt_now.hour) + str("_") +
                str(dt_now.minute) + str("_") +
                str(dt_now.second) + '.png')

まとめ

  • canvas を使って下地を用意しておくことで、複数の画像結合が容易になる
  • canvas は台紙のようなイメージで考えると分かり易い
  • canvas はリサイズ可能