2009年5月15日金曜日

Python: 複数の画像ファイルを余白を埋めるように貼り付ける

このブログ記事をはてなブックマークに追加

何か面白いコードが書きたいなぁ、と思ってPython+PILで複数の画像ファイルを適当に余白を埋めるようにキャンバスに貼り付けるプログラムを書いてみたけどあまり面白くなかった。それでもせっかく書いたので一応公開する。

まず、入力した画像ファイルすべての面積を取得して、その面積と出力画像の面積の比を求める。すべての入力画像に対して、その比から大きさを変更する。これで入力画像の面積の総和と出力画像の面積が等しくなる。次に、入力画像を面積の大きい順にソートし、入力画像一つ一つと出力画像を重ね合わせ、もっとも余白との一致が大きい場所を見つける。その場所に入力画像を貼り付ける。貼り付ける際、全体の余白を少なくするために画像の大きさをランダムに1.2~1.5倍に拡大している。すべての入力画像を貼り付ければ完了だ。

ここの画像はフリー画像素材EyesPicに置いてあった植物写真の22枚を貼り付けてみたものだ。プログラムの使い方は、

place_pictures.py 出力画像ファイル 複数の入力画像ファイル...

のようにする。例えば、同じディレクトリにあるすべてのJPEGファイルをimage.jpgに貼り付ける場合、以下のようにすればよい。因みにデフォルトでは600×800ピクセルの大きさの画像となっている。コード内のXSIZE, YSIZEを指定することで自由に変更できる。

place_pictures.py image.jpg *.jpg

以下にソースコードを示す。

place_pictures.py

#!/usr/bin/env python import sys, os, glob, math, random, Image IMAGE_EXT = (".jpg", ".jpeg", ".jpe", ".png", ".bmp", ".gif", ".tif", ".tiff") IMAGE_XSIZE, IMAGE_YSIZE = 600, 800 def place_pictures(files, img_file): area = 0 sorted_files = [] for f in files: if f[-4:].lower() in IMAGE_EXT: im = Image.open(f) width, height = im.size a = width * height sorted_files.append((a, f)) area += a sorted_files.sort(reverse=True) files = map(lambda x: x[1], sorted_files) rate = math.sqrt(IMAGE_XSIZE * IMAGE_YSIZE / float(area)) new_im = Image.new("RGB", (IMAGE_XSIZE, IMAGE_YSIZE), (0, 0, 0)) for f in files: if os.path.splitext(f)[1].lower() in IMAGE_EXT: im = Image.open(f) rnd = random.uniform(1.2, 1.5) width, height = map(lambda x: int(x * rate * rnd), im.size) im = im.resize((width, height)) max_area_rate = 1.0 max_npixel = 0 paste_x, paste_y = random.randint(0, IMAGE_XSIZE - width), random.randint(0, IMAGE_YSIZE - height) mx = (IMAGE_XSIZE - (IMAGE_XSIZE / width + 1) * width) / 2 my = (IMAGE_YSIZE - (IMAGE_YSIZE / height + 1) * height) / 2 for rx in range(mx, IMAGE_XSIZE, width / 3): for ry in range(my, IMAGE_YSIZE, height / 3): rx1 = rx if rx >= 0 else 0 ry1 = ry if ry >= 0 else 0 rx2 = rx + width if rx + width < IMAGE_XSIZE else IMAGE_XSIZE ry2 = ry + height if ry + height < IMAGE_YSIZE else IMAGE_YSIZE crop_im = new_im.crop((rx1, ry1, rx2, ry2)) s = crop_im.tostring() npixel = len(s) area_rate = sum(map(lambda x: 0 if ord(x) == 0 else 1, s)) / float(npixel) if max_area_rate > area_rate or (max_area_rate == area_rate and max_npixel < npixel): max_area_rate = area_rate max_npixel = npixel paste_x, paste_y = rx, ry new_im.paste(im, (paste_x, paste_y)) new_im.save(img_file) new_im.show() def main(args): if len(args) < 3: print >>sys.stderr, "Usage: %s output-image-file image-files..." % os.path.basename(args[0]) sys.exit(1) files = [] img_file = args[1] for arg in args[2:]: files.extend(glob.glob(arg)) place_pictures(files, img_file) if __name__ == "__main__": main(sys.argv)

0 コメント: