[python]複数の画像を連結、リサイズしてウディタのマップチップ画像にする

概要

この間買った大量の加工可能、ゲームエンジン自由のマップチップをウディタで扱いたいので書きました。

必要なチップを選択して一枚の画像にしたいだけならぴぽやさんの下記の記事がおすすめです。

Tiledでタイルセット画像を並び替えたりパーツ合成する方法 - ぴぽや倉庫
本記事は2017年3月に旧ブログに掲載したものをそのまま転載しております。Tiledは2019年10月現在も活発にアップデートが行われているため、記事内容と最新版との機能では若干違う部分もあると思いますので、ご了承ください。 WOLF

環境

Python3.7

更新履歴

2021-07-17 Tiledが便利だったので概要に記事を追加
2021-07-05 以前のコードだと一部変換できない画像が出てきたので修正

参考

Installing scikit-image — skimage v0.18.0 docs
Python, Pillowで画像の一部をトリミング(切り出し/切り抜き) | note.nkmk.me
Pythonの画像処理ライブラリPillow(PIL)のImageモジュールに、画像の一部の領域を切り抜くメソッドcrop()が用意されている。Image Module — Pillow (PIL Fork) 4.2.1 documentation ここでは以下の4つの場合についてサンプルコードとともに説明する。通常の...

※ほか同サイトのいろいろな記事

内容

インストール

Pillowとscikit-imageとOpenCV使用するのでインストールします。
いろいろあってキメラにライブラリを使っています…。

pip install Pillow
pip install scikit-image
pip install opencv-python

コード

# coding: utf-8
import os

import cv2
from PIL import Image
import skimage.util
import skimage.io
import numpy as np

# ウディタマップのタイルサイズ
tile_size = 32

# 画像読み込み(保存ファイル名、リサイズ時の拡大率、ソースのマップチップ)
image_list = [
	{"map_name": "[ウディタ]始まりの村", 
	"resize": 1, 
	"map_list": [ 
		'なんとか_01.png',
		'なんとか_02.png',
		'なんとかマップ_03.png',
	]},
	{"map_name": "[ウディタ]最終ダンジョン", 
	"resize": 2, 
	"map_list": [
		'沼.png',
		'魔王城.png',
		'調度品など.png',
	]},
]

# ウディタ最大横幅
max_width = tile_size * 8

# トリミングの始点終点
slice_width_from = 0
slice_width_to = 0

# 結合用リストサイズ
slice_count_list = {}


# マップ単位のループ
for img_obj in image_list:
	total_slice_count = 0
	# マップリスト単位のループ
	for img_name in img_obj["map_list"]:
		image = Image.open('./in/' + img_name)

		# 画像サイズ
		w = image.size[0]
		h = image.size[1]
		slice_count = 0

		# 必要な場合画像リサイズ
		if img_obj["resize"] > 1:
			# あまり正確ではないので整数以外入れないほうがいい
			w = int(w * img_obj["resize"])
			h = int(h * img_obj["resize"])
			image = image.resize((w, h), Image.NEAREST)
		while slice_count < (w / max_width):
			# 切り出せる横幅の計算
			slice_width_from = slice_count * max_width
			slice_width_to = slice_width_from + max_width

			# max_width幅で切り出し(slice_width_from: h, 0: slice_width_to)
			img1 = image.crop((slice_width_from, 0, slice_width_to, h))

			# 画像の書き出し
			out_file_name = "./out/" + img_obj["map_name"] + '_' + str(total_slice_count) + '.png'
			img1.save(out_file_name, 'PNG')
			print(out_file_name)

			# 処理回数記録
			slice_count = slice_count + 1
			total_slice_count = total_slice_count + 1
	# 最終的な処理回数
	slice_count_list[img_obj["map_name"]] = total_slice_count

print(slice_count_list)
# 画像結合
for img_obj in image_list:
	print(img_obj)
	cnt = 0
	max_cnt = int(slice_count_list[img_obj["map_name"]])
	print(max_cnt)
	out_img_list = []
	while cnt < max_cnt:
		filepath = './out/' + img_obj["map_name"] + '_' + str(cnt) + ".png"

		# cv2はアスキー文字以外の読み込みができないのでskimageで行う
		# img_item = cv2.imread(filepath)
		img_item = skimage.io.imread(filepath)
		out_img_list.append(img_item)

		# 結合用に作ったファイルを削除
		os.remove(filepath)
		cnt = cnt + 1

	# 画像結合して保存
	# Pillowでの画像結合は概念が面倒で、skimageで画像結合するとndimが4以外になって落ちることが頻発するので結合はOpenCvを使うことにした
	im_v = cv2.vconcat(out_img_list)
	out_file_name = "./out/" + img_obj["map_name"] + '.png'

	# OpenCVで保存するとRBが入れ替わって悲惨なことになるのでskimageで保存
	skimage.io.imsave(out_file_name, im_v)

使い方

ファイルと同じ階層に「in」フォルダと「out」フォルダを作って、inフォルダの方に結合したいマップを入れます。

コード内の「結合対象のマップチップリスト」を編集してください。項目の詳細は下記の通りです。
map_name: 最終的な保存ファイル名を書く
resize: リサイズ時の拡大率、2倍にして切り取りたい場合は2、そのままでいいなら1。整数で入力してください。
map_list: 切り取りしたいマップチップのリストを拡張子付きで並べる
pythonを実行後は、resizeの拡大率でリサイズされたmap_listの画像が、ウディタ規格のマップチップの幅(タイルサイズ*8倍)にカットされて、map_nameの名前で縦に結合されます。

仮に上記のコードのまま、実行すると、「out」フォルダに下記のファイルが現れるはずです。
[ウディタ]始まりの村.png
[ウディタ]最終ダンジョン.png

おわり。