Bild auf dem Bildschirm suchen (Der Index war außerhalb des Arraybereichs)

  • VB.NET

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von Takafusa.

    Bild auf dem Bildschirm suchen (Der Index war außerhalb des Arraybereichs)

    Guten Abend liebe Gemeinde,

    ich habe ein Problem und weiß noch nicht ganz wie ich es euch vermitteln kann, da ich selbst nicht weiß, wo es liegt.
    Ich habe eine Methode gefunden um Bilder auf dem Desktop zu suchen und dessen Koordinaten zu finden. Auf der Arbeit erleichtert das den Workflow ungemein, aber das ist ja nun erstmal egal.

    Folgende Funktion:


    VB.NET-Quellcode

    1. Private Function FindImageOnScreen(ByVal bmpMatch As Bitmap, ByVal ExactMatch As Boolean) As Rectangle
    2. Dim ScreenBmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
    3. Using g As Graphics = Graphics.FromImage(ScreenBmp)
    4. g.CopyFromScreen(Screen.PrimaryScreen.Bounds.Location, Point.Empty, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy)
    5. End Using
    6. Dim ImgBmd As BitmapData = bmpMatch.LockBits(New Rectangle(0, 0, bmpMatch.Width, bmpMatch.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
    7. Dim ScreenBmd As BitmapData = ScreenBmp.LockBits(Screen.PrimaryScreen.Bounds, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
    8. Dim ImgByts((Math.Abs(ImgBmd.Stride) * bmpMatch.Height) - 1) As Byte
    9. Dim ScreenByts((Math.Abs(ScreenBmd.Stride) * ScreenBmp.Height) - 1) As Byte
    10. Marshal.Copy(ImgBmd.Scan0, ImgByts, 0, ImgByts.Length)
    11. Marshal.Copy(ScreenBmd.Scan0, ScreenByts, 0, ScreenByts.Length)
    12. Dim FoundMatch As Boolean = False
    13. Dim rct As Rectangle = Rectangle.Empty
    14. Dim sindx, iindx As Integer
    15. Dim spc, ipc As Integer
    16. Dim skpx As Integer = CInt((bmpMatch.Width - 1) / 10)
    17. If skpx < 1 Or ExactMatch Then skpx = 1
    18. Dim skpy As Integer = CInt((bmpMatch.Height - 1) / 10)
    19. If skpy < 1 Or ExactMatch Then skpy = 1
    20. For si As Integer = 0 To ScreenByts.Length - 1 Step 3
    21. FoundMatch = True
    22. For iy As Integer = 0 To ImgBmd.Height - 1 Step skpy
    23. For ix As Integer = 0 To ImgBmd.Width - 1 Step skpx
    24. sindx = (iy * ScreenBmd.Stride) + (ix * 3) + si
    25. iindx = (iy * ImgBmd.Stride) + (ix * 3)
    26. spc = Color.FromArgb(ScreenByts(sindx + 2), ScreenByts(sindx + 1), ScreenByts(sindx)).ToArgb
    27. ipc = Color.FromArgb(ImgByts(iindx + 2), ImgByts(iindx + 1), ImgByts(iindx)).ToArgb
    28. If spc <> ipc Then
    29. FoundMatch = False
    30. iy = ImgBmd.Height - 1
    31. ix = ImgBmd.Width - 1
    32. End If
    33. Next
    34. Next
    35. If FoundMatch Then
    36. Dim r As Double = si / (ScreenBmp.Width * 3)
    37. Dim c As Double = ScreenBmp.Width * (r Mod 1)
    38. If r Mod 1 >= 0.5 Then r -= 1
    39. rct.X = CInt(c)
    40. rct.Y = CInt(r)
    41. rct.Width = bmpMatch.Width
    42. rct.Height = bmpMatch.Height
    43. Exit For
    44. End If
    45. Next
    46. bmpMatch.UnlockBits(ImgBmd)
    47. ScreenBmp.UnlockBits(ScreenBmd)
    48. ScreenBmp.Dispose()
    49. Return rct
    50. End Function



    Folgender Aufruf:

    VB.NET-Quellcode

    1. Dim ImgToFind As New Bitmap(My.Computer.FileSystem.CurrentDirectory & "\Images\Test.png")
    2. Dim rect As Rectangle = FindImageOnScreen(ImgToFind, False)
    3. If rect <> Rectangle.Empty Then
    4. MsgBox("Bild gefunden")
    5. Else
    6. MsgBox("Bild nicht gefunden")
    7. End If



    Klappt bei mir BESTENS. Innerhalb von 200ms hat er das Bild, egal wo es auf meinem Bildschirm ist.
    Habe es also meinem Kollegen gesendet, er macht nichts anders, ruft die Funktion auf.. Zack:


    System.IndexOutOfRangeException: Der Index war außerhalb des Arraybereichs.
    bei Test.FindImageOnScreen(Bitmap bmpMatch, Boolean ExactMatch)


    Es ist leider nicht möglich auf seinem PC VS zu installieren, da er dafür keine Rechte hat.
    Ich habe also hinter alle möglichen Zeilen eine Funktion eingebaut um in eine Textdatei zu schreiben:


    VB.NET-Quellcode

    1. Private Function FindImageOnScreen(ByVal bmpMatch As Bitmap, ByVal ExactMatch As Boolean) As Rectangle
    2. logerrors("Geht noch 0")
    3. Dim ScreenBmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
    4. logerrors("Geht noch 1")
    5. Using g As Graphics = Graphics.FromImage(ScreenBmp)
    6. g.CopyFromScreen(Screen.PrimaryScreen.Bounds.Location, Point.Empty, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy)
    7. logerrors("Geht noch 2")
    8. End Using
    9. Dim ImgBmd As BitmapData = bmpMatch.LockBits(New Rectangle(0, 0, bmpMatch.Width, bmpMatch.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
    10. logerrors("Geht noch 3")
    11. Dim ScreenBmd As BitmapData = ScreenBmp.LockBits(Screen.PrimaryScreen.Bounds, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
    12. logerrors("Geht noch 4")
    13. Dim ImgByts((Math.Abs(ImgBmd.Stride) * bmpMatch.Height) - 1) As Byte
    14. logerrors("Geht noch 5")
    15. Dim ScreenByts((Math.Abs(ScreenBmd.Stride) * ScreenBmp.Height) - 1) As Byte
    16. logerrors("Geht noch 6")
    17. Marshal.Copy(ImgBmd.Scan0, ImgByts, 0, ImgByts.Length)
    18. logerrors("Geht noch 7")
    19. Marshal.Copy(ScreenBmd.Scan0, ScreenByts, 0, ScreenByts.Length)
    20. Dim FoundMatch As Boolean = False
    21. logerrors("Geht noch 8")
    22. Dim rct As Rectangle = Rectangle.Empty
    23. logerrors("Geht noch 9")
    24. Dim sindx, iindx As Integer
    25. logerrors("Geht noch 10")
    26. Dim spc, ipc As Integer
    27. logerrors("Geht noch 11")
    28. Dim skpx As Integer = CInt((bmpMatch.Width - 1) / 10)
    29. logerrors("Geht noch 12")
    30. If skpx < 1 Or ExactMatch Then skpx = 1
    31. logerrors("Geht noch 13")
    32. Dim skpy As Integer = CInt((bmpMatch.Height - 1) / 10)
    33. logerrors("Geht noch 14")
    34. If skpy < 1 Or ExactMatch Then skpy = 1
    35. logerrors("Geht noch 15")
    36. For si As Integer = 0 To ScreenByts.Length - 1 Step 3
    37. FoundMatch = True
    38. For iy As Integer = 0 To ImgBmd.Height - 1 Step skpy
    39. For ix As Integer = 0 To ImgBmd.Width - 1 Step skpx
    40. sindx = (iy * ScreenBmd.Stride) + (ix * 3) + si
    41. iindx = (iy * ImgBmd.Stride) + (ix * 3)
    42. spc = Color.FromArgb(ScreenByts(sindx + 2), ScreenByts(sindx + 1), ScreenByts(sindx)).ToArgb
    43. ipc = Color.FromArgb(ImgByts(iindx + 2), ImgByts(iindx + 1), ImgByts(iindx)).ToArgb
    44. If spc <> ipc Then
    45. FoundMatch = False
    46. iy = ImgBmd.Height - 1
    47. ix = ImgBmd.Width - 1
    48. End If
    49. Next
    50. Next
    51. If FoundMatch Then
    52. Dim r As Double = si / (ScreenBmp.Width * 3)
    53. Dim c As Double = ScreenBmp.Width * (r Mod 1)
    54. If r Mod 1 >= 0.5 Then r -= 1
    55. rct.X = CInt(c)
    56. rct.Y = CInt(r)
    57. rct.Width = bmpMatch.Width
    58. rct.Height = bmpMatch.Height
    59. Exit For
    60. End If
    61. Next
    62. logerrors("Geht noch 20")
    63. bmpMatch.UnlockBits(ImgBmd)
    64. ScreenBmp.UnlockBits(ScreenBmd)
    65. ScreenBmp.Dispose()
    66. Return rct
    67. End Function



    Fazit: Er kommt bis "Geht noch 15"..

    Habt ihr eine Idee warum? Er hat nichts anders gemacht als ich. Was kann ich noch zum Debuggen tun, ohne VS zu verwenden?

    Vielen Dank im Voraus..
    Mit freundlichen Grüßen,
    xored

    EDIT: Wenn ich die For-Schleifen in einen Try-Block packe, kommt natürlich kein Fehler mehr, er sagt aber natürlich, er habe den Button nicht gefunden..

    *Topic verschoben*


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Marcus Gräfe“ ()

    So aus dem Bauch:
    statt Step 3 das hier mal versucht?

    For si As Integer = 0 To ScreenByts.Length - 1 Step 1
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen

    xored schrieb:

    VB.NET-Quellcode

    1. Dim skpx As Integer = CInt((bmpMatch.Width - 1) / 10)
    machst Du Integer-Division:

    VB.NET-Quellcode

    1. Dim skpx As Integer = (bmpMatch.Width - 1) \ 10)
    Die vielen logerrors() kannst Du ersatzlos löschen, die verwirren nur.
    Lerne zu debuggen:
    Debuggen, Fehler finden und beseitigen
    Hat @mrMo sogar in seiner Signatur. Danke.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich hatte ja geschrieben, dass VS bei ihm KEINE Option ist, was debuggen auf dem normalen Weg schwierig macht :)
    Ich danke euch, ich teste es am Sonntag.

    Schönes Wochenende!
    xored


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza
    Hey, sorry für die späte Resonanz hier.
    Um das ganze abzuschließen.. Ich kann mir nicht erklären, warum der Code nur bei mir ging. Hatte nun mehrere Testpersonen gehabt, aber bei keinem wurde das Bild dann gefunden.
    Dank euch gabs zwar keinen Fehler mehr, aber er fand das Bild einfach nicht mehr. Schade, weil die Methode halt ca. 80% schneller ist, als die, welche ich jetzt nutze:


    VB.NET-Quellcode

    1. Private Declare Function SetCursorPos Lib "user32" (ByVal x As Integer, ByVal y As Integer) As Integer
    2. Public Sub CaptureScreen()
    3. Using g As Graphics = Graphics.FromImage(bmpScreen)
    4. g.CopyFromScreen(0, 0, 0, 0, bmpScreen.Size)
    5. End Using
    6. Me.Refresh()
    7. End Sub
    8. Public Sub FindImageMethode2(ByVal ImageToFind As String, ByVal MoveMouseAndClick As Boolean)
    9. Private bmpScreen As Bitmap = New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
    10. Public ImageFound As Boolean
    11. Dim bmpTarget As Bitmap = New Bitmap(ImageToFind)
    12. CaptureScreen()
    13. Try
    14. Dim clrScreen, clrTarget, clrUL, clrUR, clrLL, clrLR As Color
    15. Dim Found As Boolean
    16. clrUL = bmpTarget.GetPixel(0, 0)
    17. clrUR = bmpTarget.GetPixel(bmpTarget.Width - 1, 0)
    18. clrLL = bmpTarget.GetPixel(0, bmpTarget.Height - 1)
    19. clrLR = bmpTarget.GetPixel(bmpTarget.Width - 1, bmpTarget.Height - 1)
    20. For x = 0 To bmpScreen.Width - bmpTarget.Width
    21. For y = 0 To bmpScreen.Height - bmpTarget.Height
    22. clrScreen = bmpScreen.GetPixel(x, y)
    23. If clrScreen = clrUL Then
    24. clrScreen = bmpScreen.GetPixel(x + bmpTarget.Width - 1, y)
    25. If clrScreen = clrUR Then
    26. clrScreen = bmpScreen.GetPixel(x, y + bmpTarget.Height - 1)
    27. If clrScreen = clrLL Then
    28. clrScreen = bmpScreen.GetPixel(x + bmpTarget.Width - 1, y + bmpTarget.Height - 1)
    29. If clrScreen = clrLR Then
    30. Dim w1 As Integer
    31. Found = True
    32. If bmpTarget.Width > bmpTarget.Height Then w1 = bmpTarget.Height Else w1 = bmpTarget.Width
    33. For x1 = 1 To w1 - 2 Step 5
    34. clrTarget = bmpTarget.GetPixel(x1, x1)
    35. clrScreen = bmpScreen.GetPixel(x + x1, y + x1)
    36. If clrTarget <> clrScreen Then
    37. Found = False
    38. Exit For
    39. End If
    40. Next
    41. If Found Then
    42. If x = -1 Then
    43. ImageFound = False
    44. Else
    45. ImageFound = True
    46. End If
    47. If (MoveMouseAndClick = True) Then
    48. SetCursorPos(x + 20, y + 20) ' Habe die + 20 jeweils hinzugefügt, weil das bei mir dann passte, sonst sucht er ja die linke obere Ecke. Man kann hier natürlich auch die Mitte berechnen
    49. End If
    50. Exit Sub
    51. End If
    52. End If
    53. End If
    54. End If
    55. End If
    56. Next
    57. Next
    58. Catch ex As Exception
    59. msgbox(ex.ToString)
    60. End Try
    61. ImageFound = False
    62. End Sub


    Ist auch nicht von mir, wer hätte es gedacht, habs lediglich um die Funktion erweitert, dass man dort direkt seine Maus hinbewegen lassen kann.
    Gruß, xored


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Marcus Gräfe“ ()

    @xored Was soll das:

    VB.NET-Quellcode

    1. If clrScreen = clrUL Then
    2. clrScreen = bmpScreen.GetPixel(x + bmpTarget.Width - 1, y)
    3. If clrScreen = clrUR Then
    Wie ist es möglich, dass die Zeile hier 3 erreicht wird?
    Niemals! Deswegen ist er wohl so schnell.
    Bewerte unter diesem Aspekt Deinen Code.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    xored schrieb:

    Es ist leider nicht möglich auf seinem PC VS zu installieren, da er dafür keine Rechte hat.


    In diesem Fall kannste aber trotzdem an die Exception mit Fehlerzeile kommen. Teste das mal:

    VB.NET-Quellcode

    1. Try
    2. Dim i As Integer = Integer.MaxValue
    3. i += 1
    4. Catch ex As Exception
    5. Dim stackTrace As New StackTrace(ex, True)
    6. Dim stackFrame As StackFrame = stackTrace.GetFrame(0)
    7. MessageBox.Show(String.Format("Exception: {1}{0}In File: {2}{0}In Line: {3}", Environment.NewLine, ex.Message, stackFrame.GetFileName(), stackFrame.GetFileLineNumber()))
    8. End Try

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Takafusa“ ()