Reactなどの仮想DOMで書かれているサイトやゲームなどをマクロで操作していく場合、画像認識(テンプレートマッチング)を使用したい場面は非常に多いです。
単純なHTMLで書かれているものであれば、SeleniumのWebDriverをVBAから呼び出して対応していけばよいです。
しかしボタン名などが不明な場合、
- ボタンが画面上に出現しているか確認する
- (ボタンが出現していなければ待機、または操作する)
- そのボタンをクリックする
というプロセスで操作をしていく必要があります。上記1の判断で必要になるのが画像認識です。
VBAはExcel用のライブラリなので、その辺は外部参照に頼っていく必要があります。
何故VBAで作る必要があるのか?
「C#やJavaで作ったほうが良くない?」という意見は当然あると思います。
なんでVBAでブラウザ操作や画像認識までやる必要があるのか?
それは当ブログがVBAブログだからです。
例えば、自分はJsonのデータを取得して、それをExcelに転記する既存マクロを持っていました。
ここで新しくJavaでツールを作成してしまうと、Jsonの取得処理はVBA、ブラウザマクロはJavaという構成になってしまいツールの管理が面倒です。
未だにVBAからブラウザを操作しているようなシステムは、こんな感じの理由が大半だと思います。(そもそもIE以外操作しづらいし…)
もし新規でツールを作るつもりなら、UWSC + ChkImgXが一番いいかと思います。(もし特定PCだけの利用ならUWSCのみでも可)
参考:
UWSCであいまい画像検出する方法と、入手方法など
https://qiita.com/hirohiro77/items/2969db0494d1a2056e87
まずは画像認識用のDLLを作成する必要がある
VBAからテンプレートマッチングを行うために、まずはOpenCVを使用して画像認識用のDLLを作成します。
自分は下記のサイトに記載しているソースをほぼ利用させてもらい、DLL化しました。
AutoIt+OpenCVでデスクトップから任意の画像を曖昧検索(テンプレートマッチング)する
上記のサイトでは、AutoItより作成したDLLを呼び出していますが、この部分を単純にVBAから呼び出すようにすればいいだけです。(次項で記載)
OpenCVのインストールは滅茶苦茶面倒なので、1から解説があるのは非常に有り難いですね。
ちなみにthresholdは一致させたい画像によっては変更したほうがいいため、searchImg関数の引数に渡してあげるようにしたほうが良いかもしれません(これもTodoで記載してくれています。)
ソースの作成が終わったらVisualStudioでリリースビルドをします。
自分はあまりVisualStudioに詳しくなかったため、32bitアプリを作成してしまい動かず…。
原因の調査に大幅に時間を使用してしまいました。もし同じようにうまくいかない人がいたら、その辺を確認してみるのが良いかと思います。
画像認識用のファイルを配置し、教師画像も用意
VBAからDLLを呼び出す方法ですが、まず配置場所は以下の2パターンがあります。
- 動かしたいExcelファイルと同じフォルダ
- C:\Windows\System32配下
自分は、わかりやすいようにExcelファイルと同じフォルダに配置を行いました。
配置するファイルは上記サイトでコンパイルすると出来る以下のDLLです。
- opencv_ccalib342.dll
- opencv_core342.dll
- opencv_highgui342.dll
- opencv_imgcodecs342.dll
- opencv_imgproc342.dll
次にテンプレートマッチングで使用する教師画像を作成します。
これはBMPでスクリーンショットを取るほうが良いです。(圧縮されるとマッチングしにくくなるため)
VBAからDLLを呼び出す
Private Declare PtrSafe Functionを使用してsearchImgを呼び出します。
この時に気をつけたい点ですが

元記事のソース内にある型のうち、ポインタがついているものはVBA側でByRefで宣言をして上げる必要があります。
このsearchImgを呼び出すと
- returnが1の場合は一致あり
- returnが0の場合は一致なし
となり、pDetectedPosXとpDetectedPosYに画像の座標が格納されるので、必要があればこの座標を使用してクリックしてあげればよいだけです。
正直一番躓くところはOPENCVのインストールな気がしますね…。
画像認識を行うVBAのサンプル
上記説明だけだとわかりにくいかもしれないので、実際にDLLを呼び出すプログラムのサンプルが以下になります。
Private Declare PtrSafe Function SetDefaultDllDirectories Lib "kernel32.dll" _
(ByVal DirectoryFlags As Long) As Long
Private Declare PtrSafe Function AddDllDirectory Lib "kernel32.dll" _
(ByVal fileName As String) As LongPtr
Private Declare PtrSafe Function search Lib "imageSearch.dll" _
(ByVal pszFilePath As String, _
ByVal nPosLeft As Long, _
ByVal nPosTop As Long, _
ByVal nDestWidth As Long, _
ByVal nDestHeight As Long, _
ByVal threshold As Single, _
ByRef pDetectedPosX As Long, _
ByRef pDetectedPosY As Long) As Long
Private isDllLoaded As Boolean
Public Sub searchImgTest()
Dim ret As Long
Dim x As Long
Dim y As Long
If Not isDllLoaded Then
'自フォルダのDLLをロード対象にする
Dim b As Byte, p As LongPtr
b = SetDefaultDllDirectories(&H1000)
p = AddDllDirectory(StrConv(ThisWorkbook.Path, vbUnicode))
isDllLoaded = True
End If
'画像検索を実行
'引数1:画像ファイルのパス
'引数2:検索範囲の始点(X)
'引数3:検索範囲の始点(Y)
'引数4:検索範囲の終点(X)
'引数5:検索範囲の終点(Y)
'引数6:1~0の値を指定。1に近いほど厳しくマッチングする
'引数7:マッチした画像の座標(X)
'引数8:マッチした画像の座標(Y)
ret = search(ThisWorkbook.Path & "\無題.bmp", 0, 0, 1920, 1080, 0.99, x, y)
Select Case ret
Case 10
MsgBox "画像の指定が不正です"
Case 11
MsgBox "デスクトップの指定が不正です"
Case 12
MsgBox "テンプレートマッチングでエラー"
Case 1
MsgBox "画像が見つかりませんでした"
Case 0
MsgBox "画像が見つかりました" & vbCrLf & "座標は" & CStr(x) & "," & CStr(y)
End Select
End Sub
同じフォルダ内にある”無題.bmp”を読み込んで、画面上に同じ画像が見つかれば画像の座標をメッセージで表示しています。
以下が動かした実績のあるプログラムになります。
解凍したら現れる「画像検索テスト.xlsm」が本体となります。dllのパスが通らないときは、dllをC:¥windows¥system32におけば動くかも…
OpenCVとC++を使って開発しているのですが、実行環境にもランタイム的なものが必要なのか、ちょっと知識がないです。
もし、動かなかったらすみません。
※上記DLLは64Bit版のExcel(≠OSが64Bit)で動くように作成しているので、32Bit版のExcelでは動かないようです。(以下のエラーになります。)
実行時エラー53
ファイルが見つかりません:imageSearch.dll
コメントを残す