Kommunikation mit Backgroundworker

  • VB.NET
  • .NET (FX) 3.0–3.5

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Bagplatt.

    Kommunikation mit Backgroundworker

    Hallo zusammen,

    Ich beiße mir seit gestern Abend immer noch die Zähne mit diesem Backgroundworker aus.

    Allgemeines zum Aufbau :

    Ein kleines Programm liest/ schreibt mit LockBit das Array eines Bild ein, und gibt mir dieses in einer "PixelArray" Klasse zurück.
    Dieses kann ich dann durch verschiedene Filter schicken die die Werte manipulieren. Anschließen schiebe ich diese Klasse in die Funktion "WriteLockBit" diese verändert das Bild entsprechend.

    Dass bedeutet, dass das Bild auch richtig eingelesen wurde, da die Filter funktionieren.


    Nun wollte ich eine Funktion erstellen, die mir dass Bild in einzelne Schnittmengen unterteil, in etwa so dem bekannten Zauberstab in einigen Bildbearbeitungsprogrammen. Nur dass er nicht nur die angeklickte Farbe einlesen soll, sondern dass ganze Bild in einzelne Schnittmengen einteilen. Hier geht es jetzt nicht um dass "wie", da habe ich schon Ansätze,

    Da die Berechnung bei einem großen Bild doch schon sehr Zeit in Anspruch nimmt, lagere ich diese ganze Arbeit in ein Backgroundworker aus. Um den Fortschritt zu sehen, habe ich eine Klasse mit EventArgs implementiert, die die momentanen Informationen zurück an die Form schickt. Darin sind unter anderem auch die aktuelle Position und die Farbe derer.

    So und jetzt zu dem Problem



    Rechts sieht man dass Orginale Bild, links dass was der Backgroundworker aus dem PixelArrayliest.

    Hier der Code vom Backgroundworker

    VB.NET-Quellcode

    1. Private Sub BGW_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGW.DoWork
    2. 'Daten für das Array intern im Thread erstellen
    3. Dim myArray As PixelArray = MyOrginalKlasse.ReadLockBit(MyOrginalKlasse.GetMyPic)
    4. For x As Integer = 0 To MyOrginalKlasse.GetMyPic.Width - 1
    5. For y As Integer = 0 To MyOrginalKlasse.GetMyPic.Height - 1
    6. Dim t As New Point(x, y)
    7. Dim ns As New SchnittstellenEventArgs
    8. ns.GetThisColor = myArray.SetValue(t)
    9. ns.GetMyInnerLoop = New Point(x, y)
    10. RaiseEvent ReportProgress(Me, ns)
    11. n = True
    12. Next
    13. Next
    14. End Sub


    Habe schon einiges durch, alles nichts geholfen. Arbeite mit Vielen "Kind" Formularen die über ein Property in der Hauptklasse auf die Klasse mit den Funktionen zugreifen. Aus diesem Grund hab ich mal mit "Memberwise.Clone" die ganze Klasse kopiert, um sicherzustellen das der Backgroundworker nichts Threadübergreifendes versucht, hat aber auch nichts geholfen. LG


    Nachtrag
    Wenn ich in der Klasse auf das gespeicherte Bild mit GetPixel direkt die Daten lese, und diese dann durch das Event feuer, dann funktioniert es Ironischerweiße.
    Logischerweiße komme ich dann zu dem Schluss - dass die Funktion mit Lockbits ein falsches Array einlesen würde, was ich aber zu 100% wiederlegen kann, da die Funktion zum lesen/schreiben nur mit der PixelArray Klasse von mir arbeitet, und diese auch nur 1 mal implementiert sind, zusätzlich funktionieren die anderen Filter, diese verwenden auch dass PixelArray und schieben es in die WriteFunktion.



    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Bagplatt“ ()

    Bagplatt schrieb:

    VB.NET-Quellcode

    1. RaiseEvent ReportProgress(Me, ns)
    Du bremst Dein Programm absolut aus, da Du nach jedem einzelnen Pixel ein Event sendest.
    Dieser Code liefe schneller, wenn Du ihn im Main-Thread beließest.
    Sende nach jeder Zeile oder alle 4 oder 8 Zeilen ein Event und stelle das Ergebnis dar. Das bringt etwas an Performance.
    Schmeiß die Boolean-Variable n raus, die wird so nicht verwendet.
    Ich weiß nun nicht, wie Du auf den Bildinhalt zugreifst, ich kenne Deine Klasse PixelArray nicht.
    Wenn Du echte Bildverarbeitung machen willst, überlege, ob Du auf C# umsteigst, denn da kannst Du per unsafe mit Pointern, wie in C / C++ in Deinem Array arbeiten.
    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!
    Hey,

    Danke für deine Antwort,
    Dies ist nicht der eigentliche Code, nach dem mein Programm mir unverständliche Bereiche geliefert hat, hab ich mir das Visuell veranschaulicht, um zu sehen wo der Fehler liegt und wieso.

    Ich weis aber leider immer noch nicht, warum der BGW die Werte die in der Klasse liegen falsch einließt,

    VB.NET-Quellcode

    1. Dim myArray As PixelArray = MyOrginalKlasse.ReadLockBit(MyOrginalKlasse.GetMyPic)


    Steht eigentlich in meiner ersten Form, bzw. in der Übergeordneten, drauf zugreifen tue ich über Propertys. Habe es nur mal testweise in den BGW verschoben um zu sehen ob durch die Übergabe, die vor dem Starten des BGW der selber in einer Klasse, die die Schnittmengen erstellt Fehler entstehen. -Woran es nicht lag-

    Dann habe ich das PixelArray (testweise) nach dem der BGW mir falsche Werte lieferte, wieder in die WriteLockbit Funktion verschieben und mir dass Bild erstellen lassen, dass 1zu1 dem Orginale gleichte, dass bedeutet - der BGW ließt die Werte falsch ein.

    Wenn der BGW in einer Klasse liegt, dass PixelArray mit Zusammenhänge einzelner Bildpunkte,Farben ect. in der selben, diese werden mit ByVal übergeben, und dann in einer PrivatVariabel gespeichert. Ist dies dann immer noch ein Threadübergreifender Vorgang? Selbst wenn ich mit Memberwise.Clone (doppelte Daten mit abertausenden Werten und Zusammenhänge=BadPerformance) in der Klasse speichere?

    Doch selbst wenn : Wie kann ich denn die Variabel die als Private in der Klasse des BGW liegt dann Threadsicher übergeben? Habe gestern hier noch etwas rumgestöbert, dabei habe ich ungefähr dass gefunden :

    ​Arrays und Backgroundworker ergeben meist einen einzigen Zahlensalat. Um dass zu vermeiden sollte man mit Delegaten arbeiten.


    Bin mir jetzt nicht sicher, aber selbst über google habe ich nicht viel gefunden.


    Weil ich nicht weiterkomme habe ich alles mal entfernt, dass Array weggelassen und die Pixel ganz Trival mit einem bestimmten Wert auf eine zu größe Änderung der Farbe mit .GetPixel überprüft.



    (Ich glaub ich werd noch wahnsinnig :D )

    Ich war bereits am überlegen ob ich mir in C++ ein Programm schreibe dass mir die Datenverarbeitet und diese dann in einem ByteArray mit den einzelnen Menge speichert, um sie in diesem zu verwenden. Ganz auf VB möchte ich nicht verzichten, mir gefällt die einfache GUI Erstellung zu sehr.

    LG

    Bagplatt schrieb:

    C++
    C#, das ist mit VB.NET bezüglich des Designers absolut identisch.
    Beschreib mak im Klartext, was Dein Programm machen soll.
    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,
    Es geht um die Erkennung von Gewissen Strukturen. Zuerst will ich die Struktur vom Rest trennen. Diese soll weiter Analysiert werden, dazu verwandele ich sie in Graustufen. Die Beziehungen von den Einzelnen Werten werden ermittelt (Gewisse Fall/Steigwerte und dessen Werte und Auswirkungen auf die Umliegende Pixel). Daraus soll eine Art Variabler Stempel werden der mit einem, ich nenne es mal "Rauschfaktur" anhand der Beziehungen die vom Orginal ermittelt wurden eine neue Struktur mit gewissen Abweichungen erstellt. Danach werden die Pixel gefärbt. Daran orientiere ich mich dann wieder an dem Orginal, und dessen Colorierung.

    Folgendes Beispiel,
    Ich möchte eine Dynamische Textur von Gras erstellen, dazu sollen die Zusammenhänge verarbeitet werden.
    Gewisse Abweichungen einfügen, Verhalten neu Berechnen, Berechnen was für Farbtöne nun passen.

    Aber mein Problem ist, dass der Backgroundworker diese Daten falsch einließt.
    Dass Array erstellen und die eigentlich Bilddaten mit der LockBit Methode einlesen, funktioniert aber.

    Bei der Übergabe dieses Array von einer Klasse zur nächsten entstehen glaub ich diese Fehler, jedoch kann ich dass Bild dass ich mal testweise mit übergeben habe Pixelweise korrekt einlesen

    Bagplatt schrieb:

    Aber mein Problem ist, dass der Backgroundworker diese Daten falsch einließt.
    Das kann ich mir nicht vorstellen.
    Hat Dein Algorithmus jemals ohne BGW funktioniert?
    Bringe ihn zunächst ohne BGW zum Laufen. Wenn er dann läuft, kümmern wir uns darum, Teile in einen separaten Thread auszulagern. Ein BGW als solcher ist da eher suboptimal, heute nimmt man Tasks.
    Fang dann an mit Parralel.For, wenn das Dein Algorithmus hergibt.
    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!
    Hier geht es ja gar nicht um den Algorithmus.

    Es geht rein um die Klasse wo die Orginale Farbe jedes Pixel speichert.

    Ich hab den kompletten Code entfernt. Bis zur ersten Klasse.

    VB.NET-Quellcode

    1. Public Class PixelArray
    2. Private MyArrayColor(0, 0) As Color
    3. ''' <summary>
    4. ''' <code>Erstellt eine Neue Instanz des Pixelarrays</code>
    5. ''' </summary>
    6. ''' <remarks></remarks>
    7. Public Sub New(ByVal Größe As Size)
    8. ReDim MyArrayColor(Größe.Width, Größe.Height)
    9. End Sub
    10. ''' <summary>
    11. ''' <code>Setzt den Wert neu, oder gibt ihn zurück</code>
    12. ''' </summary>
    13. ''' <param name="Item"></param>
    14. ''' <value></value>
    15. ''' <returns></returns>
    16. ''' <remarks></remarks>
    17. Public Property SetValue(ByVal Item As Point) As Color
    18. Get
    19. Return MyArrayColor(Item.X, Item.Y)
    20. End Get
    21. Set(value As Color)
    22. MyArrayColor(Item.X, Item.Y) = value
    23. End Set
    24. End Property
    25. ''' <summary>
    26. ''' <code>Setzt alle Farben</code>
    27. ''' </summary>
    28. ''' <param name="c"></param>
    29. ''' <param name="s"></param>
    30. ''' <remarks></remarks>
    31. Public Sub SetAllToColor(ByVal c As Color, ByVal s As Size)
    32. For x As Integer = 0 To s.Width - 1
    33. For y As Integer = 0 To s.Height - 1
    34. MyArrayColor(x, y) = c
    35. Next
    36. Next
    37. End Sub
    38. End Class


    Diese Klasse ist nichts weiter als ein Platzhalter um nicht jedes mal mit LockBits ein einzelner Pixel einzulesen.

    Wenn ich diese Klasse mit einem vorhandenem Bild fülle, und ohne BGW wieder als Bild ausgebe, enstpricht dass Logischerweise dem Orginal.
    Fange ich jedoch mit dem BGW an, treten diese Verzerrungen ein.


    Der eigentliche Algorithmus wird erst viel später aufgerufen, zu erst erstell ich die Graustufenbitmap mit den besten Kontrasten.

    Bagplatt schrieb:

    Fange ich jedoch mit dem BGW an, treten diese Verzerrungen ein.
    Ich habe da so eine Idee.
    Das ganze hat was mit der BitmapData-Property Stride zu tun.
    Du lieferst z.B. Daten von Index 0 bis 6 (7 Stück, Arrays in .NET sind Null-basiert) und das 7. ist dann das 0. der nächsten Zeile, das ist dann falsch.
    Da musst Du aufpassen, in welcher Richtung Du Daten überträgst, denn in der Bitmap ist dann .Stride die Zielbreite, nicht aber .Width.
    Teste mal eine Bitmap, deren Breite vollständig durch 4 teilbar ist.
    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:

    Teste mal eine Bitmap, deren Breite vollständig durch 4 teilbar ist.


    Getestet mit einem kleinem Bild von 100x100 Pixel.

    In der Klasse

    VB.NET-Quellcode

    1. ​Private Sub BGW_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGW.DoWork
    2. 'Daten für das Array intern im Thread erstellen
    3. Dim Stand As New SchnittstellenEventArgs
    4. With MyOrginalKlasse
    5. For x As Integer = 0 To .GetMyPic.Width - 1
    6. For y As Integer = 0 To .GetMyPic.Height - 1
    7. Stand.GetMyPosition = New Point(x, y)
    8. Stand.GetThisColor = MyOrginalKlasse.GetPictureArray.SetValue(New Point(x, y))
    9. RaiseEvent ReportProgress(Me, Stand)
    10. Next
    11. Next
    12. End With
    13. RaiseEvent Complette(Me, Nothing)
    14. End Sub


    Zum sehen was er denn da einliest

    VB.NET-Quellcode

    1. Private Sub Working(ByVal sender As Object, ByVal e As SchnittstellenEventArgs) Handles MySchnittstelle.ReportProgress
    2. FortSchrittBmp.SetPixel(e.GetMyPosition.X, e.GetMyPosition.Y, e.GetThisColor)
    3. PictureBox1.Image = FortSchrittBmp
    4. End Sub


    Im großen und ganzen hab ich den Code eigentlich nicht verändert, und das Bild stimmt mit dem Orginal überein. ?(
    Ich werde jetzt nochmal die Orginale Funktion einfügen, und sie nicht Pixelweise zeichnen sondern sie in die Funktion WriteLockBit einfügen.

    Aber so ganz ist mir dass noch nicht schlüssig.

    Wieso funktionieren die Filter? Diese lesen dass Array auch ein, und gleichgültig wie groß dass Bild liefern sie dass Array mit den Umgeschriebenen Werten Korrekt zurück?

    Danke schonmal :thumbsup: