2021/10/13(水) [n年前の日記]
#1 [python][pygame] 別幅で道路の自動生成を試してる。その5
 8x8のタイルで道路の自動生成ができないか試しているところ。使用環境は、Windows10 x64 21H1 + Python 3.9.7 64bit + Pygame 2.0.1。
なんとかどうにかある程度は一応それらしく見えてくれるような感じになってきた気がする。まだ盛り込めてない処理が多いけど…。
 なんとかどうにかある程度は一応それらしく見えてくれるような感じになってきた気がする。まだ盛り込めてない処理が多いけど…。
◎ ソースと使用画像その他。 :
ソースと使用画像その他も一応アップロード。CC0 / Public Domain ってことで。
_make_road_ml.py
_road_chip.png
_block_road_middle.json
_block_road_large.json
同じフォルダに入れて、python make_road_ml.py で実行できる。
以下のキー操作を受け付ける。
_make_road_ml.py
_road_chip.png
_block_road_middle.json
_block_road_large.json
同じフォルダに入れて、python make_road_ml.py で実行できる。
以下のキー操作を受け付ける。
- SPACEキー: 道路を作り直し。
- Gキー : グリッド表示。
- Sキー : 生成した道路をpng画像として保存。
- カーソルキー or WASDキー : スクロール。(生成画像がウインドウより大きい場合)
- Qキー, ESCキー : 終了
◎ パターン定義ファイルの生成。 :
今回、8x8タイルで1パターンとして扱って処理をしてみたけれど、パターンの作成にはマップエディタ Tiled を利用させてもらった。
_Tiled | Flexible level editor
Tiled用のファイルは以下。
_road_large.tmx
_road_middle_new.tmx
_road_chip.tsx
_road_chip.json
_road_chip.png
今回は、以下のような見た目のパターンを作った。


番号を描き込んでみると以下のような感じ。

Tiled から .json でエクスポートして、Python + Pygame で利用できるように変換したい。エクスポートした .json は以下。
_road_large.json
_road_middle_new.json
この2つの .json を Pythonスクリプトで読み込んで、パターン定義ファイルとして利用できる .json に変換する。変換用 Pythonスクリプトは以下。処理内容は、1次元配列を2次元配列にしているだけ…。
_make_road_pat_json.py
python make_road_pat_json.py を実行すると、以下の2つのファイルを生成・保存する。
_block_road_middle.json
_block_road_large.json
それぞれ、パターン定義ファイルとして利用できるかどうか動作確認するための Pythonスクリプトも書いた。
_test_draw_blocks.py
python test_draw_blocks.py を実行すると、block_road_large.json を読み込んで、ウインドウに表示する。スクリプト内のファイル名指定を書き換えれば、block_road_middle.json も読み込める。
_Tiled | Flexible level editor
Tiled用のファイルは以下。
_road_large.tmx
_road_middle_new.tmx
_road_chip.tsx
_road_chip.json
_road_chip.png
今回は、以下のような見た目のパターンを作った。


番号を描き込んでみると以下のような感じ。

Tiled から .json でエクスポートして、Python + Pygame で利用できるように変換したい。エクスポートした .json は以下。
_road_large.json
_road_middle_new.json
この2つの .json を Pythonスクリプトで読み込んで、パターン定義ファイルとして利用できる .json に変換する。変換用 Pythonスクリプトは以下。処理内容は、1次元配列を2次元配列にしているだけ…。
_make_road_pat_json.py
python make_road_pat_json.py を実行すると、以下の2つのファイルを生成・保存する。
_block_road_middle.json
_block_road_large.json
それぞれ、パターン定義ファイルとして利用できるかどうか動作確認するための Pythonスクリプトも書いた。
_test_draw_blocks.py
python test_draw_blocks.py を実行すると、block_road_large.json を読み込んで、ウインドウに表示する。スクリプト内のファイル名指定を書き換えれば、block_road_middle.json も読み込める。
◎ 課題。 :
生成結果をよく見ると、中幅道路と太幅道路の、交差部分の見た目がおかしいわけで…。道路脇の(路肩|路側帯)もドット絵に含ませているものだから、交差部分にもソレが見えてしまう。本来なら、そこだけ(路肩|路側帯)が消えているタイルを置き直さないといけない。
また、太幅道路に、中幅道路が繋がっている場合は、中幅道路側に横断歩道を描画したい。そういう箇所には横断歩道があるはずだろうと。
さらに、斜めの太幅道路に中幅道路が繋がっている場合は、ちゃんと隙間を埋めるような見た目になるタイルを置きたい。
つまるところ、以下のようなタイルの置き換えが必要になりそうなのだけど…。
この手の組み合わせが、これだけで済むとは思えないわけで…。また、この後、細い幅の道路も追加したいと思っているので、そうなると組み合わせ数が更に膨大になる…。
そもそも、道路1マス分に(路肩|路側帯)まで含めたドット絵を描いてしまったあたりがマズかったのではないかと思えてきた。道路の周辺に歩道相当のタイルを配置して、(路肩|路側帯)はそのタイルに含めたほうが処理が楽になったのではないか…。ちょっとドットを打ち直して考え直してみたい…。今の方法のままでは、どうも終わりが見えないし…。
また、太幅道路に、中幅道路が繋がっている場合は、中幅道路側に横断歩道を描画したい。そういう箇所には横断歩道があるはずだろうと。
さらに、斜めの太幅道路に中幅道路が繋がっている場合は、ちゃんと隙間を埋めるような見た目になるタイルを置きたい。
つまるところ、以下のようなタイルの置き換えが必要になりそうなのだけど…。
この手の組み合わせが、これだけで済むとは思えないわけで…。また、この後、細い幅の道路も追加したいと思っているので、そうなると組み合わせ数が更に膨大になる…。
そもそも、道路1マス分に(路肩|路側帯)まで含めたドット絵を描いてしまったあたりがマズかったのではないかと思えてきた。道路の周辺に歩道相当のタイルを配置して、(路肩|路側帯)はそのタイルに含めたほうが処理が楽になったのではないか…。ちょっとドットを打ち直して考え直してみたい…。今の方法のままでは、どうも終わりが見えないし…。
[   ツッコむ ]
#2 [python] Python + PIL で画像を分割したり比較したりしてみたり
 Python + PIL (Pillow) を使って、画像を分割したり、分割した画像群を比較してユニークな画像を抜き出したりする処理を試してみた。環境は、Windows10 x64 21H1 + Python 3.9.7 64bit + Pillow 8.3.2。
