Hochgeschwindigkeitsauswerung eines Arrays

  • VB.NET
  • .NET (FX) 4.0

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

    Hochgeschwindigkeitsauswerung eines Arrays

    Hallo liebes Forum

    Ich habe ein Programm welches Bilder einer Zeilenkamera (8192p * 1p Auflösung) empfängt und auswerten soll. Die Bilder der Kamera bekomm ich ohne Probleme, jedoch müssen diese auch ausgewertet werden und zwar in Echtzeit.
    Die Auswertung ist simpel, ich muss einfach ein Integral aus dem Array bilden. Mein Problem ist es jedoch, das diese Operation viel zu langsam ist.
    Die Kamera liefert mir 12'000 FPS und ich sollte mindestens 7'000 davon auswerten können. Die Bilder in der Geschwindigkeit in die Software einlesen ist kein Problem, jedoch sobald ich die Berechnung einschalte gehen die FPS in den Keller, und zwar auf ca. 80 FPS was viel zu langsam ist. Wie kann ich die Integralberechnung massiv beschleunigen?

    Momentan löse ich das ganze folgendermassen:

    VB.NET-Quellcode

    1. Private Sub calc_image_new(ByVal data As Array)
    2. Dim sum As Integer = 0
    3. For i As Integer = 0 To 8191
    4. sum += data(i, 0)
    5. Next
    6. Label2.Text = sum.ToString
    7. End Sub


    In dem Array sind die Graustufen abgespeichert, also jeder Pixel hat einen wert zwischen 0 und 255.

    Ich hoffe Ihr könnt mir dabei helfen.

    Zusatzinfos

    Kamera: Basler raL8192-12gm
    Kommunikation: GigE
    Ansteuerungsmodul: ActiveGige Control von ab-soft.com



    Gruss Ebrithil
    Mein Erstes Tutorial: Erweitertes Arduino Tutorial

    Simpler D&D FTP Uploader: Edge-Load
    Also soweit ich weiß gibt es eine Möglichkeit, den Speicher den deine Anwendung normal reservieren darf, zu erweitern.
    Somit könnte man mehr im RAM auslagern.

    WIe man die Berechnung jetzt an sich beschleunigt weiß ich leider nicht, habe mich damit noch nicht beschäftigt.

    Ich bin mir aber ziemlich sicher, dass man das über die Grafikkarte berechnen lassen kann anstatt
    über den Prozessor.
    Versuchs doch einmal mit LockBits

    Evtl. hilft dir der Beitrag

    Mfg Ratdinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Parallel.For könnte auch dein Freund sein. Verteilt von allein die Schleife auf die vorhandenen Cores auf, so rechnen alle CPU-Kerne mit voller Leistung anstelle nur eines einzelnen.
    Auch könnte es vllt. helfen, kein untypisiertes Array als Parameter mitzugeben, so das .NET gezwungen ist, das Ding erstmal zurechtzukonvertieren. Ein Array(of Integer) könnte da nochmal ordentlich Power bringen.

    Viel schneller als der bisherige Code geht dann praktisch nur noch mit Assembler und wenn das nicht reicht, ist der Rechner schlicht zu langsam.
    Hi
    bei den paar Berechnungen genügt das schon. Was du aber willst, ist eigentlich doch nicht das Integral, sondern die Summe, Integral wäre nur approximiert möglich, weil die Funktion ja nicht mal bekannt ist, außer du legst da was durch. Die Summe der Flächenstücke wäre eine Approximation für die Fläche, falls du das möchtest, da ist dein Ansatz aber falsch. Das Array solltest du, wie bereits gesagt wurde, typisiert angeben (d.h. z.B. Double() oder Byte()) und nach Möglichkeit 1-dimensional halten.

    Was macht außerdem Option Strict bei dir?

    Gruß
    ~blaze~

    Radinator schrieb:

    Evtl. hilft dir der Beitrag
    Nein, Dein Beitrag ist nicht hilfreich, denn LockBit ist eine Bitmap-Methode.
    @ebrithil 1. Mach Option Strict On und übergib ein Byte-Array, da sparst Du ggf. ein paar Array-Konvertierungen.
    2. Wen Du fit bist, mach das ganze in C# und dort mit unsave Code, arbeite mit einem Pointer auf das Byte-Feld, da entfällt die Index-Rechnung.
    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!
    Danke für die vielen Ideen,

    Ich habe jetzt Option Strict eingeschaltet und alles verbessert. Ausserdem auch noch das Array Typisiert und die Anzeige im Label abgeschaltet. Das Array kann ich aus Hardware-Gründen leider nicht 1-dimensional halten.
    Nach all den Verbesserungen bekomme ich jetzt immerhin statt 80 FPS ganze 6000 FPS.

    Doch am besten wäre es wenn ich die vollen 12'000 bekomme.
    Wäre es möglich die Berechnung auf mehrere Cores zu verteilen? Und wenn Ja, wie sieht da der Ansatz aus?


    mfg Ebrithil
    Mein Erstes Tutorial: Erweitertes Arduino Tutorial

    Simpler D&D FTP Uploader: Edge-Load

    ebrithil schrieb:

    wie sieht da der Ansatz aus?
    Parallel.For und Parrallel.ForEach. Da musst Du aber den Code entsprechend parallelisierbar gestalten.
    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!
    Bei einer Parallelisierung mit Grad n lass jeden Thread arrayLänge/n Elemente durchaddieren und summiere die Resultate anschließend einfach auf. Dazu speichert jeder Thread seine Zwischensumme und legt diese nach Abschluss seiner Summenberechnung in einem Array ab oder addiert sie mittels System.Threading.Interlocked auf (geht bei Double glaub ich aber nicht, d.h. Array ist praktischer. Setzt halt voraus, dass jeder Thread seinen Index kennt ==> Parallel.For kanns). Ehrlich gesagt erstaunt es mich aber, dass die Berechnung so viel Performance frisst.
    Dass es hardwaretechnisch nicht möglich ist, macht btw. wenig Sinn, da es der Hardware egal ist, wie die High-Level-Ansicht ist, sie muss ja nur wissen, wo der Speicher liegt. D.h. vmtl. liegt's entweder an einer etwas unschön programmierten Software zum Hardware verwalten oder du denkst, du würdest es in einer bestimmten Art und Weise tun müssen, die aber nicht unbedingt der Fall ist.

    Am besten postest du nochmal deinen aktuellen Code.

    Gruß
    ~blaze~
    Danke für die weitere Hilfe, habe meine Schleife so umgeschrieben das sie mit Parallel.For läuft, und die Framerate springt auf 10'000 FPS hoch. Der Prozessor erleidet auch statt 17% Auslastung, bis zu 23%.

    Warum ich das Array nicht 1-dimensional bekomme ist folgender Grund: Meine Zeilenkamera macht zuerst 50 Aufnahmen und sendet die mir als Bild mit einer Auflösung von 8192 * 50 zu, dies ist nötig damit ich die vollen 12'000 FPS bekomme.


    Hier mein Aktueller Code:

    VB.NET-Quellcode

    1. Private Sub calc_image_new(ByVal data(,) As Byte)
    2. Dim height As Integer = CInt(nud_settings_height.Value - 1)
    3. Parallel.For(0, height, Sub(count)
    4. Dim sum As Integer = 0
    5. For i As Integer = 0 To 8191
    6. sum += data(i, count)
    7. Next
    8. ' Label2.Text = sum.ToString
    9. End Sub)
    10. End Sub


    Und hier das Event, welches beim Erhalt eines Bildes gefeuert wird:

    VB.NET-Quellcode

    1. Private Sub AxActiveGige1_FrameAcquired(sender As System.Object, e As System.EventArgs) Handles AxActiveGige1.FrameAcquired
    2. count += 1
    3. grab = CType(AxActiveGige1.GetImageData, Byte(,))
    4. If cb_measure.Checked Then
    5. 'Dim calcThread As Thread = New Thread(New ThreadStart(AddressOf calc_imageThreadStart))
    6. 'calcThread.Start()
    7. calc_image_new(grab)
    8. End If
    9. End Sub



    mfg Ebrithil
    Mein Erstes Tutorial: Erweitertes Arduino Tutorial

    Simpler D&D FTP Uploader: Edge-Load
    Das die Kerne nur zu 17% ausgelastet sind, ist eigentlich ein Zeichen dafür, das die Daten nicht schnell genug kommen. Wäre das der Fall, müßten die Cores allesamt mit 100% laufen.
    Da ist nun irgendwo ein neuer Flaschenhals aufgetaucht (ich tippe mal auf das abfischen der rohen Billdaten).

    ebrithil schrieb:

    habe meine Schleife so umgeschrieben das sie mit Parallel.For läuft, und die Framerate springt auf 10'000 FPS hoch. Der Prozessor erleidet auch statt 17% Auslastung, bis zu 23%.


    von 80 auf 10000 FPS. Wow, das anzeigen im Label macht es wirklich langsam xD (und nein, es kann nicht an der parallelisierung liegen, wenn die auslastung nur von 17% auf 23% stieg.
    Die grösste Anstieg wurde durch das Typisieren des Arrays verursacht, das Anzeigen im Label hatte dann noch um die 1000 FPS gebracht.

    Das "abfischen" der rohen Bilddaten stellt kein Problem dar, denn wenn ich das alleine betreibe, ohne Berechnung, dann habe ich die ganzen 12'000 FPS
    Mein Erstes Tutorial: Erweitertes Arduino Tutorial

    Simpler D&D FTP Uploader: Edge-Load
    Aber es dürfte trotzdem möglich sein als 1D Array, ein 2D Array ist auch nur ein 1D Array im Speicher, nur der Zugriff ist anders und den kann man bei einem 1D Array beschleunigen...
    2d[x,y] = 1d[y*width+x]

    das gibt z.B. bei Schleifen einen Vorteil(nur ein Beispiel, gibt noch mehr):

    VB.NET-Quellcode

    1. Dim index As Integer = 0
    2. for y As Integer = 0 To height-1
    3. for x As Integer = 0 To width-1
    4. 1d[index]//datenverarbeitung
    5. index +=1
    6. Next
    7. Next

    Hier hast du ausschließlich increments(wenn du x und y nicht brauchst, kannst du eine Schleife draus machen - auch schneller). Anstelle von einer Multiplikation bei jedem Array-Zugriff...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Die Online Doku zu deiner Komponente scheint nicht sehr gut für .Net Dokumentiert zu sein(und ich kenn mich mit ActiveX nicht genügend aus), ob du das jetzt einfach als 1D abreifen kannst(vmtl. nicht).
    Aber wenn ich die richtige Doku habe, dann kannst du dir aufjedenfall mit C# einen rießen Vorteil verschaffen:
    ab-soft.com/AGEHELP/agehelp.html?_getimagepointer.htm
    GetImagePointer-> denn GetImageData legt noch eine Kopie an, wenn es irrelevant ist, dass das Originalbild bearbeitet wird(bzw. du nur analysierst). Selbst wenn nicht hast du die Möglichkeit mit unsafe zuzugreifen, den Speicher direkt zu kopieren und kommst automatisch an ein 1D-Array.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---