見出し画像

アセンの全パーツが写った画像を楽に作成する(Python & openCV)

要旨

複数のスクリーンショットから、アセンのパーツが全て写った一枚の画像を自動で作成します。

C21のアセン画面は、スクロールしなければ全パーツが写りません。SNSでアセンを共有したいときや、アセンを保存したいときなどには不便です。そこで、複数のスクリーンショットをフォルダに入れると、一枚の画像にできるプログラムを作成しました。

Webアプリ

公開していたソースコードをWebアプリ化しました。スクリーンショットをドラッグアンドドロップすると、画像が結合されます。スクリーンショットの撮影方法は後の節を参照してください。

https://c21tools-ss-joint.streamlitapp.com/

※2022/11/03にURLが上記に変更になりました。
旧URLは下記です。(アクセス不可)
https://c21tools-ss-joint-main-28v0gk.streamlitapp.com

スクリーンショットの撮影

今回作成したプログラムは解像度800x600用ですので、C21のゲーム画面の解像度を800x600に変更します。
ガレージ内のアセン画面で、スクロールしながら全パーツが写るように、スクリーンショットを撮影します。
1枚目はスクロールバーが一番上の状態で撮影します。
2枚目は、1枚目の一番下のパーツが、2枚目の一番上のパーツとして写るように撮影します。同様に、3枚目、4枚目・・・と撮影します。
スクリーンショット間で被る部分はプログラムで削除するので、上記のことを守っていれば細かいスクロール位置は気にしなくて構いません。
また、最後のスクリーンショットだけは、スクロールの関係で上記の関係になりませんが、そのまま撮影します。

スクリーンショットの撮影方法

ローカルで実行する場合

Python環境のインストール

PythonとopenCVのライブラリをインストールしておきます。
下記のサイトが参考になると思います。



プログラムの準備

下記のようにファイル、フォルダを構成します。
スクリーンショット名は撮影したファイル名そのままでOKです。ただし、ファイル名の昇順で読み込みますので、アセンの上から順番にスクリーンショットを撮影してください。

(任意のフォルダ)
┣ main.py
┗ SS ┳(1枚目のスクリーンショット)
     ┣(2枚目のスクリーンショット)
     ┣(・・・・)

main.pyはプログラム本体です。ソースコードは下記です。
(2022.06.11 ステータス画面を結合した画像も出力するようにしました。)

import cv2
import glob
import numpy as np

files_img = sorted(glob.glob('./SS/*')) #SSフォルダから画像を読み込む
files_template = np.roll(files_img,-1#テンプレートマッチングで次の画像を参照するために、一つずらした配列を用意する。

frame = cv2.imread(files_img[-1]) #ヘッダー、フッターを切り出すために、最後のスクリーンショットを読む
header = frame[71:90,18:211#ヘッダー
footer = frame[90:408,18:211#一番下の画像+フッター

result = header #結果を格納する配列を作成&ヘッダーを格納する。

#テンプレートマッチングを行い、各スクリーンショットの被る部分を削除して結合する。
for file_img,file_template in zip(files_img[:-1],files_template[:-1]):
    img = cv2.imread(file_img)
    img = img[90:297,18:211]
    template = cv2.imread(file_template)
    template = template[90:100,18:211]
    match = cv2.matchTemplate(img,template,cv2.TM_CCOEFF_NORMED)
    minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(match)
    img = img[0:maxLoc[1],:]
    result = cv2.vconcat([result,img])

result = cv2.vconcat([result,footer]) #結果を格納する配列にフッターを格納する

cv2.imwrite('result.png', result) #画像ファイルを出力する

status = frame[71:408,234:790] #ステータス画面
status = cv2.copyMakeBorder(status, 0, result.shape[0]-status.shape[0], 0, 0, cv2.BORDER_CONSTANT, value=(0,0,0))  #resultと高さを合わせる

result_status = cv2.hconcat([result,status]) #resultとstatusを水平方向に結合する

cv2.imwrite('result_with_status.png', result_status) #画像ファイルを出力する

実行

main.pyを実行すると、同階層に画像が2つ生成されます。
result.png・・・パーツツリーを結合した画像
result_with_status.png・・・パーツツリーとステータスを結合した画像

result.png
result_with_status.png

プログラムの説明

概要はコメントに記載していますが、詳細は別の記事で記載します。(後日執筆予定)

最後に

  • テンプレートマッチングを使用しているため、同じパーツが連続して続く場合は誤認識する可能性があります。その場合は、テンプレート画像の大きさを調整すれば正常に動作すると思います。ご報告ください。

この記事が気に入ったらサポートをしてみませんか?