[UWSC]あいまいな画像認識の前に“できない理由”を調べる

UWSCという開発ソフトを使って作り上げる作業を実際にやってみるカテゴリ。準備した自作関数や使い方、トラブルを記事にしていきます。得るものがあれば幸いです。
今回は、非常にトラブルの原因になりやすい画像認識、画像検索について整理します。

記事の内容
○画像認識ができない理由を掴む

画像をUWSC自身に取得させる
画像の検索範囲をすぐ見れるよう準備

ポク太郎です。

できるだけ画像認識は使わないのが定石ですが、やむを得ず必要になることがあります。あいまいな画像検索用の関数を作成した方がいて、CHKIMGXとして配布されていますが、筆者はまだ試しておりません。

その前に、標準の画像認識で失敗する理由を確実につかもうと思い、関数を作成しました。

画像認識ができない理由

今、筆者から見えている“画像認識できない”理由は下記です。
※ここではUWSCに画像を比較させるための画像をリファレンス画像と呼んでいます。

1 リファレンス画像と異なるものを検索 
1-1 リファレンス画像をBMPで取得していない

PNGやJPEGなど非可逆圧縮で保存すると元の画像を劣化させてしまうのでBMP(ビットマップ)で保存する必要があります。
1-2 リファレンス画像を保存する際加工されてしまった

AltPrintScreenキーで画像編集ソフトに貼り付けて保存する場合に、そのまま保存したつもりが加工されてしまうものがある。筆者が経験しているのはWin10標準アプリの「Paintペイント」。
1-3 グラボによりγカーブが掛けられ色味が異なる

画像を採取したPCと別環境ではグラボが異なるので、別のγカーブが当てられる。同じベンダ(nVidia、Radeonなど)同士であれば初期状態は同じなので、大抵は認識できるが完全には難しい。
1-4 対象のソフトがアップデートされ異なる表示になってしまった

特にブラウザのように頻繁にアップデートされるソフトではよく遭遇すると思います。
2 検索範囲がずれている
2-1 検索範囲を勘違いしている

処理速度を上げようと画像検索の範囲を絞ろうとした場合などによく陥ります。

そこで、通常の画像認識でどこまで確実にできるかを確認したいと思い、デバッグ機能付きの関数を作成します。

※上記の1-3、1-4は回避できませんが、1-1、1-2、2-1は回避可能になるので追加機能が必要かどうか確かめることができます。曖昧な画像認識が可能なCHKIMGXを使用した場合に1-3、1-4が完全対応できるかどうかは筆者はまだわかっておりません。

スクリーンショット画像をUWSC自身に取得させる

AltPrintScreenキーで画像を取得していると上で挙げた操作ミスなどを招いてしまう&面倒なので、画像を取得する機能を関数として作成しました。

この関数を呼び出すと、選択ボタン2つ(“取得”と“5秒後に取得”)で動作を選択させるダイアログを出します。押されたボタンに従い、ソースコードのあるディレクトリに“画像_000X.bmp”というファイル名で画像を保存します。関数を抜ける条件はダイアログの閉じるボタン(Xボタン)が押されたとき。

※関数の形にしておりますが、独立したアプリにしておいてもいいかもしれません。

画像をUWSC自身に取得させる関数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
FUNCTION get_pic(id)
            cancel=0
WHILE (cancel=0)
      SELECT SLCTBOX(slct_btn,0,1,400,画像を取得します。,取得,5秒後に取得)
      CASE 1
            cancel=1
      CASE 1
            wait=0
      CASE 2
            wait=1
      SELEND
   IF cancel=0
            PRINT 準備しています。
      IF id<>1
         IF STATUS(id,ST_ICON)//最小化状態かどうか
            CTRLWIN(id,NORMAL)//通常表示に
         ENDIF
         IF !STATUS(id,ST_ACTIVE)//非アクティブかどうか
            CTRLWIN(id,ACTIVATE)//アクティブに
         ENDIF
      ELSE
            PRINT アプリが見つからないので全体画像を取得します。
      ENDIF
      IF wait=1
         FOR i=5 TO 1 step 1
            PRINT i+秒後に取得します。
            SLEEP(1)
         NEXT
      ENDIF
            n=0
      WHILE (0=0)
            path=GET_CUR_DIR+\画像_+REPLACE(FORMAT(n,4), ,0)+.bmp
         IF FOPEN(path,F_EXISTS)
            n=n+1
         ELSE
         IF id<>1
            SAVEIMG(path,id)
         ELSE
            SAVEIMG(path)
         ENDIF
            PRINT 画像を取得しました。
            break
         ENDIF
      SLEEP(0.1)
      WEND
   ENDIF
SLEEP(0.05)
WEND
            RESULT=1
FEND
関数プログラム説明
1,50行目1
50
FUNCTION get_pic(id)
FEND
get_pic(id)関数宣言:画像をUWSC自身に取得させる関数。
引数id:アプリIDを渡します。画面全体を取得する場合は-1指定。
2~3,47~48行目2
3
47
48
            cancel=0
