Pythonでバラバラな画像をいい感じに詰め合わせる
はじめに
今回は、サイズの異なる複数枚の画像をいい感じに1枚の画像にする方法を考えていきます。
画像サイズが同じであれば簡単に画像を並べる事が出来るのですが、今回はもう少しおしゃれな感じにしたいです。
こうでなく | こう! |
---|---|
右の画像は適当に手で並べ替えただけなのでちょっと不格好ですが、イメージはこんな感じです。
※この素敵なイラストは友人に掲載許可を頂きました[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 |
---|
800x512 |
---|
800x800 |
---|
1024x1024 |
---|
いい感じに並べられていますね!
おわりに
長方形配置問題を用いて、サイズがバラバラな画像をいい感じに並べられるようになりました。 今後はこのプログラムを用いて、様々な図を可視化していきたいと思います。
[1] 木密るう - pixiv