画像を分割する処理は、以下のページが参考になった。ありがたや。
_[Python3.6]画像を任意の枚数に分割する - Qiita
.crop(x, y, x + w, y + h) を使えばいいらしい。
画像群の比較は、NumPy とやらを使うと楽にできそうと知った。
_Python, NumPyで画像処理(読み込み、演算、保存) | note.nkmk.me
_numpy - 配列同士を比較する関数について - pystyle
im = numpy.array(Image.open('hoge.png')) で、ndarray なる配列(?)が得られるらしい。また、pil_img = Image.fromarray(im) で、PIL の画像形式に変換できて、pil_img.save("fuga.png") で保存できる模様。
ndarray 同士の比較は、numpy.array_equal(a1, a2) が使えそうだなと…。これで、寸分たがわず同じ画像かどうかを判定することができるので、リストに登録した画像群と比較して、同じ画像だったら除去、同じ画像ではなければリストに追加、という処理をして、ユニークな画像群だけを得ることができた。
できたスクリプトは以下のような感じ。大きいサイズ(今回は1024x768ドット)の画像群を読み込んで、64x64ドット単位で分割して、ユニークな画像だけを抜き出して、64x64ドットの画像群として保存する、という処理をしている。
_10_split_pattern.py
 画像を分割する処理は、以下のページが参考になった。ありがたや。
_[Python3.6]画像を任意の枚数に分割する - Qiita
.crop(x, y, x + w, y + h) を使えばいいらしい。
画像群の比較は、NumPy とやらを使うと楽にできそうと知った。
_Python, NumPyで画像処理(読み込み、演算、保存) | note.nkmk.me
_numpy - 配列同士を比較する関数について - pystyle
im = numpy.array(Image.open('hoge.png')) で、ndarray なる配列(?)が得られるらしい。また、pil_img = Image.fromarray(im) で、PIL の画像形式に変換できて、pil_img.save("fuga.png") で保存できる模様。
ndarray 同士の比較は、numpy.array_equal(a1, a2) が使えそうだなと…。これで、寸分たがわず同じ画像かどうかを判定することができるので、リストに登録した画像群と比較して、同じ画像だったら除去、同じ画像ではなければリストに追加、という処理をして、ユニークな画像群だけを得ることができた。
できたスクリプトは以下のような感じ。大きいサイズ(今回は1024x768ドット)の画像群を読み込んで、64x64ドット単位で分割して、ユニークな画像だけを抜き出して、64x64ドットの画像群として保存する、という処理をしている。
_10_split_pattern.py
import glob
from PIL import Image, ImageFilter
import numpy
infiles = "../output_images/*.png"
bw, bh = 64, 64
def main():
    l = glob.glob(infiles)
    imgs = []
    for s in l:
        im = Image.open(s)
        iw, ih = im.size
        print("%s, w x h = %d x %d" % (s, iw, ih))
        for y in range(0, ih, bh):
            for x in range(0, iw, bw):
                c = im.crop((x, y, x + bw, y + bh))
                n = numpy.array(c)
                imgs.append(n)
    nimgs = []
    for n in imgs:
        for o in nimgs:
            if numpy.array_equal(n, o):
                break
        else:
            nimgs.append(n)
    cnt = 0
    for n in nimgs:
        fn = "pat_%06d.png" % cnt
        im = Image.fromarray(n)
        im.save(fn)
        cnt += 1
if __name__ == '__main__':
    main()
[   ツッコむ ]
以上、1 日分です。
