« タイミングって重要よね | トップページ | パチンコ業界について思うこと »

2017年10月27日 (金)PCにも作業してもらおう

雨続きで外に出るのが億劫になります

晴耕雨読とは田園で世間の煩わしさから離れて心穏やかに暮らすことだそうですが、雨の日はコードを読んで過ごすのも悪くないかもしれません。
おはようございます、本日のブログ当番M.Iです

先日CEDECに行ってきましたが、自動化についてのセッションが増え、効率化がゲーム開発に求められるように感じてきました。
効率化の考え方の一種、GUIでの自動化について話します。

CUIのプログラムなら自動的に走らせるのは難しくないことが多いです。
例えばpowershellなら
ls -r | Select-String "error"
とすればカレントディレクトリを再帰的に回り、ファイルを読みとり何行目にerrorという文字列があるかを出力してくれます。
これをjenkinsにさせれば指定した時間に自動的にログを調べ、errorの文字を検索するというような動作ができます。

しかし、最近のゲームエンジンはUnity然り、UE4然りGUIで操作します。
パッケージ作成等はCUIでも操作できるので自動化が楽ですが、自動配置というようなことをしたい場合は自動化が難しいかと思います。
プラグインで実装ということもできますが、APIの構造など、内部をよく知っておかないと実装ができないです。

そこで画像認識とマウス自動操作を実装し、自動化を実現してみます。
コンピュータに画像にある物体を認識させるのは、コンピュータビジョンという研究分野なのですが、うれしいことに人類の研究の成果がOpenCVというライブラリに還元されていて、手軽に利用できます。
CVはComputer Visionの略なのでコンピュータビジョン専門のライブラリです。
映っている物体を学習して分類する機能もあり、機械学習といった分野とも関係するので、サポートベクターマシン等の幅広いアルゴリズムも備えています。

コンピュータビジョンで実装する利点として、ソフトのAPIに依存しないプログラムが書けることが挙げられます。
反面似たような画像が画面上に現れることで、フローがおかしくなったり、誤って別の箇所をクリックするといった事故が起こることもありえます。
プラグインにも言えることですが、最悪アセットが削除されたり、変な状態で上書き保存されることも考えられます。
どちらにしても、こまめにバージョン管理しておけば大体の場合リカバリできます。

前置きが長くなりましたが実装していきます。
C++から利用すれば最新の手法が試用できるのですが、古典的なテンプレートマッチングという手法で実装するので、C#版の方で実装します。
テンプレートマッチングとは探したい物体の映った画像を作り、その画像と一致するところを探すという手法です。
違う物体を目的のものと認識してしまうことがほとんどないのですが、画像の向きがちょっとでも変わったり、少しでも形が違うと認識されなくなってしまいます。
3D空間に浮いていたり、回転するUIを認識させたいわけではないのならこの手法で充分です。
[ LINK ] https://github.com/shimat/opencvsharp
からソースをビルドすることもできますし、nugetから自分のプロジェクトに組み込んでもいいです。

まずは雰囲気をつかむため、画像からテンプレート画像を検出してみます。
コード中の<>は半角に直してください。

string srcPath = "src.png";
string templatePath = "template.png";
// 画像を指定パスからグレースケールに変換してロードを行う
Mat src = new Mat( srcPath , ImreadModes.GrayScale);
// 検出させたい物体が映ったテンプレート画像をグレースケールに変換してロードを行う
Mat template = new Mat(templatePath , ImreadModes.GrayScale);
// 4バイトの結果格納用の配列を作成 
Mat result = new Mat(src.Rows - template.Rows + 1, src.Cols - template.Cols + 1, MatType.CV_32FC1);
// テンプレートマッチングし、結果をresultに入れる。テンプレート画像と似ているほど1に近付く。
Cv2.MatchTemplate(src, template, result, TemplateMatchModes.CCoeffNormed);
// 配列の閾値以下の要素を0にする つまり0.95未満の類似度の個所を類似度0にする
Cv2.Threshold(result, result, 0.95, 1.0, ThresholdTypes.Tozero);
List < Point > detectedLocations = new List < Point > ();

for (int x = 0; x < result.Rows; x++)
{
	for (int y = 0; y < result.Cols; y++)
	{
		// 配列から類似度0以上の時の座標のみ追加
		if (result.At < float > (x, y) > 0)
		{
			detectedLocations.Add( new Point(x,y) );
		}

	}
}

テンプレートマッチングはテンプレート画像を画像の上で縦横にずらし、一致するピクセル数を数えるイメージです。
画像からはみ出た個所を検出しても意味がないので、resultはテンプレート画像分引いたサイズになっています。

認識はこれで試せました。
PCの画面をキャプチャし、OpenCVに渡す必要があります。
OpenCVにカメラ出力をもらう関数があります。
SCFH DSFという画面キャプチャソフトの出力をとれるのでそれを利用します。

Mat template = new Mat(templatePath, ImreadModes.GrayScale);
// カメラからキャプチャ
VideoCapture capture = new VideoCapture(0);
// キャプチャ画像を出力
Mat srcColor = capture.RetrieveMat();
Mat src = new Mat();
// カメラ画像をグレースケールに変換
Cv2.CvtColor(srcColor, src, ColorConversionCodes.RGB2GRAY);
Mat result = new Mat(src.Rows - template.Rows + 1, src.Cols - template.Cols + 1, MatType.CV_32FC1);
// 後はテンプレートマッチングと同じ

最後にマウス入力を行います。
[ LINK ] http://whoopsidaisies.hatenablog.com/entry/2013/03/22/142031
[1]
を参考にWindowsAPIを使用し、指定個所をクリックさせるようにします。

class WinAPI
{
	[DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)]
	static extern void SetCursorPos(int X, int Y);

	[DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)]
	static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
	private const int MOUSEEVENTF_LEFTDOWN = 0x2;
	private const int MOUSEEVENTF_LEFTUP = 0x4;
	public static void Click(int x, int y)
	{
		SetCursorPos(x, y);
		mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
		mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
	}
}

テンプレートマッチング後、detectedLocationsに見つかった個所が入っているので、そこでWinAPI.Clickを呼んでやればクリックしてくれます。
これで画面から物体を認識し、クリックまで実装できました。

jenkinsのworkspaceフォルダにテンプレート画像とビルドした実行ファイルを入れておけば、「Windowsバッチコマンドの実行」の機能で自動操作に組み込めます。

マウスクリック以外にもキーボード入力といった任意の操作が自動化できるので、アイデア次第で様々な仕事ができるようになるかもしれません。

参考文献
[1] C#で指定した位置をクリックする , whoopsidaisies ,
[ LINK ] http://whoopsidaisies.hatenablog.com/entry/2013/03/22/142031 (参照 2017/10/26)

follow us in feedly
result = encodeURIComponent( "http://www.accessgames-blog.com/blog/2017/10/pc-8047.html" );document.write( "result = " , result );&media=https%3A%2F%2Ffarm8.staticflickr.com%2F7027%2F6851755809_df5b2051c9_z.jpg&description=Next%20stop%3A%20Pinterest">

| | コメント (0) | トラックバック (0)

« タイミングって重要よね | トップページ | パチンコ業界について思うこと »

プログラマー」カテゴリの記事

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: PCにも作業してもらおう:

« タイミングって重要よね | トップページ | パチンコ業界について思うこと »