社会人研究者が色々頑張るブログ

pythonで画像処理やパターン認識をやっていきます

Pythonでバラバラな画像をいい感じに詰め合わせる

はじめに

今回は、サイズの異なる複数枚の画像をいい感じに1枚の画像にする方法を考えていきます。
画像サイズが同じであれば簡単に画像を並べる事が出来るのですが、今回はもう少しおしゃれな感じにしたいです。

こうでなく こう!
f:id:nsr_9:20211110190711p:plain f:id:nsr_9:20211110190718p:plain

右の画像は適当に手で並べ替えただけなのでちょっと不格好ですが、イメージはこんな感じです。
※この素敵なイラストは友人に掲載許可を頂きました[1]

長方形詰め込み問題

今回のやりたい事を整理すると次のようになります。

任意のサイズの複数枚の画像を、制限された領域に効率的に配置したい

この様にある条件に基づいて効率的な方法を探す問題は、組み合わせ最適化問題と呼ばれます。
組み合わせ最適化問題はとても奥が深く、とてもとても説明しきれる量ではない為、今回は省略します。

今回やりたい長方形を効率的に詰め込む問題は、組み合わせ最適化問題の中でも特に「典型的な例」として扱われています。
典型例と言われるだけあってこの問題は数多くの研究が行われており、更には簡単に扱えるPythonライブラリまで公開されています。

pythonによる長方形詰め込み問題

rectpackと呼ばれる長方形詰め込み問題を解くライブラリを使用します。
GitHub - secnot/rectpack: Python 2D rectangle packing library

pipを使うことで簡単にインストールすることが出来ます。

pip install rectpack

使い方は簡単で画像を配置する領域のサイズをbins, 配置する画像をrectとして与えます。

from tectpack import newPacker
rectangles = [(100, 30), (40, 60), (30, 30),(70, 70), (100, 50), (30, 30)] # 配置する長方形
bins = [(300, 450)]  # 領域のサイズ

packer = newPacker()

# 長方形の登録
for rect in rectangle:
    packer.add_rect(*r)

# 領域の登録
for b in bins:
    packer.add_bin(*b)

# 最適化の実行
packer.pack()

出力は次のように取り出せます。

packer.all_rect_list()
項目 説明
b ビンインデックス
x 長方形のX座標
y 長方形のY座標
w 長方形の幅
h 長方形の高さ
rid 長方形のID

実装

指定のフォルダに保存された画像をパッキングするプログラムを実装しました。

import glob
import cv2
import sys
import numpy as np
from rectpack import newPacker


def get_items(dir_path):
    files = glob.glob(dir_path+"/*.*")
    images = list()
    items = list()

    for f in files:
        img = cv2.imread(f)
        item = (img.shape[1], img.shape[0])
        images.append(img)
        items.append(item)
    return images, items


def packing(images, out_items, width, height):
    # 画像を配置する
    out = np.zeros((height, width, 3), dtype=np.uint8)
    for item in out_items:
        index, x, y, w, h = item
        img = images[index]
        if w == img.shape[0]:
            img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
        out[y: y+h, x: x+w] = img


    return out


if __name__ == "__main__":
    images, items = get_items(sys.argv[1])
    width = int(sys.argv[2])
    height = int(sys.argv[3])

    packer = newPacker()
    packer.add_bin(width, height, bid=0)

    for index, item in enumerate(items):
        packer.add_rect(item[0], item[1], rid=index)

    packer.pack()
    out_items = list()

    for r in packer.rect_list():
        out_items.append((r[5], r[1], r[2], r[3], r[4]))

    out = packing(images, out_items, width, height)
    cv2.imwrite("out.png", out)

実行すると次のような出力を得ました。

512x512
f:id:nsr_9:20211110220031p:plain
800x512
f:id:nsr_9:20211110220246p:plain
800x800
f:id:nsr_9:20211110220221p:plain
1024x1024
f:id:nsr_9:20211110220200p:plain

いい感じに並べられていますね!

おわりに

長方形配置問題を用いて、サイズがバラバラな画像をいい感じに並べられるようになりました。 今後はこのプログラムを用いて、様々な図を可視化していきたいと思います。

[1] 木密るう - pixiv