0

我想知道如何在屏幕上搜索设置的矩形并将其与我指定的图像进行比较以查看它是否匹配?

假设它可以搜索 x1 y1 到 x2 y2 并与图像进行比较?并返回布尔值?

我知道 Auto-it 在这里看到了类似的功能:http ://www.autohotkey.com/docs/commands/ImageSearch.htm

有没有人这样做他们可以参考?我正在使用 vb.net。

编辑: Abdias,我已将您的代码放入一个类中,我这样称呼它:

     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 格式,但仍然没有。

这是两个示例图像。在此处输入图像描述在此处输入图像描述

4

2 回答 2

1

我制作了这个功能,可以查看图像是否存在于更大的图像中。它是作为扩展编写的,但可以轻松修改为正常功能以及支持区域。

要使用它:

  • 将主图像加载到标准Bitmapbmp
  • 加载要查找的图像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 算法的东西(我猜这是一个容易实现的算法 -> wikipedia 有一个很好的伪代码)也可能会有所帮助:

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

我使用以下“基准”函数与Abdias Software的代码进行比较:

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 图像的绝对数字为 40 毫秒与 75 毫秒。结果可能取决于图像的类型,因为 KMP(和其他)赢了,当他们可以跳过更大的区域时。

于 2012-12-14T15:40:21.310 回答