WHILE (cancel=0)
SLEEP(0.05)
WEND
変数cancel:下で表示させるダイアログの閉じるボタンが押された場合に1になる変数。
閉じるボタンが押されるまで、4~46行目の間をループします。
4~11行目4
5
6
7
8
9
10
11
      SELECT SLCTBOX(slct_btn,0,1,400,画像を取得します。,取得,5秒後に取得)
      CASE 1
            cancel=1
      CASE 1
            wait=0
      CASE 2
            wait=1
      SELEND
セレクトボックスを表示します。
case -1 ⇒ 閉じるボタンが押された場合の処理
case 1 ⇒ 取得ボタンが押された場合の処理
case 2 ⇒ 5秒後に取得ボタンが押された場合の処理(取得したい状態を準備するための待ち時間として5秒)
変数wait:画像取得までに5秒間の待ち時間を入れるかどうかを記憶します。
12,46行目12
46
   IF cancel=0
   ENDIF
閉じるボタンが押されていない場合のみ、13~45行目を実行。
14~23行目14
15
16
17
18
19
20
21
22
23
      IF id<>1
         IF STATUS(id,ST_ICON)//最小化状態かどうか
            CTRLWIN(id,NORMAL)//通常表示に
         ENDIF
         IF !STATUS(id,ST_ACTIVE)//非アクティブかどうか
            CTRLWIN(id,ACTIVATE)//アクティブに
         ENDIF
      ELSE
            PRINT アプリが見つからないので全体画像を取得します。
      ENDIF
引数idが-1ではないとき(対象が画面全体ではないとき)
ウィンドウが最小化状態なら ⇒ 通常表示にする
ウィンドウが非アクティブ状態なら ⇒ アクティブにする
24~29行目24
25
26
27
28
29
      IF wait=1
         FOR i=5 TO 1 step 1
            PRINT i+秒後に取得します。
            SLEEP(1)
         NEXT
      ENDIF
