![]() |
Bilder komprimieren und speichern mit Threads
Liebe Mitlesende,
ich stehe vor folgendem Problem und dabei ein bisschen auf dem Schlauch: Gegeben ist ein Programm, dass über TDelphiTwain einen Seitenscanner ansteuert und pro Scan-Auftrag etwa 25 DIN-A4-Seiten als TBitmap geliefert bekommt. Der Scanner schafft ca. 45 Seiten/min. Die TBitmaps werden zunächst jeweils in einer TObjectList abgelegt. Erst wenn das letzte Bild gescannt wurde, werden diese alle nacheinander beschnitten, größenangepasst, gedreht, ins PNG-Format komprimiert und dann gespeichert. Das hat ein paar Nachteile: So belegt das Programm während des Scannes viel, viel Speicher, die CPU wird während des Scannens de facto nicht benutzt etc. pp. Meine Idee war es daher, das ganze in Threads auszulagern. Allerdings habe ich Probleme damit, wie ich das logisch hinbekommen kann. Ich könnte jeweils einen Thread erzeugen, sobald das nächste Bild geliefert wird. Damit würde ich aber den PC heillos überlasten. Ich müsste es daher hinbekommen, dass ich die Anzahl auf z.B. 10 beschränke. Wenn also 10 Threads gestartet sind, sollen weitere Bilder nach dem alten Muster wieder in eine Liste kommen. Ist ein Thread fertig (also wieder nur 9), soll ein neuer gestartet werden ... Meine Fragen:
Bitte auch gern Links, wo ich mich zu dem Thema belesen kann. Gruß, Alex |
AW: Bilder komprimieren und speichern mit Threads
Zitat:
Zitat:
Das mit der Objectlist lässt sich leicht lösen. Solange du auf ein Bitmap nur aus einem Thread zugreifst, ist alles OK. Also im Hauptthread erstellen, das Bitmap an den Thread weitergeben und dort dann zerstören sollte kein Problem sein solange du nach der Übergabe im Hauptthread nichts mehr machst ;) |
AW: Bilder komprimieren und speichern mit Threads
Danke für die Antwort.
Zitat:
Wenn ich das - bis jetzt - richtig verstanden habe, dann wäre mein Anfang schon falsch,
Delphi-Quellcode:
denn so lege ich ein neues TBitmap an und kopiere lediglich das Bild. Und da ich nicht nach dem Motto Trial-and-error arbeiten kann/wollte, habe ich das hier
Constructor TSaveThread.Create(Bitmap: TBitmap; FileName: String;
Compression: Integer; Resize: Boolean; DPI: Integer; Tolerance: Integer); Begin Inherited Create(True); FBitmap:=TBitmap.Create; // <- Blödsinn??? FBitmap.Assign(Bitmap); // <- klappt IMHO nur nach .Create ... End; Procedure TSaveThread.Execute; Begin ... FBitmap.Free; End;
Delphi-Quellcode:
noch nicht probiert. Es ist auch ein bisschen schwierig für mich. Normalerweise mache ich das schon einmal mit Versuch und Irrtum. Hier hält mich aber davon ab, dass ich den umfangreichen Code nicht zu Testzwecken aus dem Programm herausschreiben kann und ich für jeden Test zig Blätter einscannen muss :lol:
FBitmap:=Bitmap;
Gruß, Alex |
AW: Bilder komprimieren und speichern mit Threads
Okay, ich versuch das mal:
Also: Solange man nur aus einem Thread auf ein Bitmap zugreift kann eigentlich nichts schiefgehen. Du kannst also im Hauptthread das Bitmap erstellen und befüllen. Dann erstellst du einen Thread (Modus Suspended um sicher zu gehen) und übergibst dem Thread das TBitmap-Objekt. Der Thread "übernimmt" das Objekt (ja, mit FBitmap:=Bitmap;) und gibt es am Ende auch frei. Danach kannst du im Hauptthread die Bitmapvariable auf nil setzen (um auch ja keine Sachen mehr damit zu machen) und den Thread anschließend loslaufen lassen. Das müsste gehen weil ja immer nur ein Thread zugreift. Ich weis nur nicht genau, ob TBitmap zu der (nicht thread-safe) VCL gehört. (Eigentlich wird ja nichts angezeigt...) Laut dieser Diskussion: ![]() |
AW: Bilder komprimieren und speichern mit Threads
Zitat:
|
AW: Bilder komprimieren und speichern mit Threads
Zitat:
Delphi-Quellcode:
Mit dieser Procedure kommt das Bild an. Image wird automatisch verworfen/überschrieben oder sonstwas, wenn die Procedure wieder beendet wird. Ich starte meinen Thread genau dort und übergebe das TBitmap.
Procedure TScan.TwainTwainAcquire(Sender: TObject;
Const Index: Integer; Image: TBitmap; Var Cancel: Boolean); Begin End; Ich habe leider keinen Scanner hier, werde es aber morgen dann mal probieren. Danke, Alex |
AW: Bilder komprimieren und speichern mit Threads
@Bummi: Wie sinnfrei ist das denn? Also, gut möglich, aber wenn die einzige Referenz im Thread liegt, warum sollte ich dann noch was locken müssen? Klingt für mich nach einer ziemlichen VCL-Nachlässigkeit bzw. Konzeptfehler. Ein Grund mehr, warum ich sowohl in Delphi als auch .NET praktisch grundsätzlich mit eignen Bitmapklassen arbeite, die ihre Daten in hübschen eindimensionalen, performaten Arrays halten. Der ganze Overhead in den diversen Grafikroutinen geht mir langsam auf die Nüsse... aber das nur so nebenbei, und ist ja nicht dein Verschulden =)
Was ich gern mache, ist einem Thread eine List<TAbzuarbeitendeObjektklasse> zu verpassen. Im Thread dann:
Delphi-Quellcode:
und im Hauptthread dann
procedure Execute;
var ref: TAbgearbeiteteObjektklasse; begin repeat if MyTaskList.Count>0 do begin ref := PerformTask(MyTaskList[0]); MyTaskList.Delete(0); SendMessage(hwndMyMainForm, WM_MYOBJECT_READY, Integer(ref), 0); // falls nötig, dass der MainThread die Fertigstellung mitbekommen muss // wobei ddas nicht ganz sauber ist, der MainThread muss dann gesichert "ref" verarbeitet oder kopiert haben, bevor das neue "ref" fertig ist. // Da ggf. eine weitere Queue nehmen, oder mit Flags arbeiten. end; Sleep(1); until Terminated; end;
Delphi-Quellcode:
Und wenn die zu verarbeitenden Objekte selbst aus einem Thread stammen, bekommt der einen Callback, der o.g. Prozedur auslöst, was wahlweise auch per Windows Message hübsch sein kann. Dadurch gibts dann einen Thread, der ständig auf neue Inputs wartet, und wenn etwas kommt es fix durchrattert, so schnell wie es eben geht, und dann einfach mal fertig damit ist. Die Ergebnisse aus dem Verarbeitungsthread könnten dann ggf. in gleicher Weise an einen Speicher- oder Netzwerk-Thread übergeben werden, der dann entsprechend weiter macht. Im Grunde einfaches Queuing nach FIFO Prinzip. Macht sich für so Arbeitsthreads ganz nett.
procedure MyNewDataHandler(aObjectToHandle: TAbzuarbeitendeObjektKlasse);
begin MyThread.MyTaskList.Add(aObjectToHandle); end; \\Edit: Hupps, Code war LIFO, sollte FIFO sein :> \\Edit2: Natürlich kann man PerformTask() wieder einen neuen Thread auslösen lassen, wodurch man je nach Struktur noch mehr von Multicores profitieren würde, wenn die eigentliche Abarbeitung single-threaded ist. Seit Multicores so langsam Standard sind, nutz ich solche Modelle zumindest ausschweifend =). Dann ändert sich aber natürlich die Stelle fürs Benachrichtigen des Hauptthreads. \\Edit3: Schleife geändert... zu viel C# in letzter Zeit, ich bring alles durcheinander =) |
AW: Bilder komprimieren und speichern mit Threads
Zitat:
|
AW: Bilder komprimieren und speichern mit Threads
Aha okay! Wenn mehrere Threads auf den Canvas zugreifen, dann ist's ja sonnenklar. Dann gehört das auch so :)
|
AW: Bilder komprimieren und speichern mit Threads
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für Eure vielen Hinweise!
Ich habe mal angefangen und das Ergebnis sieht schon ganz gut aus. Auf mehrere Threads habe ich verzichtet, da wir hier in absehbarer Zeit sowieso vermutlich nie Rechner mit mehreren Kernen haben werden und es so einfacher zu strukturieren war. Ich habe meinen Code mal als Datei angehängt und würde mich freuen, wenn jemand noch gute Hinweise hat. Canvas muss erwartungsgemäß nicht gelocked werden. Mir geht es insbesondere um das Aufräumen. Ich wollte mich nicht um den Thread kümmern müssen und habe daher
Delphi-Quellcode:
gesetzt. Aus diesem Grund muss ich
FreeOnTerminate:=True;
Delphi-Quellcode:
am Ende von
TObjectList
Delphi-Quellcode:
freigeben, weil der Destructor so nicht aufgerufen wird, in dem ich das sonst getan hätte.
TThread.Execute;
Ferner bin ich mir nicht sicher, ob ich innerhalb des Thread für das Hinzufügen bzw. Löschen von Bildern mit einer CriticalSection arbeiten muss/sollte. Es wäre ja sonst denkbar, dass in exakt demselben Moment mit nicht absehbaren Folgen ein Bild hinzugefügt und ein anderes gelöscht wird. Den Code habe ich mit FastMM geprüft. Speicherlecks wurden keine angezeigt :-D Gruß, Alex |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:30 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz