VB6.0によるピクセル比較を用いた画像認識機能の実装

大きな画像データ内から特定のテンプレート画像を探し出し、その位置座標を取得する処理は、自動化ツールやRPA(ロボティック・プロセス・オートメーション)の開発において重要な要素です。本記事では、Visual Basic 6.0(VB6.0)とWin32 APIを活用し、画像のピクセルデータを直接メモリに読み込んで比較するアルゴリズムの実装手法について解説します。

従来のDLL作成ツールに依存するのではなく、VB6.0標準の機能とWindows APIであるGetDIBitsを使用して、ビットマップ画像の生データ(Raw Data)をバイト配列へ展開します。その後、サーチ画像とターゲット画像のピクセル値を走査し、類似度に基づいて座標を特定するロジックを構築します。

以下に、メイン画面のボタンクリックイベント等から呼び出されるメイン処理と、実際の画像探索を行う関数の実装例を示します。コードの可読性を高めるため、変数名や構造を再構築し、より汎用的な設計にしています。

' ============================================================
' メイン処理:画像の読み込みと探索関数の呼び出し
' ============================================================
Private Sub btnSearchTarget_Click()
    Dim srcPath As String
    Dim tplPath As String
    Dim srcPic As StdPicture
    Dim tplPic As StdPicture
    Dim srcWidth As Long, srcHeight As Long
    Dim tplWidth As Long, tplHeight As Long
    Dim srcData() As Byte
    Dim tplData() As Byte
    Dim foundPos As POINT_API
    Dim similarityThreshold As Single

    ' 画像パスの設定(環境に合わせて変更してください)
    srcPath = App.Path & "\background.bmp"
    tplPath = App.Path & "\target_icon.bmp"
    similarityThreshold = 0.9 ' 90%以上の類似度で一致とみなす

    ' 画像オブジェクトの読み込み
    Set srcPic = LoadPicture(srcPath)
    Set tplPic = LoadPicture(tplPath)
    
    If srcPic Is Nothing Or tplPic Is Nothing Then
        MsgBox "画像ファイルの読み込みに失敗しました。", vbCritical
        Exit Sub
    End If

    ' 画像サイズの取得(HimetricからPixelへ変換)
    srcWidth = ScaleX(srcPic.Width, vbHimetric, vbPixels)
    srcHeight = ScaleY(srcPic.Height, vbHimetric, vbPixels)
    tplWidth = ScaleX(tplPic.Width, vbHimetric, vbPixels)
    tplHeight = ScaleY(tplPic.Height, vbHimetric, vbPixels)

    ' ビットマップ情報ヘッダーの初期化
    Dim bmiSrc As BITMAPINFO, bmiTpl As BITMAPINFO
    
    With bmiSrc.bmiHeader
        .biSize = Len(bmiSrc.bmiHeader)
        .biWidth = srcWidth
        .biHeight = -srcHeight ' トップダウンDIB
        .biPlanes = 1
        .biBitCount = 32 ' 32bit ARGB形式
        .biCompression = BI_RGB
    End With

    With bmiTpl.bmiHeader
        .biSize = Len(bmiTpl.bmiHeader)
        .biWidth = tplWidth
        .biHeight = -tplHeight
        .biPlanes = 1
        .biBitCount = 32
        .biCompression = BI_RGB
    End With

    ' ピクセルデータ格納用配列の確保
    ' 配列構造: (Blue, Green, Red, Alpha/Reserved, X, Y)
    ReDim srcData(0 To 3, 0 To srcWidth - 1, 0 To srcHeight - 1)
    ReDim tplData(0 To 3, 0 To tplWidth - 1, 0 To tplHeight - 1)

    ' デバイスコンテキストの取得とピクセルデータの取得
    Dim hdcScreen As Long
    hdcScreen = GetDC(0)
    
    Call GetDIBits(hdcScreen, srcPic.Handle, 0, srcHeight, srcData(0, 0, 0), bmiSrc, 0)
    Call GetDIBits(hdcScreen, tplPic.Handle, 0, tplHeight, tplData(0, 0, 0), bmiTpl, 0)
    
    Call ReleaseDC(0, hdcScreen)

    ' 探索処理の実行
    foundPos = LocateImagePattern(srcData, tplData, srcWidth, srcHeight, tplWidth, tplHeight, similarityThreshold)

    ' 結果の表示
    If foundPos.X >= 0 Then
        Me.txtResult.Text = "対象画像を発見しました: X=" & foundPos.X & ", Y=" & foundPos.Y
    Else
        Me.txtResult.Text = "対象画像は見つかりませんでした。"
    End If

    ' オブジェクトの解放
    Set srcPic = Nothing
    Set tplPic = Nothing
End Sub

次に、画像探索の核となるロジックです。この関数は、ソース画像の左上から順にスキャンし、テンプレート画像と比較します。全ピクセルを完全一致でチェックするのではなく、類似度(コサイン類似度や単純な色差の割合)に基づいて判定を行うため、ノイズや微妙な色調の違いに対してある程度の許容を持たせることが可能です。

' ============================================================
' 画像探索ロジック
' 引数:
'   srcBytes() - 検索対象となる大画像のピクセルデータ
'   tplBytes() - 探したい小画像のピクセルデータ
'   sw, sh    - 大画像の幅と高さ
'   tw, th    - 小画像の幅と高さ
'   threshold - 一致判定の閾値 (0.0 ~ 1.0)
' 戻り値:
'   POINT_API - 見つかった座標。見つからない場合は(-1, -1)
' ============================================================
Public Function LocateImagePattern(ByRef srcBytes() As Byte, ByRef tplBytes() As Byte, _
                                  ByVal sw As Long, ByVal sh As Long, _
                                  ByVal tw As Long, ByVal th As Long, _
                                  ByVal threshold As Single) As POINT_API

    Dim x As Long, y As Long
    Dim i As Long, j As Long
    Dim matchCount As Long
    Dim totalCheckPixels As Long
    Dim currentSimilarity As Single
    Dim r1 As Long, g1 As Long, b1 As Long
    Dim r2 As Long, g2 As Long, b2 As Long
    Dim diffSum As Long
    Dim maxDiff As Long
    
    ' 初期化
    LocateImagePattern.X = -1
    LocateImagePattern.Y = -1
    
    ' テンプレートがソースより大きい場合は検索不可
    If tw > sw Or th > sh Then Exit Function

    ' 総ピクセル数(簡易計算用)
    totalCheckPixels = tw * th
    maxDiff = totalCheckPixels * 3 ' RGB各チャンネルの最大差分の合計

    ' ソース画像を走査
    ' 注:境界チェックのため sw - tw, sh - th までループ
    For y = 0 To sh - th
        For x = 0 To sw - tw
            
            diffSum = 0
            matchCount = 0
            
            ' テンプレート画像と比較領域をピクセル単位で比較
            For j = 0 To th - 1
                For i = 0 To tw - 1
                    
                    ' ピクセル値の取得(バイト配列インデックス: 0=Blue, 1=Green, 2=Red)
                    b1 = srcBytes(0, x + i, y + j)
                    g1 = srcBytes(1, x + i, y + j)
                    r1 = srcBytes(2, x + i, y + j)
                    
                    b2 = tplBytes(0, i, j)
                    g2 = tplBytes(1, i, j)
                    r2 = tplBytes(2, i, j)
                    
                    ' 色差の絶対値を累積
                    diffSum = diffSum + Abs(r1 - r2) + Abs(g1 - g2) + Abs(b1 - b2)
                    
                    ' パフォーマンスのため、閾値を超えそうなら早期判定でスキップ
                    ' (簡易的な早期終了ロジック)
                    If j > 0 And i > 0 Then
                        If (diffSum / ((j * tw + i + 1) * 3)) > (1 - threshold) * 255 Then
                            GoTo NextPosition
                        End If
                    End If
                    
                Next i
            Next j
            
            ' 類似度の計算 (差分が少ないほど類似度が高い)
            ' diffSumが0なら完全一致
            Dim avgDiff As Single
            avgDiff = diffSum / (totalCheckPixels * 3)
            currentSimilarity = 1 - (avgDiff / 255)
            
            ' 閾値を超えていれば発見とみなす
            If currentSimilarity >= threshold Then
                LocateImagePattern.X = x
                LocateImagePattern.Y = y
                Exit Function
            End If

NextPosition:
        Next x
    Next y

End Function

上記のコードでは、LocateImagePattern関数内で2重のループ処理を行い、テンプレート画像の各ピクセルと、ソース画像の注目領域のピクセルを比較しています。RGB各色の差分の絶対値を合計し、その平均値から類似度を算出します。threshold(例:0.9)を設定することで、完全一致だけでなく、わずかな画質の劣化やノイズが含まれる場合でも画像を検出することが可能になります。

5月17日 04:39 投稿