上記のセレクトボックスで“5秒後に取得”ボタンが押された場合(変数wait1に実行。1秒ごとのカウントダウンを表示していきます。
30~45行目30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
            n=0
      WHILE (0=0)
            path=GET_CUR_DIR+\画像_+REPLACE(FORMAT(n,4), ,0)+.bmp
         IF FOPEN(path,F_EXISTS)
            n=n+1
         ELSE
         IF id<>1
            SAVEIMG(path,id)
         ELSE
            SAVEIMG(path)
         ENDIF
            PRINT 画像を取得しました。
            break
         ENDIF
      SLEEP(0.1)
      WEND
ファイル名を“画像_000x.bmp”として保存していきますが、空きファイル名を調べるためのルーチン。被らないファイル名ができるまでループを回り続けます。保存完了するとループを抜け関数を抜けます。
32行目 :変数pathにファイル名を作り出します。
33行目 :作成したファイル名のものが存在しているかどうかを調べ、
(存在する)
変数nをインクリメントして再度ループ先頭へ
(存在しない)
画像を保存(引数idにより分岐)、ループ抜ける
します。


画像の検索範囲をカーソルで描く

以前こちらで[UWSC]表示された指定画像の中心座標を返す関数で作成した関数にデバッグの引数を追加し、検索範囲の外周に沿ってマウスカーソル移動させ、その動きを見てすぐにわかるように機能追加します。

トラブルがあった際に、呼び出し側のソースをデバッグ用に変更して(デバッグ用引数として1を渡す)すぐに検索範囲を確認できるようにしておきます。

表示された指定画像の中心座標を返す関数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
FUNCTION imgxyd(m,pic,db)
            DIM xy[1]
            wh=bmp(pic)
   IF db=1
            FUKIDASI(SX1+,+SY1,SX1,SY1,0,,,$000000,$00CCEE)
      FOR i=SX1 TO SX2 step (SX2SX1)/100
            MMV(i,SY1)
            SLEEP(0.001)
      NEXT
            FUKIDASI(SX2+,+SY1,SX2,SY1,0,,,$000000,$00CCEE)
      FOR j=SY1 TO SY2 step (SY2SY1)/100
            MMV(SX2,j)
            SLEEP(0.001)
      NEXT
            FUKIDASI(SX2+,+SY2,SX2,SY2,0,,,$000000,$00CCEE)
      FOR i=SX2 TO SX1 step (SX1SX2)/100
            MMV(i,SY2)
            SLEEP(0.001)
      NEXT
            FUKIDASI(SX1+,+SY2,SX1,SY2,0,,,$000000,$00CCEE)
      FOR j=SY2 TO SY1 step (SY1SY2)/100
            MMV(SX1,j)
            SLEEP(0.001)
      NEXT
   ENDIF
      SELECT m
      CASE 1
            haba=IMG_MSK_BGR1
      CASE 2
            haba=IMG_MSK_BGR2
      CASE 3
            haba=IMG_MSK_BGR3
      CASE 4
            haba=IMG_MSK_BGR4
      DEFAULT
            haba=IMG_MSK_BGR4
      SELEND
         IF CHKIMG(pic,,SX1,SY1,SX2,SY2,,haba)
            xy[0]=G_IMG_X+wh[0]/2
            xy[1]=G_IMG_Y+wh[1]/2
            FUKIDASI(xy[0]+,+xy[1],xy[0],xy[1],0,,,$FFFFFF,$008800)
         ELSE
            xy[0]=0
            xy[1]=0
            FUKIDASI(画像が見つかりません。,0,0,0,,,$FFFFFF,$008800)
         ENDIF
            RESULT=SLICE(xy,0,LENGTH(xy)1)
FEND
関数プログラム説明
1,48行目1
48
FUNCTION imgxyd(m,pic,db)
FEND
imgxyd(m,pic,db)関数宣言:表示された指定画像の中心座標を返す関数。(デバッグ機能付)
引数m:色幅を1~4で指定します。
引数pic:リファレンス画像のパスを渡します。
引数db:デバッグのオンオフ指定。デバッグ時には1を渡す。

ご注意】この関数を使用する際には、グローバル変数として以下を宣言、代入しておく必要があります。
PUBLIC SX1,SY1,SX2,SY2//画像検索範囲用のパラメータ

2,3行目2
3
            DIM xy[1]
            wh=bmp(pic)
配列変数の定義とBMP画像の幅と高さを取得。
bmp()関数はこちら[UWSC]BMP画像の幅と高さを得る関数で作成した自作関数。
配列変数xy[1]:画像を見つけた座標。xy[0]がX座標、xy[1]がY座標。
4~25行目4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   IF db=1
            FUKIDASI(SX1+,+SY1,SX1,SY1,0,,,$000000,$00CCEE)
      FOR i=SX1 TO SX2 step (SX2SX1)/100
            MMV(i,SY1)
            SLEEP(0.001)
      NEXT
            FUKIDASI(SX2+,+SY1,SX2,SY1,0,,,$000000,$00CCEE)
      FOR j=SY1 TO SY2 step (SY2SY1)/100
            MMV(SX2,j)
            SLEEP(0.001)
      NEXT
            FUKIDASI(SX2+,+SY2,SX2,SY2,0,,,$000000,$00CCEE)
      FOR i=SX2 TO SX1 step (SX1SX2)/100
            MMV(i,SY2)
            SLEEP(0.001)
      NEXT
            FUKIDASI(SX1+,+SY2,SX1,SY2,0,,,$000000,$00CCEE)
      FOR j=SY2 TO SY1 step (SY1SY2)/100
            MMV(SX1,j)
            SLEEP(0.001)
      NEXT
   ENDIF
引数db=1のときは、マウスカーソルを検索範囲の外周に沿って動かします。四隅で座標を吹き出し表示。

4~9行目 : 上の辺を移動
10~14行目 : 右の辺を移動
15~19行目 : 下の辺を移動
20~24行目 : 左の辺を移動

26~37行目26
27
28
29
30
31
32
33
34
35
36
37
      SELECT m
      CASE 1
            haba=IMG_MSK_BGR1
      CASE 2
            haba=IMG_MSK_BGR2
      CASE 3
            haba=IMG_MSK_BGR3
      CASE 4
            haba=IMG_MSK_BGR4
      DEFAULT
            haba=IMG_MSK_BGR4
      SELEND
引数mに従い、CHKIMG()関数へ渡す“色幅”を指定します。
38~46行目38
39
40
41
42
43
44
45
46
         IF CHKIMG(pic,,SX1,SY1,SX2,SY2,,haba)
            xy[0]=G_IMG_X+wh[0]/2
            xy[1]=G_IMG_Y+wh[1]/2
            FUKIDASI(xy[0]+,+xy[1],xy[0],xy[1],0,,,$FFFFFF,$008800)
         ELSE
            xy[0]=0
            xy[1]=0
            FUKIDASI(画像が見つかりません。,0,0,0,,,$FFFFFF,$008800)
         ENDIF
画像を検索します。
(見つかった場合)
配列変数xy[1]にそれぞれの座標を代入し、見つかった位置に座標を吹き出し表示。
(見つからなかった場合)
座標を(0、0)とし、見つからない由を吹き出し表示します。
47行目47            RESULT=SLICE(xy,0,LENGTH(xy)1)
関数の戻り値としてX座標とY座標の入った配列変数xy[1]を返します。



トラブルがあった場合にはこの関数の引数db1にしてやることですぐ調べられます。

Excel VBAでIEを思いのままに操作できるプログラミング術 Excel 2013/2010/2007/2003対応

WindowsPC倍速・時短テクニックユーザー・ハンドブック (User Hand Book)

記事の内容は伝わりましたでしょうか。
○画像認識ができない理由を掴む

画像をUWSC自身に取得させる
画像の検索範囲をすぐ見れるよう準備

コメント

タイトルとURLをコピーしました