Bilderkennung mit positionsrückgabe (X,y)

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 29 Antworten in diesem Thema. Der letzte Beitrag () ist von nafets.

    Bilderkennung mit positionsrückgabe (X,y)

    Hallo,

    wie einfach ist es realisierbar wenn ich einen Screenshot habe und darin dann einen Bildausschnitt suchen möchte? Am besten mit Positionsrückgebe mit den Koordinaten! Gibt es dafür evtl gut benutzbare Bibliotheken?

    Natürlich könnte ich jetzt eine Doppelschleife und pixel für pixel vergleichen aber es wäre cool wenn ich dazu noch Toleranzen berücksichtige!

    liebe grüße

    *Topic verschoben*
    Liebe Grüße,
    C.O.D.E

    Testautomatisierung (TA):

    5%
    Du findest meinen Beitrag Hilfreich? :thumbup: Dann drück auf Hilfreich!

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

    @C.O.D.E Die Aufgabenstellung ist suboptimal.
    Was genau soll mit diesen Koordinaten passieren?
    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!
    Also ich habe einen Screenshot von meinem Bildschirm, in diesem ist ein bereich den ich gerne ausschneiden möchte. Das Problem ist das ich mich an verschiedene auflösungen anpassen muss und dieser bereich dann nicht immer an der gleichen stelle ist und auch nicht immer gleich groß. Ich muss also die position von 2 Markern finden um das ganze dann auszuschneiden! :)

    lg
    Liebe Grüße,
    C.O.D.E

    Testautomatisierung (TA):

    5%
    Du findest meinen Beitrag Hilfreich? :thumbup: Dann drück auf Hilfreich!
    @C.O.D.E Kannst Du mal den Screenshot posten und dabei die Ecken markieren?
    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!
    Frag (auch) mal @xored, wie weit er mit seinem Thread/Programm gekommen ist.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Habe in meinem Thread mal geantwortet. Hilft dem TE aber wahrscheinlich nicht, weil es bei mir keine Toleranzen gibt, wie C.O.D.E wünscht. Das wäre wirklich noch was interessantes.
    AutoIT bietet ja eine ImageSearch DLL, welche mit Toleranzen arbeitet (kann man einstellen). Aber in .net..


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza

    RodFromGermany schrieb:

    @C.O.D.E Kannst Du mal den Screenshot posten und dabei die Ecken markieren?


    Ich habe die beiden Bilder mal angehangen. Bild Nummer 1 ist der komplette Screenshot und Bild Nummer 2 der Bereich den ich gerne hätte.

    @xored ich gucke mir das mal an! :)
    Dateien
    • 1.bmp

      (6,22 MB, 116 mal heruntergeladen, zuletzt: )
    • 2.bmp

      (1,16 MB, 91 mal heruntergeladen, zuletzt: )
    Liebe Grüße,
    C.O.D.E

    Testautomatisierung (TA):

    5%
    Du findest meinen Beitrag Hilfreich? :thumbup: Dann drück auf Hilfreich!
    @C.O.D.E Kannst Du Dir zunächst das Fenster als solches holen?
    Schau dazu mal hier rein:
    Andere Programme fernsteuern
    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!
    code-ai.mk/image-search/
    Jetzt machst du dir eine Matrix und rennst über das bild :D

    C#-Quellcode

    1. public double CalculateSimilarity(Bitmap bmpImage1, Bitmap bmpImage2)
    2. {
    3. int correct = 0;
    4. for (int i = 0; i < bmpImage1.Width; i++)
    5. {
    6. for (int j = 0; j < bmpImage1.Height; j++)
    7. {
    8. // hier bitte optimieren !!!!!
    9. Color c1 = bmpImage1.GetPixel(i, j);
    10. Color c2 = bmpImage2.GetPixel(i, j);
    11. if (c1.ToArgb() == c2.ToArgb())
    12. correct++;
    13. }
    14. }
    15. int maxPixels = bmpImage1.Width * bmpImage1.Height;
    16. double SimilarityPercent = (100.0 * correct)/maxPixels;
    17. return SimilarityPercent;
    18. }

    Ich hab mich jetzt auch mal dran versucht, für dieses Bild pass mein Code, ist allerdings gurkig.


    Was mir auf dem Bilde sofort aufgefallen ist, sind die beiden hellen Rahmen, so viele Pixel mit dieser Farbrange gibt es nur in 2 Zeilen. Somit lassen sich StartZeile und EndZeile leicht finden. Damit kann man dann auch leicht die Höhe ermitteln. In diesen Beiden Zeilen sind jeweils 2 Linien mit dieser Farbrange, nurnoch start und end positionen der 2. Linie ermitteln und wir haben alles was wir brauchen. Sogar Toleranz ist mit drin.

    Der Code sollte aber noch verbessert werden, da hab ich keine Lust mehr drauf.
    Laufzeit ist auch flott: 00:00:00.1699079

    //hab noch schnell Option Strict On konform gemacht

    VB.NET-Quellcode

    1. Private Function ColorMatch(c As Color) As Boolean
    2. If c.R > 100 AndAlso c.R < 150 Then
    3. If c.G > 195 Then
    4. If c.B > 200 Then
    5. Return True
    6. End If
    7. End If
    8. End If
    9. Return False
    10. End Function
    11. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    12. Dim startTime As DateTime = DateTime.Now
    13. Using bmp As New Bitmap("1.bmp")
    14. Dim bmpData As BitmapData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat)
    15. Dim scan0 As IntPtr = bmpData.Scan0
    16. Dim bytes As Integer = Math.Abs(bmpData.Stride) * bmp.Height
    17. Dim pixelData(bytes - 1) As Byte
    18. Marshal.Copy(scan0, pixelData, 0, bytes)
    19. bmp.UnlockBits(bmpData)
    20. Dim bytesPerLine As Integer = bmp.Width * 3
    21. Dim lineNumbers As New List(Of Integer)
    22. For y = 0 To bmp.Height - 1
    23. Dim matchInLine As Integer = 0
    24. For x = 0 To bmp.Width - 1
    25. Dim pos As Integer = y * bytesPerLine + x * 3
    26. Dim c As Color = Color.FromArgb(pixelData(pos + 2), pixelData(pos + 1), pixelData(pos))
    27. If ColorMatch(c) Then
    28. matchInLine += 1
    29. If matchInLine > 500 Then
    30. If Not lineNumbers.Contains(y) Then
    31. lineNumbers.Add(y)
    32. End If
    33. Exit For
    34. End If
    35. End If
    36. Next
    37. Next
    38. Dim startX As Integer = -1
    39. Dim endX As Integer = -1
    40. If lineNumbers.Count = 2 Then
    41. Dim firstLineStarted As Boolean = False
    42. Dim firstLineEnded As Boolean = False
    43. Dim secondsLineStarted As Boolean = False
    44. For x = 0 To bmp.Width - 1
    45. Dim pos As Integer = lineNumbers(0) * bytesPerLine + x * 3
    46. Dim c As Color = Color.FromArgb(pixelData(pos + 2), pixelData(pos + 1), pixelData(pos))
    47. If ColorMatch(c) Then
    48. If Not firstLineStarted Then
    49. firstLineStarted = True
    50. Debug.WriteLine("1st line start : " & x)
    51. End If
    52. If firstLineStarted AndAlso firstLineEnded Then
    53. If Not secondsLineStarted Then
    54. startX = x
    55. secondsLineStarted = True
    56. Debug.WriteLine("2nd line start : " & x)
    57. End If
    58. End If
    59. Else
    60. If firstLineStarted AndAlso Not firstLineEnded Then
    61. firstLineEnded = True
    62. Debug.WriteLine("1st line end : " & x)
    63. End If
    64. If secondsLineStarted Then
    65. endX = x
    66. Debug.WriteLine("2nd line end : " & x)
    67. Exit For
    68. End If
    69. End If
    70. Next
    71. Dim r As New Rectangle(startX, lineNumbers(0), endX - startX, lineNumbers(1) - lineNumbers(0))
    72. Using bmp2 As New Bitmap(r.Width, r.Height)
    73. Using g As Graphics = Graphics.FromImage(bmp2)
    74. Dim destRect As New Rectangle(0, 0, bmp2.Width, bmp2.Height)
    75. g.DrawImage(bmp, destRect, r, GraphicsUnit.Pixel)
    76. BackgroundImage = CType(bmp2.Clone(), Image)
    77. BackgroundImageLayout = ImageLayout.None
    78. End Using
    79. End Using
    80. End If
    81. Debug.WriteLine((DateTime.Now - startTime).ToString)
    82. End Using
    83. End Sub

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

    Im empfehl die GPU :D (12 Kerne a 12sec mit bpp32)



    Ist zwar etwas overkill aber ...


    How to use:

    C#-Quellcode

    1. using Bitmap screen = (Bitmap) Bitmap.FromFile("Screenshot.png");
    2. using Bitmap toFind = (Bitmap) Bitmap.FromFile("toFind.png");
    3. using MemoryImage screenMI = new MemoryImage(screen);
    4. using MemoryImage toFindMI = new MemoryImage(toFind);
    5. var p = MemoryImageService.FindImage(toFindMI, screenMI);


    Source:
    Spoiler anzeigen

    C#-Quellcode

    1. public class MemoryImageService
    2. {
    3. /// <summary>
    4. ///
    5. /// </summary>
    6. /// <param name="src">image to search for</param>
    7. /// <param name="dest">image for search</param>
    8. /// <returns></returns>
    9. public static Point FindImage(MemoryImage src, MemoryImage dest)
    10. {
    11. int bestPoints = 0;
    12. Point location = Point.Empty;
    13. object locker = new object();
    14. //Parallel.For(0, dest.Height - src.Height, yDest =>
    15. for (int yDest = 0, n = dest.Height - src.Height; yDest < n; yDest++)
    16. {
    17. Parallel.For(0, dest.Width - src.Width, xDest =>
    18. //for(int xDest = 0, n = dest.Width - src.Width; xDest < n; xDest++)
    19. {
    20. unsafe
    21. {
    22. int points = 0;
    23. Pixel* destPtr = dest.Buffer + (yDest * dest.Width) + xDest;
    24. Pixel* srcPtr = src.Buffer;
    25. for (int ySrc = 0; ySrc < src.Height; ySrc++)
    26. {
    27. for (int xSrc = 0; xSrc < src.Width; xSrc++)
    28. {
    29. Pixel destPixle = *(destPtr + xSrc);
    30. Pixel srcPixel = *(srcPtr);
    31. if (destPixle.Value == srcPixel.Value) points += 4;
    32. else
    33. {
    34. if (destPixle.Channel1 == srcPixel.Channel1) points++;
    35. if (destPixle.Channel2 == srcPixel.Channel2) points++;
    36. if (destPixle.Channel3 == srcPixel.Channel3) points++;
    37. if (destPixle.Channel4 == srcPixel.Channel4) points++;
    38. }
    39. srcPtr++;
    40. }
    41. destPtr += dest.Width;
    42. srcPtr++;
    43. }
    44. lock (locker)
    45. {
    46. if (bestPoints < points)
    47. {
    48. bestPoints = points;
    49. location.X = xDest;
    50. location.Y = yDest;
    51. }
    52. }
    53. }
    54. }
    55. );
    56. }
    57. //);
    58. return location;
    59. }
    60. }
    61. public readonly unsafe struct MemoryImage : IDisposable
    62. {
    63. public readonly int Width;
    64. public readonly int Height;
    65. public readonly int PixelCount;
    66. public readonly int ByteCount;
    67. public readonly int PixelWidth;
    68. public readonly Pixel* Buffer;
    69. public MemoryImage(Bitmap bmp)
    70. {
    71. Width = bmp.Width;
    72. Height = bmp.Height;
    73. Rectangle bmpRect = new Rectangle(0, 0, Width, Height);
    74. BitmapData bmpData = bmp.LockBits(bmpRect, ImageLockMode.ReadOnly, bmp.PixelFormat);
    75. PixelCount = Width * Height;
    76. ByteCount = bmpData.Stride * Height;
    77. Buffer = (Pixel*)Marshal.AllocHGlobal(ByteCount);
    78. PixelWidth = GetPixelWidth(in bmp);
    79. System.Buffer.MemoryCopy(
    80. (int*)bmpData.Scan0,
    81. Buffer,
    82. ByteCount,
    83. ByteCount);
    84. bmp.UnlockBits(bmpData);
    85. }
    86. private static int GetPixelWidth(in Bitmap bmp)
    87. {
    88. switch (bmp.PixelFormat)
    89. {
    90. case PixelFormat.Format32bppRgb:
    91. case PixelFormat.Format32bppArgb:
    92. return 4;
    93. default:
    94. throw new NotSupportedException(nameof(Bitmap.PixelFormat));
    95. }
    96. }
    97. public void Dispose()
    98. {
    99. Marshal.FreeHGlobal((IntPtr) Buffer);
    100. }
    101. }
    102. [StructLayout(LayoutKind.Explicit)]
    103. public readonly struct Pixel
    104. {
    105. [FieldOffset(0)]
    106. public readonly int Value;
    107. [FieldOffset(0)]
    108. public readonly byte Channel1;
    109. [FieldOffset(1)]
    110. public readonly byte Channel2;
    111. [FieldOffset(2)]
    112. public readonly byte Channel3;
    113. [FieldOffset(3)]
    114. public readonly byte Channel4;
    115. }
    @C.O.D.E
    Ist 2.bmp bekannt bei der Suche? Oder war es Ziel diesen Bereich zu finden und die Koordinaten zurückzugeben, ohne 2.bmp als vergleich zu nutzen?

    Hab was vergessen wegen der verschiedenen Auflösungen, du müsstest wenn du meinen Code verwendest die Anzahl der Pixel in einer Zeile anpassen, ich hab fix 500 genommen, bei kleineren Auflösungen könnte das zu hoch sein.

    VB.NET-Quellcode

    1. If matchInLine > 500 Then

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

    Vollzitat des direkten Vorposts entfernt ~VaporiZed

    Nein, 2.bmp ist nicht bekannt. Hier sind viele Codes nun gepostet worden und ich gucke mir jetzt erst mal alle an. :) Danke an euch alle!
    Liebe Grüße,
    C.O.D.E

    Testautomatisierung (TA):

    5%
    Du findest meinen Beitrag Hilfreich? :thumbup: Dann drück auf Hilfreich!

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

    C.O.D.E schrieb:

    VB.NET-Quellcode

    1. If matchInLine > 500 Then
    Solche Tests sind stets mit Vorsicht zu genießen und können im wahren Leben sehr dynamisch sein.
    Ich würde den Wert dafür in die Settings packen.
    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!
    Für alle Fälle auch noch, mein Code funtioniert so nur mit 24bbp Bildern, da deine Bitmap 3 Bytes pro Pixel für die Farbe nutzt und keine Alphakanal hat. Du kannst aber das Format auslesen und es anpassen, falls Transparenz ins Spiel kommt. Dann hast du 32bbp also 4 Bytes pro Pixel.

    Das Array pixelData ist so aufgebaut: 24bbp

    byte b //pixel x0 y0
    byte g
    byte r
    byte b //pixel x1 y0
    byte g
    byte r

    Mit alpha 32bbp

    byte b
    byte g
    byte r
    byte a
    byte b
    byte g
    byte r
    byte a

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

    @C.O.D.E Die VMLLib kannst Du doch als solche Deinem Projekt hinzufügen, das würde ich nicht übersetzen.
    Das Testprogramm kannst Du einfach übersetzen.
    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!
    Und ich merke grade, dass dieses Programm (Link von @Facebamm in Post #9) wenig getestet wurde, es knallt an allen Ecken und Kanten.
    Werd mich mal eine Weile damit befassen.
    ====
    Man muss die Gebrauchsanleitung im Video ansehen, um die richtige Reihenfolge der Button zu erfahren.
    Leider erhalte ich bei 2 und 4 Centroids unterschiedliche Best Match Images, das spricht nicht unbedingt für Stabilität und Robustheit.
    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!

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