![見出し画像](https://assets.st-note.com/production/uploads/images/80468425/rectangle_large_type_2_9278b57e8795ea6ff6c6c37633b47839.png?width=1200)
アセンの全パーツが写った画像を楽に作成する(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枚目・・・と撮影します。
スクリーンショット間で被る部分はプログラムで削除するので、上記のことを守っていれば細かいスクロール位置は気にしなくて構いません。
また、最後のスクリーンショットだけは、スクロールの関係で上記の関係になりませんが、そのまま撮影します。
![](https://assets.st-note.com/img/1654867133247-kdy7qkVKsF.png?width=1200)
ローカルで実行する場合
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・・・パーツツリーとステータスを結合した画像
![](https://assets.st-note.com/img/1654867180394-PpRw1RkwXn.png)
![](https://assets.st-note.com/img/1654947944034-Ad0nUf9PpL.png?width=1200)
プログラムの説明
概要はコメントに記載していますが、詳細は別の記事で記載します。(後日執筆予定)
最後に
テンプレートマッチングを使用しているため、同じパーツが連続して続く場合は誤認識する可能性があります。その場合は、テンプレート画像の大きさを調整すれば正常に動作すると思います。ご報告ください。