0

画面上で設定された長方形を検索し、指定した画像と比較して一致するかどうかを確認するにはどうすればよいでしょうか。

x1y1からx2y2を検索して、画像と比較できるとしましょう。ブール値を返しますか?

私は自動を知っています-それはここで見られる同様の機能を持っています:http ://www.autohotkey.com/docs/commands/ImageSearch.htm

誰かがこれを参照できるようにしたことがありますか?私はvb.netを使用しています。

編集:アブディアス、私は代わりにあなたのコードをクラスに入れました、そして私はそれをこのように呼んでいます:

     Dim bm As Bitmap = Bitmap.FromFile(Label1.Text)
    Dim bm2 As Bitmap = Bitmap.FromFile(Label2.Text)
    Dim pnt As Point = ImageFinder.Contains(bm, bm2)
    If pnt <> Nothing Then
        MessageBox.Show("Possible match found at " & pnt.X.ToString() & " " & pnt.Y.ToString())
    Else
        MessageBox.Show("No match.")
    End If

私が試したすべての画像セットは意味を返さないようです。それらは100%お互いを含んでいますが。私は画像を撮り、それを数ピクセルでトリミングしましたが、それでも一致が返されませんでした。ソースが大きいことを確認しました。いくつかの画像を24ビットjpgとしてペイントで保存しようとしましたが、まだ何もありません。

これが2つのサンプル画像です。ここに画像の説明を入力してくださいここに画像の説明を入力してください

4

2 に答える 2

1

より大きな画像の中に画像が存在するかどうかを確認できるこの関数を作成しました。これは拡張機能として記述されていますが、領域をサポートするだけでなく、通常の関数に簡単に変更できます。

それを使用するには:

  • メインイメージを標準にロードしBitmapますbmp
  • 画像を読み込んで検索しますbmpSearch

次に、電話します。

Dim pt as Point = bmp.Contains(bmpSearch)
If pt <> Nothing Then
    '... image found at pt
End If

拡張機能のコード(最適化の余地がありますが、このサイトの別の質問に対する20分の演習として記述されています):

'
'-- Extension for Bitmap
'
<Extension()>
Public Function Contains(src As Bitmap, ByRef bmp As Bitmap) As Point
    '
    '-- Some logic pre-checks
    '
    If src Is Nothing OrElse bmp Is Nothing Then Return Nothing

    If src.Width = bmp.Width AndAlso src.Height = bmp.Height Then
        If src.GetPixel(0, 0) = bmp.GetPixel(0, 0) Then
            Return New Point(0, 0)
        Else
            Return Nothing
        End If
    ElseIf src.Width < bmp.Width OrElse src.Height < bmp.Height Then
        Return Nothing
    End If
    '
    '-- Prepare optimizations
    '
    Dim sr As New Rectangle(0, 0, src.Width, src.Height)
    Dim br As New Rectangle(0, 0, bmp.Width, bmp.Height)

    Dim srcLock As BitmapData = src.LockBits(sr, Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
    Dim bmpLock As BitmapData = bmp.LockBits(br, Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)

    Dim sStride As Integer = srcLock.Stride
    Dim bStride As Integer = bmpLock.Stride

    Dim srcSz As Integer = sStride * src.Height
    Dim bmpSz As Integer = bStride * bmp.Height

    Dim srcBuff(srcSz) As Byte
    Dim bmpBuff(bmpSz) As Byte

    Marshal.Copy(srcLock.Scan0, srcBuff, 0, srcSz)
    Marshal.Copy(bmpLock.Scan0, bmpBuff, 0, bmpSz)

    ' we don't need to lock the image anymore as we have a local copy
    bmp.UnlockBits(bmpLock)
    src.UnlockBits(srcLock)

    Dim x, y, x2, y2, sx, sy, bx, by, sw, sh, bw, bh As Integer
    Dim r, g, b As Byte

    Dim p As Point = Nothing

    bw = bmp.Width
    bh = bmp.Height

    sw = src.Width - bw      ' limit scan to only what we need. the extra corner
    sh = src.Height - bh     ' point we need is taken care of in the loop itself.

    bx = 0 : by = 0
    '
    '-- Scan source for bitmap
    '
    For y = 0 To sh
        sy = y * sStride
        For x = 0 To sw

            sx = sy + x * 3
            '
            '-- Find start point/pixel
            '
            r = srcBuff(sx + 2)
            g = srcBuff(sx + 1)
            b = srcBuff(sx)

            If r = bmpBuff(2) AndAlso g = bmpBuff(1) AndAlso b = bmpBuff(0) Then
                p = New Point(x, y)
                '
                '-- We have a pixel match, check the region
                '
                For y2 = 0 To bh - 1
                    by = y2 * bStride
                    For x2 = 0 To bw - 1
                        bx = by + x2 * 3

                        sy = (y + y2) * sStride
                        sx = sy + (x + x2) * 3

                        r = srcBuff(sx + 2)
                        g = srcBuff(sx + 1)
                        b = srcBuff(sx)

                        If Not (r = bmpBuff(bx + 2) AndAlso
                                g = bmpBuff(bx + 1) AndAlso
                                b = bmpBuff(bx)) Then
                            '
                            '-- Not matching, continue checking
                            '
                            p = Nothing
                            sy = y * sStride
                            Exit For
                        End If

                    Next
                    If p = Nothing Then Exit For
                Next
            End If 'end of region check

            If p <> Nothing Then Exit For
        Next
        If p <> Nothing Then Exit For
    Next

    bmpBuff = Nothing
    srcBuff = Nothing

    Return p

End Function
于 2012-12-14T07:03:41.393 に答える
0

私は少し遊んだことがあり、KMPアルゴリズム(実装は簡単だと思います->ウィキペディアには素晴らしい擬似コードがあります)のようなものを使用することも役立つかもしれないと思いました:

Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Public Class ImageFinder

Public Shared Function Contains(Parent As Bitmap, Child As Bitmap) As Point

    If Parent Is Nothing OrElse Child Is Nothing Then Throw New ArgumentException("Narf!")
    If Parent.PixelFormat <> Imaging.PixelFormat.Format32bppArgb OrElse Child.PixelFormat <> Imaging.PixelFormat.Format32bppArgb Then Throw New ArgumentException("Narf again!")

    If Parent.Width = Child.Width AndAlso Parent.Height = Child.Height AndAlso Parent.GetPixel(0, 0) <> Child.GetPixel(0, 0) Then Return Nothing
    If Child.Width > Parent.Width OrElse Child.Height > Parent.Height Then Return Nothing

    Dim bmdParent, bmdChild As BitmapData
    Try
        ' Get bitmap data into array of int
        bmdParent = Parent.LockBits(New Rectangle(0, 0, Parent.Width, Parent.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
        bmdChild = Child.LockBits(New Rectangle(0, 0, Child.Width, Child.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)

        Dim ParentValuesPerLine As Integer = bmdParent.Stride \ 4
        Dim ChildValuesPerLine As Integer = bmdChild.Stride \ 4

        Dim ParentData((bmdParent.Stride \ 4) * bmdParent.Height - 1) As Integer
        Dim ChildData((bmdChild.Stride \ 4) * bmdChild.Height - 1) As Integer

        Marshal.Copy(bmdParent.Scan0, ParentData, 0, ParentData.Length)
        Marshal.Copy(bmdChild.Scan0, ChildData, 0, ChildData.Length)

        If bmdParent IsNot Nothing Then Parent.UnlockBits(bmdParent)
        bmdParent = Nothing
        If bmdChild IsNot Nothing Then Child.UnlockBits(bmdChild)
        bmdChild = Nothing

        ' Create KMP-Table:
        Dim T(Child.Height - 1)() As Integer
        For i = 0 To Child.Height - 1
            T(i) = KMP_Table(ChildData, i * ChildValuesPerLine, ChildValuesPerLine)
        Next

        Dim line_c As Integer = 0
        Dim line_p As Integer = 0
        Dim found As Boolean

        While line_p <= Parent.Height - Child.Height
            line_c = 0
            Dim childoffset As Integer = line_c * ChildValuesPerLine
            Dim parentoffset As Integer = line_p * ParentValuesPerLine


            Dim m As Integer = -1
            While True
                m = KMP_Search(ParentData, parentoffset, ParentValuesPerLine, m + 1, ChildData, 0, ChildValuesPerLine, T(0))
                If m > -1 Then
                    ' first line found
                    Debug.Print("Possible match at {0},{1}", m, line_p)
                    found = True
                    Dim p = parentoffset + ParentValuesPerLine
                    Dim c = childoffset + ChildValuesPerLine
                    For i = 1 To Child.Height - 1
                        If KMP_Search(ParentData, p, ParentValuesPerLine, m, ChildData, childoffset, ChildValuesPerLine, T(i)) <> m Then
                            ' this line doesnt match
                            found = False
                            Exit For
                        End If
                        p += ParentValuesPerLine
                        c += ChildValuesPerLine
                    Next
                    If found Then
                        Debug.Print("Found match at {0},{1}", m, line_p)
                        Return New Point(m, line_p)
                    End If
                Else
                    Exit While
                End If
            End While

            line_p += 1
        End While

        'Catch ex As Exception
        'Throw
    Finally
        If bmdParent IsNot Nothing Then Parent.UnlockBits(bmdParent)
        If bmdChild IsNot Nothing Then Child.UnlockBits(bmdChild)
    End Try

End Function

Private Shared Function KMP_Search(ByVal S As Integer(), s0 As Integer, slen As Integer, m As Integer,
                                   ByVal W As Integer(), w0 As Integer, wlen As Integer,
                                   tbl() As Integer) As Integer

    Dim i As Integer = 0

    While m + i < slen
        If W(w0 + i) = S(s0 + m + i) Then
            If i = wlen - 1 Then Return m
            i += 1
        Else
            m = m + i - tbl(i)
            If tbl(i) > -1 Then
                i = tbl(i)
            Else
                i = 0
            End If
        End If

    End While

    Return -1


End Function

Private Shared Function KMP_Table(ByRef arr() As Integer, start As Integer, count As Integer) As Integer()

    Dim table(count - 1) As Integer
    table(0) = -1
    table(1) = 0

    Dim pos As Integer = 2
    Dim cnd As Integer = 0

    While pos < count - 1
        If arr(start + pos - 1) = arr(start + cnd) Then
            cnd += 1
            table(pos) = cnd
            pos += 1
        ElseIf cnd > 0 Then
            cnd = table(cnd)
        Else
            table(pos) = 0
            pos += 1
        End If
    End While

    Return table

End Function

End Class

次の「ベンチマーク」関数を使用して、AbdiasSoftwareのコードと比較しました。

Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click

    Dim ofd As New OpenFileDialog
    If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
        Dim bm As Bitmap = Bitmap.FromStream(New MemoryStream(File.ReadAllBytes(ofd.FileName)))
        Dim bm2 As New Bitmap(bm.Width, bm.Height, PixelFormat.Format32bppArgb)
        Dim gr = Graphics.FromImage(bm2)
        gr.DrawImageUnscaled(bm, New Point(0, 0))
        Dim bm3 As New Bitmap(100, 100, PixelFormat.Format32bppArgb)
        gr = Graphics.FromImage(bm3)
        gr.DrawImage(bm2, New Rectangle(0, 0, 100, 100), New Rectangle(bm2.Width - 110, bm2.Height - 110, 100, 100), GraphicsUnit.Pixel)
        PictureBox1.Image = bm3
        Dim res As New List(Of Integer)
        For i = 1 To 10
            Dim stp = Stopwatch.StartNew
            Dim k = ImageFinder.Contains(bm2, bm3)
            stp.Stop()
            res.Add(stp.ElapsedMilliseconds)
        Next
        ListBox1.Items.Add(String.Format("KMP: Image = {3}x{4}, Min = {0}, Max = {1}, Avg = {2}", res.Min, res.Max, res.Average, bm2.Width, bm2.Height))
        res.Clear()
        For i = 1 To 10
            Dim stp = Stopwatch.StartNew
            Dim k = bm2.ContainsSO(bm3)
            stp.Stop()
            res.Add(stp.ElapsedMilliseconds)
        Next
        ListBox1.Items.Add(String.Format("SO: Image = {3}x{4}, Min = {0}, Max = {1}, Avg = {2}", res.Min, res.Max, res.Average, bm2.Width, bm2.Height))
    End If

End Sub

大きい(8MP)と小さい(1MP)の写真(図面やアイコンなどなし)でテストしたところ、kmpバージョンは他のアプローチよりも約2倍高速であることがわかりました。絶対数では、i7-2600でテストされた8MPイメージの75msに対して40msです。KMP(およびその他)がより大きな領域をスキップできる場合に勝つため、結果は画像のタイプによって異なる場合があります。

于 2012-12-14T15:40:21.310 に答える