Bestimmte Color in Bild finden

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

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Bestimmte Color in Bild finden

    Hey leute,

    ich wollte mal fragen welche Methode ich wählen würdet wenn ihr ein Bild / BIldauschnitt auf eine bestimmte farbe checken wollt. Würdet ihr das Bild in eine Picturebox laden und dann von da aus weiter arbeiten? Oder gibt es da noch bessere methoden?

    liebe grüße und frohes neues Jahr :)
    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 Nicht PictireBox, sondern eine Bitmap-Instanz.
    Die Frage ist, wie groß der zu durchsuchende Bereich ist.
    Du kannst in 2 For-Schleifen die x- und y-Koordinate durchgehen und pixelweise testen.
    Das ist programmiertechnisch sehr einfach, aber zeitaufwändig.
    Du kannst mit Bitmap.LockBits docs.microsoft.com/en-us/dotne…?view=dotnet-plat-ext-5.0
    das ganze wesentlich beschleunigen, indem Du gezielt Speicherinhalte suchst.
    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!
    Weitere Links zum Thema LockBits: Tutorial oder ein Beitrag aus der Eigenwerbung
    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.
    @RodFromGermany

    So wie bei deinem ersten Beispiel hatte ich es auch wirklich probiert - du hast recht es ist wirklich langsam.

    VB.NET-Quellcode

    1. Dim PixelList As New List(Of String())
    2. Private Sub btn_scan_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim btmp As New Bitmap(pic_scan.Image)
    4. For x As Integer = 0 To btmp.Width - 1
    5. For y As Integer = 0 To btmp.Height - 1
    6. If btmp.GetPixel(x, y).ToString = "Color [A=255, R=248, G=253, B=45]" Then
    7. MessageBox.Show("Found")
    8. End If
    9. Next
    10. Next
    11. End Sub


    LockBits werde ich mir wirklich angucken.
    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 schrieb:

    VB.NET-Quellcode

    1. If btmp.GetPixel(x, y).ToString = "Color [A=255, R=248, G=253, B=45]" Then
    ist suboptimal.
    Dies sollte etwas schneller laufen:

    VB.NET-Quellcode

    1. Dim PixelList As New List(Of String())
    2. Private Sub btn_scan_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim btmp As New Bitmap(pic_scan.Image)
    4. Dim col = Color.FromArgb(255, 248, 253, 45).ToArgb()
    5. For x As Integer = 0 To btmp.Width - 1
    6. For y As Integer = 0 To btmp.Height - 1
    7. If btmp.GetPixel(x, y).ToArgb() = col Then
    8. MessageBox.Show("Found")
    9. End If
    10. Next
    11. Next
    12. End Sub
    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!
    @Bartosz Imho extrem unsinnige Einsparung - die Laufzeit ist immer noch O(n^2) (für width = height) - da machen die paar Einsparungen an den Rändern keinerlei Unterschied, im Gegenteil: Das macht es sogar schlechter, wenn
    man dann die 1D-Variante implementieren möchte.

    @TE: Schau dir ehrlich Lockbits an, das geht wesentlich schneller als über die gesamte Pixel-Matrix mit .GetPixel(x,y) zu iterieren.
    So kannst du exemplarisch vorgehen - hatte mal was dazu im Showroom gestellt: Scanline-Algorithmus

    Anmerkung: Gut, man kann argumentieren, dass die Laufzeit sich nicht signifikant ändert, denn auch wenn dann nur noch 1D betrachtet wird, gibt es insgesamt N*M Elemente, dennoch sind geschachtelte Schleifen mitnichten besser als eine einfache, schon deshalb nicht, weil man die Iteration durch ein 1D-Array häufig (in diesem Fall erst recht) parallelisieren kann.

    _
    Und Gott alleine weiß alles am allerbesten und besser.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „φConst“ ()

    Imho extrem unsinnige Einsparung
    Ja ok. Das war mir nur von einem Gespräch mit Jemandem eingefallen, der Gesichtserkennungssoftware schreibt. Man prüft nicht an den Rändern. Weil wenn man ein Foto macht, stehen die Personen gewöhnlich mittiger beziehungsweise Gesichter, die halb abgeschnitten sind, bringen nix. Danke dir trotzdem für die Aufklärung du hast Recht, für einmalige Sachen ist das Einsparen unnütz.

    φConst schrieb:

    bei einer Gesichtserkennung
    wird per Bildverarbeitung zunächst das Gesicht vom Hintergrund getrennt, da gehen ganz andere Algorithmen ab, und die gehen nicht mit GetPixel().
    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!
    Lieber RFG, das habe ich auch nicht gesagt. Es ging mir nur um den jeweiligen Frame. :)
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.
    Hab hier noch ne "FastPix"-Klasse gefunden, vllt. hilfreich:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Drawing.Imaging
    2. Imports System.Runtime.InteropServices
    3. Public Class FastPix
    4. '(c) Vic Joseph (Boops Boops) 2009. Correction for UseByteData applied in October 2013.
    5. 'The FastPix class provides fast pixel processing of bitmaps. It encapsulates the LockBits/UnlockBits methods.
    6. 'FastPix is designed to deal only with the default GDI+ pixel format 32bppArgb and the format 32bppPArgb,
    7. 'but it includes a method for converting bitmaps.
    8. 'To use FastPix, unzip it to your hard drive.
    9. 'To add it to your project in VisualStudio, select the Project Menu then Add Existing Item...
    10. 'Then navigate to fastpix.vb on your drive.
    11. 'The main uses of FastPix are as follows.
    12. '1. To convert a bitmap to a 32bppPArgb (32 bit premultiplied) format, use:
    13. ' FastPix.Convert(myBitmap, PixelFormat.Format32bppPArgb)
    14. ' Note: for the canonical format 32bppArgb, it may be more convenient to do something like this:
    15. ' Dim myBitmap as New Bitmap("D:\Pictures\ExistingImage.jpg")
    16. '1. A fast substitute for Bitmap.GetPixel and Bitmap.SetPixel. Example usage:
    17. ' Using fp as New FastPix(myBitmap)
    18. ' Dim myColor As Color = fp.GetPixel(x, y)
    19. ' fp.SetPixel(x, y, Color.Orange)
    20. ' End Using
    21. '2. The class provides a PixelArray property, which is a representation of the bitmap as an array of integers.
    22. ' (Actual performance will naturally also depend on other aspects of your processing loop.) Example usage:
    23. ' Using fp as New FastPix(myBitmap)
    24. ' 'Make a local reference to the array; it is roughly 4x as fast as direct references to fp.PixelArray:
    25. ' Dim pixels as Integer() = fp.PixelArray
    26. ' For i as integer = 0 to pixels.Length - 1
    27. ' 'example: substitute a color
    28. ' if pixels(i) = Color.Red.ToArgb then pixels(i) = Color.Blue.ToArgb
    29. ' 'example: invert the color
    30. ' pixels(i) = pixels(i) AND $HFFFFFF
    31. ' Next
    32. '
    33. ' End Using
    34. '3. The class also provides an optional Byte array property, which represents the bitmap as a 1D array of bytes.
    35. 'It may be useful when individual color bytes have to be modified.
    36. 'NOTE: the bytes are in BRGA order, so the first Alpha byte is at index = 3. Example usage:
    37. ' Using fp As New FastPix(myBitmap, True)
    38. ' Dim bytes As byte() = fp.ColorByteArray
    39. ' 'Modify the Alpha bytes to make the bitmap 50% transparent:
    40. ' For i As Integer = 3 to bytes.Length - 1 Step 4
    41. ' bytes(i) = 127
    42. ' Next
    43. ' End Using
    44. Implements IDisposable
    45. Private _bmp As Bitmap
    46. Private _w, _h As Integer
    47. Private _disposed As Boolean = False
    48. Private _bmpData As BitmapData
    49. Private _UseByteArray As Boolean = False
    50. 'An array of integers representing the pixel data of a bitmap.
    51. Public Property PixelArray() As Integer()
    52. 'An array of bytes representing the pixel data of a bitmap.
    53. Public Property ColorByteArray() As Byte()
    54. #Region "Constructors/Destructors"
    55. 'The New Sub contains the LockBits+Marshall.Copy code.
    56. Public Sub New(ByRef bmp As Bitmap, Optional ByVal UseByteArray As Boolean = False)
    57. _UseByteArray = UseByteArray
    58. Dim pFSize As Integer = Image.GetPixelFormatSize(bmp.PixelFormat)
    59. If pFSize <> 32 OrElse bmp.PixelFormat = PixelFormat.Indexed Then
    60. Throw New FormatException _
    61. ("FastPix is designed to deal only with 32-bit pixel non-indexed formats. Your bitmap has " _
    62. & pFSize & "-bit pixels. You can convert it using FastPix.ConvertFormat.")
    63. Else
    64. 'Convert the bitap to a 1 dimensional array of pixel data:
    65. _w = bmp.Width
    66. _h = bmp.Height
    67. _bmp = bmp
    68. Dim bmpRect As New Rectangle(0, 0, _w, _h)
    69. _bmpData = _bmp.LockBits(bmpRect, ImageLockMode.ReadWrite, _bmp.PixelFormat)
    70. If UseByteArray Then
    71. ReDim ColorByteArray(_w * _h * 4 - 1)
    72. Marshal.Copy(_bmpData.Scan0, ColorByteArray, 0, ColorByteArray.Length)
    73. Else
    74. ReDim PixelArray(_w * _h - 1)
    75. Marshal.Copy(_bmpData.Scan0, PixelArray, 0, PixelArray.Length)
    76. End If
    77. End If
    78. End Sub
    79. Public Overloads Sub Dispose() Implements IDisposable.Dispose
    80. Dispose(True)
    81. GC.SuppressFinalize(Me)
    82. End Sub
    83. 'The Dispose Sub contains the Marshal.Copy+UnlockBits code.
    84. Protected Overloads Sub Dispose(ByVal disposing As Boolean)
    85. If Not _disposed Then
    86. If _UseByteArray Then
    87. If ColorByteArray IsNot Nothing Then Marshal.Copy(ColorByteArray, 0, _bmpData.Scan0, ColorByteArray.Length)
    88. Else
    89. If PixelArray IsNot Nothing Then Marshal.Copy(PixelArray, 0, _bmpData.Scan0, PixelArray.Length)
    90. End If
    91. _bmp.UnlockBits(_bmpData)
    92. ColorByteArray = Nothing
    93. PixelArray = Nothing
    94. _bmpData = Nothing
    95. _disposed = True
    96. End If
    97. End Sub
    98. #End Region
    99. #Region "Public Methods"
    100. 'Return the color of any pixel in the bitmap.
    101. Public Function GetPixel(ByVal x As Integer, ByVal y As Integer) As Color
    102. Return Color.FromArgb(PixelArray(y * _w + x))
    103. End Function
    104. 'Set the color of any pixel in the bitmap.
    105. Public Sub SetPixel(ByVal x As Integer, ByVal y As Integer, ByVal clr As Color)
    106. PixelArray(y * _w + x) = clr.ToArgb
    107. End Sub
    108. 'Standardize the bitmap format for use with FastPix.
    109. Public Shared Sub ConvertFormat(ByRef bmp As Bitmap, _
    110. Optional ByVal TargetFormat As PixelFormat = PixelFormat.Format32bppArgb)
    111. Try
    112. Dim bmp2 As New Bitmap(bmp.Width, bmp.Height, TargetFormat)
    113. Using g As Graphics = Graphics.FromImage(bmp2)
    114. bmp.SetResolution(96, 96)
    115. g.DrawImageUnscaled(bmp, Point.Empty)
    116. End Using
    117. bmp = bmp2
    118. Catch
    119. Throw New FormatException("FastPix could not convert the bitmap to the standard format.")
    120. End Try
    121. End Sub
    122. #End Region
    123. End Class

    @Morrison Deine Klasse ist nicht vollständig implementiert.
    Wenn ein Bild (um nicht das Wort "Bitmap" zu benutzen) als ByteArray abgelegt wird, knallt es bei den Befehlen GetPixel() und SetPixel().
    Ich würde diese Befehle stets ein Integer zurück geben lassen, da die Hin- und Her-Color-Konvertierung die Zeit frisst.
    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!

    RodFromGermany schrieb:

    φConst schrieb:

    bei einer Gesichtserkennung
    wird per Bildverarbeitung zunächst das Gesicht vom Hintergrund getrennt, da gehen ganz andere Algorithmen ab, und die gehen nicht mit GetPixel().


    Das ist mir schon klar, man würde aber tatsächlich als Input für CNNs z.B. eben nicht die Ränder des Bildes forwarden - zentriertere Ansätze wären da eher angebracht. Ist aber halt nur ein Optimierungsschritt, gibt wesentlich bessere.
    Dass "GetPixel" verwendet werde habe ich nirgends gesagt, lol. Hör mal auf in mein Beitrag irgendwas hineinzuprojizieren, kann ich gar nicht ab.
    Und Gott alleine weiß alles am allerbesten und besser.
    Ich habe mich jetzt ne ganze weile eingelesen und bin zu dem Entschluss gekommen das es vielleicht sinnvoller wäre wenn ich einen bestimmten Bildausschnitt in einem Screenshot suchen könnte mit rückgabe als x-y position. Ist das möglich? Ich denke das sprengt aktuell meine Kenntnisse. Kann mir da wer wirklich weiterhelfen?

    Sprich, ich möchte auf meinem Bildschirm ein vorgefertigtes kleines Bild suchen und dann die Position erfahren.

    liebe grüße,
    Daniel
    Liebe Grüße,
    C.O.D.E

    Testautomatisierung (TA):

    5%
    Du findest meinen Beitrag Hilfreich? :thumbup: Dann drück auf Hilfreich!
    Möglich? Ja.
    Aber du scheinst das meines Erachtens ein wenig zu unterschätzen.
    Das was du vorhast, wird heute noch erforscht. Anfangs kann ich dich auf Feature-Matching verweisen, ein Objekt in einem Bild wiederzufinden ist aber kein Projekt das mit paar Zeilen Codes erledigt wäre. Sag uns doch einfach was du vorhast, statt nur Fetzen deines Vorhabens runterzugießen.
    Und Gott alleine weiß alles am allerbesten und besser.
    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!

    φConst schrieb:

    Sag uns doch einfach was du vorhast, statt nur Fetzen deines Vorhabens runterzugießen.


    Okay. Wo fange ich an. Wir (Ich, Arbeitskollegen, freunde) Spielen ein Computerspiel namens ARK. In diesem Computerspiel gibt es einen aktiven Log. Dort sieht man alles was in seinem Tribe gemacht wird. (Wenn wände abgerissen werden, etwas gesprengt mit oder zerstört wird).

    Foto davon ist im Anhang. Dort möchte ich gerne gerne auf das Wort "demolished" checken um zu Erfahren wann etwas abgerissen wurde. Das Problem - bei dem demolished ist hinter dem Font quasi immer ein anderer Background da man sich nie an der gleichen stelle im Spiel befindet.
    Bilder
    • ffffesw.jpg

      30,43 kB, 464×133, 45 mal angesehen
    • UnbenanntDDD..jpg

      625,99 kB, 1.920×1.080, 45 mal angesehen
    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 Oder Du liest das Control selbst aus.
    Was ist das für ein Programm - Native oder Assembly?
    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!