Ein Bild in einem anderen finden/erkennen [gelöst]

  • VB.NET

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von MA-Maddin.

    Ein Bild in einem anderen finden/erkennen [gelöst]

    Hallo,

    Ich brauche für mein Programm eine Bilderkennungsfunktion. Dabei sollen nicht 2 Bilder verglichen, sondern das 1. Bild im 2. erkannt werden.

    Hier ein Beispiel:

    Dieses Bild (erstes)


    soll in diesem Bild (zweites) erkannt werden


    Das zweite Bild kann dabei immer anders aussehen (die Position von Bild 1 in Bild 2 ist anders).

    Ich habe schon nach Codes im Inet gesucht und auch hier im Forum. Dabei habe ich hier diesen Beitrag gefunden. Mit dem Code wird aber nur Pixel1 von Bild1 mit Pixel1 von Bild2 usw. verglichen. Ich bräuchte aber einen Code der eine "Pixelfolge" in einem anderen Bild sucht.

    Hoffe, ihr könnt mir helfen :S
    Gruß
    Maddin

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „MA-Maddin“ ()

    Wie ich oben schon sagte, habe ich die SuFu verwendet. Nur leider war bei den Ergebnissen nicht das Thema dabei.
    Trotzdem Danke, dass du die SuFu für mich nocheinmal verwendet und gefunden hast, wonach ich suchte. :wacko:
    Bei mir geht er....

    VB.NET-Quellcode

    1. Public NotInheritable Class ImageScanner
    2. Public Shared Function FindBitmap(ByVal Haystack As Bitmap, ByVal Needle As Bitmap, Optional ByVal ShowDebug As Boolean = False) As Rectangle()
    3. Dim HaystackStride As Integer = 0
    4. Dim HaystackBPP As Integer = 0
    5. Dim HaystackData As String = BitmapToString(Haystack, New Rectangle(0, 0, Haystack.Width, Haystack.Height), HaystackStride, HaystackBPP)
    6. Dim NeedleStride As Integer = 0
    7. Dim NeedleBPP As Integer = 0
    8. Dim NeedleData As String = BitmapToString(Needle, New Rectangle(0, 0, Needle.Width, Needle.Height), NeedleStride, NeedleBPP)
    9. Dim ReturnList As New List(Of Rectangle)
    10. For Y As Integer = 0 To Haystack.Height - Needle.Height
    11. For X As Integer = 0 To Haystack.Width - Needle.Width
    12. If GetPixelFromString(HaystackData, New Point(X, Y), HaystackStride, HaystackBPP).ToArgb <> _
    13. GetPixelFromString(NeedleData, New Point(0, 0), NeedleStride, NeedleBPP).ToArgb Then Continue For
    14. If ShowDebug Then Debug.WriteLine(New Point(X, Y).ToString)
    15. Dim ChildCompare As Image = Haystack.Clone(New Rectangle(X, Y, Needle.Width, Needle.Height), Imaging.PixelFormat.Format24bppRgb)
    16. If BitmapToString(ChildCompare, New Rectangle(0, 0, Needle.Width, Needle.Height), 0, 0) = NeedleData Then
    17. ReturnList.Add(New Rectangle(X, Y, Needle.Width, Needle.Height))
    18. End If
    19. ChildCompare.Dispose()
    20. Next
    21. Next
    22. Return ReturnList.ToArray
    23. End Function
    24. Private Shared Function GetPixelFromString(ByVal S As String, ByVal P As Point, _
    25. ByVal Stride As Long, ByVal BPP As Integer) As Color
    26. Dim StartPos As Long = (Stride * P.Y) + (BPP * P.X)
    27. Dim BChar As Char = S(StartPos + 0)
    28. Dim GChar As Char = S(StartPos + 1)
    29. Dim RChar As Char = S(StartPos + 2)
    30. Return Color.FromArgb(Asc(RChar), Asc(GChar), Asc(BChar))
    31. End Function
    32. Private Shared Function BitmapToString(ByVal Image As Bitmap, ByVal Bounds As Rectangle, ByRef Stride As Integer, ByRef BPP As Integer) As String
    33. Dim BitmapData As Imaging.BitmapData = Image.LockBits(Bounds, Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format24bppRgb)
    34. BPP = (BitmapData.Stride - 1) / BitmapData.Width
    35. Stride = BitmapData.Stride
    36. Dim ImageData As String = Runtime.InteropServices.Marshal.PtrToStringAnsi(BitmapData.Scan0, BitmapData.Stride * BitmapData.Height)
    37. Image.UnlockBits(BitmapData)
    38. Return ImageData
    39. End Function
    40. End Class


    Quelle: sythe.org/showthread.php?t=155248
    Ok, die Seite funtzt jetzt auch bei mir.
    Hab aber noch ne frage: Wie bekomme ich jetzt meine 2 Bilder in diesen Code? Ich seh da nirgendwo ne Stelle, wo 2 Bilder eingelesen werden?!
    Sry, aber ich bin da halt noch nich so der Profi ;)
    Hey,

    ich habe das externe Foren-Thema nur überflogen, bin aber in Post 3 auf folgendes Zitat gestoßen:
    That code is old and slow. I rewrote it and made it about 4-5 times faster.
    Here is a sample that takes a snapshot of the screen, and finds the image:
    Darunter folgt ein überarbeitetes Code-Beispiel, das laut Eigenwerbung, 4-5 mal schneller als der obige Code ist. Und wer im Englischen etwas fit ist, erkennt im Code dann auch schnell, an welcher Stelle er beide Bilder angeben muss:

    VB.NET-Quellcode

    1. Dim Target = My.Resources.photoshop_icon
    2. ' [...] überflüssige Zeilen entfernt
    3. Dim Source = New Bitmap(ScreenBounds.Width, ScreenBounds.Height, Imaging.PixelFormat.Format32bppArgb)

    Quelle: http://www.sythe.org/showpost.php?p=4604761&postcount=3 (Post 3)

    Gruß, Manschula
    Also ich habe das jetzt mal mit dem "schnelleren" Code versucht und habe bei Target mein Bild reingemacht.
    Jetzt kommt aber beim Ausführen folgende Fehlermeldung:
    Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.

    und zwar in dem Abschnitt:

    VB.NET-Quellcode

    1. <System.Runtime.CompilerServices.Extension()> _
    2. Private Function GetBitmapData(ByVal Bitmap As Bitmap, ByVal Area As Rectangle, ByRef BitmapData As Imaging.BitmapData) As Byte()
    3. BitmapData = Bitmap.LockBits(Area, Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format24bppRgb)
    4. Dim Out = New Byte(BitmapData.Height * BitmapData.Stride) {}
    5. ''''''''''''DAS WIRD DABEI MARKIERT '''''''''''''
    6. System.Runtime.InteropServices.Marshal.Copy(BitmapData.Scan0, Out, 0, Out.Length)
    7. '''''''''''''''''''''''''''''''''''''''''''''''''
    8. Bitmap.UnlockBits(BitmapData)
    9. Return Out
    10. End Function


    Dieses Problem ist auch bei dem Typen in dem anderen vb-paradise Post aufgetreten. Dort hat nur leider noch niemand weitergeholfen :S

    edit:
    Hab mal nach der Fehlermeldung gegoogelt und gefunden, dass man die Benutzerkontensteuerung deaktivieren soll. Hab ich gemacht, Fehler nicht behoben.
    Kann es vieleicht sein, dass der Code net funtzt weil ich eine Express Edition von VB habe?

    Hab noch Fehlerbericht von Windows angehängt
    Dateien

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „MA-Maddin“ ()

    ok, also bei mir funtzt der Code jetzt auch....
    Ich hatte für "Source" nichts anderes angegeben ^^
    Aber jetzt sagt er immer "Could not find Bitmap".
    Ich habe 2 PNGs (die ich oben als Beispiel habe) in den Resourcen, das Bild welches gesucht, und dann das wo drin gesucht werden soll.
    Das "Große" Bild habe ich jetzt in "Target" und das Suchbild in "Source". Ist das richtig rum? Andersrum habe ich es auch schon probiert, da kommt aber auch "Coul not find..."

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „MA-Maddin“ ()

    Hey,

    zunächst zu deinen Englisch-Kenntnissen:
    • engl. target = Ziel
    • engl. source = Quelle
    Ergo musst du unter "Source" das große Bild einstellen und unter "Target" das Bild, nach dem gesucht werden soll. Bei mir funktioniert es übrigens mit den zwei Bsp-Bildern oben. Du hast vermutlich noch einen Fehler drin...

    Gruß, Manschula
    Ja, danke. Aber dass "Target" Ziel heißt und "Source" Quelle weiß ich schon seit einigen Jahren ^^
    Das hat mir aber nicht wirklich gesagt, wie rum ich die bilder einlesen soll. Aber ich hatte ja, wie gesagt, beides versucht.
    Ich werde dann mal, wenn ich @home bin, mein Projekt zum anschauen hier rein stellen.
    So so, du weißt also was die Begriffe bedeuten:

    MA-Maddin schrieb:

    Das "Große" Bild habe ich jetzt in "Target" und das Suchbild in "Source". Ist das richtig rum?
    Und warum fragst du dann? 8| Entscheide dich mal bitte, was genau du sagen willst...

    MA-Maddin schrieb:

    wie rum ich die bilder einlesen soll.
    ...weil einlesen musst du doch die Bilder nicht; sind sind doch, laut deiner Aussage weiter oben, als Ressourcen in deinem Programm. Ergo musst du nix einlesen, sondern nur angeben.

    Aber ich glaube es ist wirklich das beste, wenn du mal dein Projekt hoch-lädst.

    Gruß, manschula

    Manschula schrieb:

    So so, du weißt also was die Begriffe bedeuten:

    MA-Maddin schrieb:

    Das "Große" Bild habe ich jetzt in "Target" und das Suchbild in "Source". Ist das richtig rum?
    Und warum fragst du dann?

    Weil es für mich nicht logisch war, dass "Ziel" das Bild ist, nach dem gesucht werden soll und "Quelle" das Bild, IN dem gesucht werden soll.
    Ok, wenn ich mir den Code lange genug angesehen hätte, wär ich auch iwann drauf gekommen...
    Is jetzt aber mal egal. Will mich darum jetzt net streiten :|

    Das Projekt ist jetzt im Anhang (sry, müssen 2 parts sein wegen 100kb Grenze)
    Dateien
    Hey,

    naja die Bezeichnungen "target" und "source" sind in diesem Kontext wohl eindeutig... aber ist jetzt ja auch egal ;)

    In deinem Quellcode verwendest du, entgegen dem Ursprungs-Quellcode, als Source nicht ein Desktop-Screenshot, sondern das Bild "Source.png" aus deinen Ressourcen. Warum aber hast du den Screenshot-Abschnitt dann nicht auch aus deinem Quelltext entfernt?

    VB.NET-Quellcode

    1. Private Sub FindBitmap() Handles Button1.Click
    2. Dim Target = My.Resources.Target
    3. ' // löschen -> Dim ScreenBounds = Screen.PrimaryScreen.Bounds
    4. Dim Source = My.Resources.Source
    5. ' // löschen -> Using Gfx = Graphics.FromImage(Source)
    6. ' // löschen -> Gfx.CopyFromScreen(Nothing, Nothing, ScreenBounds.Size)
    7. ' // löschen -> End Using
    8. Dim Result = Source.FindBitmap(Target)
    9. MsgBox(If(Result.HasValue, "Found bitmap at: " & Result.Value.ToString, "Could not find bitmap."))
    10. End Sub

    Dann müsste es funktionieren. In Zukunft vielleicht nicht nur Copy&Paste anwenden, sondern auch versuchen, den Code zu verstehen. Dann stolpert man in der Regel auch nicht über solche Fehler...

    Außerdem würde ich den Code-Teil

    VB.NET-Quellcode

    1. Public Module ImageScanner
    2. '[...]

    nicht in Form1 schreiben, sondern, wie auch eigentlich vorgesehen, in ein Modul auslagern (Projekt > Neues Element hinzufügen > Modul).

    Gruß, Manschula

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

    Jetzt funktioniert es. Vielen Dank! :thumbup:
    copy&paste ist dann manchma auch bei mir ein Problem :whistling:
    Und den Modulcode hatte ich mit in dem Form um evtl. die Dateigröße wegen dem Upload hier zu reduzieren ;)

    Doch hab noch of Frage: wie kann ich jetzt ein externes Bild als Source nehmen?
    Wenn ich es so mache:

    VB.NET-Quellcode

    1. Dim image1 = New Bitmap("bild.bmp")
    2. Dim Source = image1

    Dann kommt immer die Fehlermeldung "es wurde versucht, im geschützten..." (siehe oben).

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „MA-Maddin“ ()