Schwarze Pixel mit getpixel() zählen sehr langsam

  • VB.NET

Es gibt 51 Antworten in diesem Thema. Der letzte Beitrag () ist von webmaster.

    Schwarze Pixel mit getpixel() zählen sehr langsam

    Hallo,

    ich habe eine Funktion geschrieben, welche in einem bestimmten Bereich eines SW-Bildes die Anzahl der schwarzen Pixel zählt. Leider muss ich diese Operation mehrmals pro Bild wiederholen, sodass es sehr lange dauert, bis das Bild bearbeitet wurde. Vielleicht hat jemand eine Idee, wie man diesen Code beschleunigen könnte?

    VB.NET-Quellcode

    1. Function SchwarzePixelZaehlen(ByVal y As Integer, ByVal x As Integer, ByVal hohe As Integer, ByVal breite As Integer, ByVal bmp As Bitmap)
    2. 'Diese Funktion zählt die Anzahl der schwarzen Pixel in einem bestimmten Bereich eines Bildes
    3. Dim ax As Integer
    4. Dim ay As Integer
    5. Dim ab As Integer
    6. Dim ah As Integer
    7. Dim SchwarzePixel As Integer = 0
    8. Dim farbe As Color
    9. ax = x - (breite * 0.1) 'Die Breite und Höhe des Bereiches wird erweitert
    10. ay = y - (hohe * 0.1) '
    11. ab = 1.2 * breite '
    12. ah = 1.2 * hohe '
    13. Dim ayAnfang As Integer = ay 'aYanfang definieren
    14. Dim axanfang
    15. Do
    16. axanfang = ax
    17. Do
    18. farbe = bmp.GetPixel(axanfang, ay)
    19. If farbe.GetBrightness = 0 Then
    20. SchwarzePixel = SchwarzePixel + 1 'bei schwarzem pixel einen dazu zählen
    21. End If
    22. axanfang = axanfang + 1
    23. Loop Until axanfang > ax + ab
    24. ay = ay + 1
    25. Loop Until ay > ayAnfang + ah
    26. SchwarzePixelZaehlen = SchwarzePixel
    27. End Function


    Herzlichen Dank!

    FreakJNS schrieb:

    such mal im showroom nach der "Fastgraphics Lib" (Fast Bitmap Lib?)..
    diese bietet auch die funktionen Set/Getpixel - allerdings mittels Lockbits realisiert und damit sehr schnell
    Hallo,

    genau diese Lockbit-Geschichte suche ich. Leider habe ich bisher nur Beispiele in C# gefunden. Auch ein Konverter konnte mir nicht weiterhelfen. Mein Wissen reicht leider nicht um den ganzen C#-Code in VB zu übertragen. Vielleicht kannst Du mir einen Link schicken?
    einfach im showroom gucken xD Heißt sogar FastGraphicsLib - habe mich nicht geirrt: [Beta] FastGraphicsLib 1.0.0.5

    Noch ein kleiner Nachtrag: der code von dir ist die hölle wenn man es übersichtlich mag xD
    benutz doch For-Schleifen - ist besser als das ganze mit loop zu basteln (UntereGrenze/ObereGrenze sind Zahlenwerte, z.b. 0 und Breite-1):

    VB.NET-Quellcode

    1. For x as integer = untereGrenze to ObereGrenze
    2. For y as integer = untereGrenze to ObereGrenze
    3. '.getpixel(x,y) etc
    4. next
    5. next


    Nachtrag2: (musst du nur noch umbasteln sodass die fastgraphicslib benutzt wird statt bitmapfunktionen)

    VB.NET-Quellcode

    1. Public Function CountBlackPixels(ByVal bit As Bitmap, ByVal Bereich As Rectangle) As Integer
    2. Dim count As Integer = 0
    3. For x As Integer = Bereich.X To Bereich.Right
    4. For y As Integer = Bereich.Y To Bereich.Bottom
    5. If bit.GetPixel(x, y) = Color.Black Then count += 1
    6. Next
    7. Next
    8. Return count
    9. End Function

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

    Du musst im blanken Bildspeicher arbeiten. Hier eine Idee davon:

    VB.NET-Quellcode

    1. Dim bmp As Bitmap = New Bitmap(DEINE_BITMAP)
    2. Dim bmpData As System.Drawing.Imaging.BitmapData
    3. bmpData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
    4. Dim ptr As IntPtr = bmpData.Scan0
    5. Dim ofs As Integer = 0
    6. For y As Integer = 0 To bmp.Height - 1
    7. ofs = y * bmpData.Stride
    8. ' auf den Beginn einer neuen Zeile legen
    9. ' wegen der byte-to-ushort-Konvertierung kein Marshal.Copy()
    10. For x As Integer = 0 To bmp.Width - 1
    11. ' hier etwas tun
    12. Next
    13. Next
    14. bmp.UnlockBits(bmpData)
    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!
    Hallo,

    ich habe die FastGraphicsLip eingerichtet.

    Leider erhalten ich folgenden Fehler:
    "Der Bitmapbereich ist bereits gesperrt."

    Wenn ich

    VB.NET-Quellcode

    1. Dim fg As FastGraphics = FastGraphics.FromBitmap(bmp)
    in meinen o. g. Code einfüge.

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

    ich weiß eine Klasse, die kann ein 2-dim Byte-Array erzeugen, mit den Farbwerten einer Bitmap. Da würden dann jeweils 3 oder 4 Bytes ein Pixel darstellen - je nach pixelformat.

    könnte man auch modifizieren, dass jedes Pixel durch einen Int32 dargestellt wäre, oder gleich durch eine Color.

    Aber kommt drauf an, was du machen willst, weil die Grundfassung mit den Bytes ist am flexibelsten - dafür wäre der Test auf bisserl unkomfortabel - er müssteja 4 Bytes prüfen, was bei Int32 in einer Operation geht.

    also 2 Fragen:
    was willst du machen?

    interessiert?
    @ ErfinderDesRades:
    Hey. Ein SW-Bitmap ist in mehrere Regionen (Vierecke) unterteilt. Wird in einer bestimmten Region eine bestimmte Anzahl schwarzer Pixel überschritten, so gilt sie als markiert. Ich bräuchte nur die Anzahl der schwarzen Pixel in einem bestimmten Bereich. Grundsätzlich bin ich interessiert, kann jedoch mit den meisten von Dir Begriffen nichts anfangen. Mir ist jedoch klar, dass die Auswertung eines Arrays durchaus schneller funktioniert als ein Bitmap Pixelweise zu durchlaufen.
    nochma 2 Fragen:
    also du wirst endgültig nur die Farbwerte auf s/w auswerten, und nicht später mal auf die Idee kommen, Farben untersuchen zu wollen, oder auch mal aus geänderten Werten eine neue Bitmap generieren?

    welche Begriffe verstehst du nicht? (könnte sehr wichtig sein ;) )

    Also das hier ist die Klasse

    VB.NET-Quellcode

    1. Imports System.Drawing
    2. Imports System.Drawing.Imaging
    3. Imports System.Runtime.InteropServices
    4. ''' <summary>
    5. ''' verschafft sich Zugriff auf die Pixeldaten von Images, in Form von 2-dim Byte-Arrays
    6. ''' </summary>
    7. Public Class PixelHelper
    8. 'Der übliche Zugang zu Pixeldaten über Bitmap.LockBits() impliziert 4 Kopiervorgänge der
    9. '! gesamten Bitmap: Lock/Unlock, und dann jeweils Marshall.Copy() in ein managed Array.
    10. 'Die Bitmap-Erstellung unter Verwendung gepinnter Arrays reduziert die Kopiervorgänge auf
    11. '! zwei.
    12. Private _ReadPixels As Byte(,)
    13. Public _ResultData As New BitmapData
    14. Private Shared _GrayAttr As New Imaging.ImageAttributes()
    15. Shared Sub New()
    16. _GrayAttr.SetColorMatrix(_GrayMatrix)
    17. End Sub
    18. Public Sub New( _
    19. ByVal Img As Image, _
    20. ByVal Format As Imaging.PixelFormat, _
    21. Optional ByVal ConvertToGray As Boolean = False)
    22. With _ResultData
    23. .Width = Img.Width
    24. .Height = Img.Height
    25. _ReadPixels = CreateWritePixels(Format)
    26. Dim GCH As GCHandle = GCHandle.Alloc(_ReadPixels, GCHandleType.Pinned)
    27. Using _
    28. Bmp As New Bitmap(.Width, .Height, .Stride, Format, GCH.AddrOfPinnedObject()), _
    29. G As Graphics = Graphics.FromImage(Bmp)
    30. Dim Rct As New Rectangle(0, 0, .Width, .Height)
    31. If ConvertToGray Then
    32. G.DrawImage(Img, Rct, 0, 0, .Width, .Height, GraphicsUnit.Pixel, _GrayAttr)
    33. Else
    34. G.DrawImageUnscaledAndClipped(Img, Rct)
    35. End If
    36. End Using
    37. GCH.Free()
    38. End With
    39. End Sub
    40. Public ReadOnly Property ReadPixels() As Byte(,)
    41. Get
    42. Return _ReadPixels
    43. End Get
    44. End Property
    45. End Class
    Ich hoffe, die geht noch, habe nämlich bischen abgespeckt - ohne zu testen.
    Um ReadPixels zu kriegen musste mit New PixelHelper(blabla) einen erstellen, und in dessen ReadPixelProperty ist halt ein 2-dim Byte-Array, bei dem 4 Bytes je ein Pixel darstellen.

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

    @ ErfinderDesRades:
    also du wirst endgültig nur die Farbwerte auf s/w auswerten, und nicht
    später mal auf die Idee kommen, Farben untersuchen zu wollen, oder auch
    mal aus geänderten Werten eine neue Bitmap generieren?
    Beides JA bzw. zu Testzwecken möchte ich Farbe einbringen (ist aber auch nicht so wichtig!).
    welche Begriffe verstehst du nicht?
    Die verschiedenen Bitmap-Typen mit Bytes und Pixeln :)

    Mit Deiner Funktion kann ich so leider nichts anfangen ...




    @Freak

    Ich habe Deinen o. g. Code angepasst:

    VB.NET-Quellcode

    1. Public Function CountBlackPixels(ByVal bit As FastGraphics, ByVal Bereich As Rectangle) As Integer
    2. Dim count As Integer = 0
    3. For x As Integer = Bereich.X To Bereich.Right
    4. For y As Integer = Bereich.Y To Bereich.Bottom
    5. If bit.GetPixel(x, y).R = 0 Then
    6. count += 1
    7. bit.SetPixel(x, y, Color.Aquamarine)
    8. End If
    9. Next
    10. Next
    11. Return count
    12. End Function


    Funktioniert prima. Ich habe

    VB.NET-Quellcode

    1. bit.SetPixel(x, y, Color.Aquamarine)
    hinzugefügt um zu Testzwecken die Pixel farblich zu markieren. Nur leider ändert sich auf den Bild nichts.

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

    ~blaze~ schrieb:

    Hi
    da SetPixel die Bitmapdaten ändert, musst du die Refresh()-Methode des Controls aufrufen, das die Bitmap verwendet. Sonst bemert das Control nichts von der Änderung.
    Übrigens überprüfst du nur, ob der Rot-Kanal 0 ist.

    Gruß
    ~blaze~
    Wie kann ich denn Prüfen, ob der Pixel Schwarz ist? Ich dachte bei Rot=0 muss der Pixel schwarz sein. Oder ist es 255? lol

    Edit: Natrülich ist es anders rum ... sorry für meine Dummheit.

    Edit: Wie bekomme ich denn das neue Bild aus der Funktion in meine Picturebox? Die Funktion gibt ja nur einen Integer zurück ... :(

    VB.NET-Quellcode

    1. Public Function CountBlackPixels(ByVal bit As FastGraphics, ByVal Bereich As Rectangle) As Integer
    2. Dim count As Integer = 0
    3. For x As Integer = Bereich.X To Bereich.Right
    4. For y As Integer = Bereich.Y To Bereich.Bottom
    5. If bit.GetPixel(x, y).GetBrightness = 0 Then
    6. count += 1
    7. bit.SetPixel(x, y, Color.Aquamarine)
    8. End If
    9. Next
    10. Next
    11. Return count
    12. End Function

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

    Richtig Schwarz ist er wenn R = 0, G = 0 und B = 0
    Du kannst allerdings eine Tolleranz einbauen sodass ein Pixel als schwarz erkannt wird wenn all diese Werte kleiner sind als z.B. 5. Bei JPG-Bildern kann es ja passieren, dass durch die Komprimierung die Farbwerte verfälscht werden.

    Allein den R-Kanal als Indikator zu benutzen ist schlecht, schau dir mal die Farbe an die R=0, G=255 und B=255 hat (einfach in Paint austesten)^^
    Einfach das Control, das die Bitmap anzeigt, mit Refresh() neuzeichnen oder das Angezeigte mit Invalidate() für ungültig erklären.
    Die Logik hinter der Farbgebung am PC ist so: Farben werden in R, G und B aufgeteilt, wobei die einzelnen Werte die Intensität beschreiben. Intensität 0 bedeutet, dass der Kanal fehlt, 255, dass er voll da ist. Wenn du jetzt R = 255, G = 0, B = 0 hast, bedeutet das, dass die Farbe Rot rauskommt - und zwar nicht abgedunkelt. Wenn du jetzt R = 0, G = 0, B = 0 hast, ist halt gar keine Farbe vorhanden ==> schwarz. R = 255, G = 255, B = 255 bedeutet, dass alle Kanäle voll vorkommen ==> weiß. Grau wäre demnach R = G = B.
    GetBrightness heißt, dass die Helligkeit herausgefunden wird. Das ist eine Funktion und kann nicht gesetzt werden. Besser ist:

    VB.NET-Quellcode

    1. Dim result As Color = If(input.GetBrightness() < 0.5f, Color.Black, Color.White)


    Gruß
    ~blaze~

    ~blaze~ schrieb:

    Einfach das Control, das die Bitmap anzeigt, mit Refresh() neuzeichnen oder das Angezeigte mit Invalidate() für ungültig erklären.
    Hallo,

    Ich verwende folgenden Code + die o. g. Funktion:

    VB.NET-Quellcode

    1. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    2. Dim fg As FastGraphics = FastGraphics.FromBitmap(New Bitmap(PictureBox1.Image))
    3. Dim bereich As New Rectangle(0, 0, 300, 300)
    4. MsgBox(CountBlackPixels(fg, bereich))
    5. PictureBox1.Refresh()
    6. End Sub


    Die Farbe wird nicht übernommen.