Delphi-PRAXiS
Seite 1 von 5  1 23     Letzte » 

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung (https://www.delphipraxis.net/205798-24-bit-bitmap-um-90-grad-drehen-resourcen-optimierung.html)

Harry Stahl 18. Okt 2020 13:15

24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Das Thema hatten wir hier zwar schon öfter, aber nicht unter dem Gesichtspunkt, den ich hier ansprechen möchte:

Ich nutze zum drehen von 24 bzw. 32 Bitmaps bislang immer die folgende Routine (aus dem Doberenz-Buch):

Delphi-Quellcode:
procedure Drehe90Rechts (bm: TBitmap);
type
  TMyHelp = array [0..0] of TRGBQuad;
var
  P, Pend : PRGBQuad; x, y, b, h: Integer; RowOut: ^TMyHelp; help: TBitmap;
begin
  Bm.pixelformat := pf32bit;

  help := TBitmap.create;
  help.pixelformat := pf32bit;

  b := bm.height;
  h := bm.width;

  help.SetSize (b, h);
  PEnd := Bm.scanline[bm.height-1];

  for y := 0 to h-1 do begin

    rowout := help.scanline [y];
    p := PEnd;
    inc (p, y);

    for x := 0 to (b-1) do begin
      rowout [x] := p^;
      inc (p, h);
    end;

  end;

  bm.Assign (help);
  help.free;
end;
Ich dachte, OK die nehmen auch für eine 24-Bit-Bitmap eine 32-Bitmap, weil der Compiler die 32-Bits in einem Register direkt kopieren kann. Wenn man sich das in der CPU ansieht, dann stimmt das wohl auch, bei 24 muss er wohl erst 16-Bit und dann noch mal 8 kopieren, dadurch 2 Befehle mehr (siehe anliegenden Screenshot).

Nur: Bei sehr großen Bilder (teste gerade eins im Format 21600 x 10800) braucht er, aber um die 32-Bit-Hilfsbit anzulegen, statt 660 MB bei einer 24 Bit, bei der 32-Bit Bitmap ca. 1 GB zusätzlichen Arbeitsspeicher.

Daher meine Überlegung, statt die Routine mit 32-Bit zu verwenden, bei einer 24-Bit es mit einer 24-Bit routine zu machen.

Delphi-Quellcode:
procedure Drehe90Rechts24 (bm: TBitmap);
type
  TMyHelp = array [0..0] of TRGBTriple;
var
  P, Pend : PRGBTriple; x, y, b, h: Integer; RowOut: ^TMyHelp; help: TBitmap;
begin
  help := TBitmap.create;
  help.pixelformat := pf24bit;

  b := bm.height;
  h := bm.width;

  help.SetSize (b, h);
  PEnd := Bm.scanline[bm.height-1];

  for y := 0 to h-1 do begin

    rowout := help.scanline [y];
    p := PEnd;
    inc (p, y);

    for x := 0 to b-1 do begin
      rowout [x] := p^;
      inc (p, h);
    end;

  end;

  bm.Assign (help);
  help.free;
end;

Leider funktioniert die nicht. Komme leider nicht dahinter warum (es fehlen 1-2 Pixel) und bei Bildgrößen mit ungeraden werten geht es nach dem 2. Drehen ganz schief.

Hat einer eine Idee, wie man es richtig machen könnte?

Gausi 18. Okt 2020 13:50

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Vor Ewigkeiten habe ich Bitmaps mal per Hand geparsed und verarbeitet, und bin dabei auf die Eigenart gestoßen, dass jede Zeile in einer Bitmap-Datei ein Vielfaches von 4 Bytes enthalten muss. Bei 32 Bit ist das automatisch der Fall, bei 24Bit hat man ggf. bis zu 3 Füllbytes am Ende jeder Zeile.

Könnte das die Ursache deines Problems sein?

Harry Stahl 18. Okt 2020 14:26

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Da hatte ich auch schon dran gedacht, aber keine Idee, wie ich das im Code berücksichtigen sollte...

Harry Stahl 18. Okt 2020 16:15

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Habe das mit den Füllbytes noch mal aufgegriffen und mache die Höhe oder Breite passend.

Von einem Test-Bitmap mit Breite = 2348 und Höhe= 3181 wird dann aus der Höhe 3184. Wenn ich nun die Bytes des alten Bitmaps in die vergrößerte Bitmap kopiere, wird alles kopiert und ich kann das Bitmap danach auch weiter drehen.

Allerdings: Ich habe eine um 3 Pixel vergrößerte Bitmap. Wenn ich die dann wieder kürze, gibt es aber beim nächsten Mal drehen ein Problem mit der Anzeige.

Wie macht man das wohl richtig?

Delphi-Quellcode:
procedure Drehe90Rechts24 (bm: TBitmap);
type
  TMyHelp = array [0..0] of TRGBTriple;
var
  P, Pend : PRGBTriple; x, y, b, h, oldw, oldh: Integer; RowOut: ^TMyHelp; help: TBitmap;
begin
  help := TBitmap.create;
  help.pixelformat := pf24bit;

  oldw := bm.Width;
  oldh := bm.Height;

  b := bm.height;
  h := bm.width;

  if bm.Width mod 4 <> 0 then
     h := ((bm.Width div 4) * 4) + 4;
   if bm.Height mod 4 <> 0 then
     b := ((bm.Height div 4) * 4) + 4;

  help.SetSize (b, h);
  PEnd := Bm.scanline[bm.height-1];

  for y := 0 to bm.width-1 do begin

    rowout := help.scanline [y];
    p := PEnd;
    inc (p, y);

    for x := 0 to bm.height-1 do begin
      rowout [x] := p^;
      inc (p, bm.Width);
    end;

  end;

  bm.Assign (help);
  //bm.SetSize(oldh, oldw);
  //BitBlt(bm.canvas.handle, 0,0, bm.Width, bm.Height, help.canvas.handle, 0,0, SRCCopy); // funktioniert nur beim 1. drehen
  help.free;
end;

venice2 18. Okt 2020 17:02

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Ich kenne solche Probleme nicht.
Liegt wohl daran das ich GDI+ verwende
Delphi-Quellcode:
GdipImageRotateFlip(img, Rotate90FlipNone);

Amateurprofi 18. Okt 2020 17:18

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Zitat:

Zitat von Harry Stahl (Beitrag 1475674)
Das Thema hatten wir hier zwar schon öfter, aber nicht unter dem Gesichtspunkt, den ich hier ansprechen möchte:

Ich nutze zum drehen von 24 bzw. 32 Bitmaps bislang immer die folgende Routine (aus dem Doberenz-Buch):

Delphi-Quellcode:
procedure Drehe90Rechts (bm: TBitmap);
type
  TMyHelp = array [0..0] of TRGBQuad;
var
  P, Pend : PRGBQuad; x, y, b, h: Integer; RowOut: ^TMyHelp; help: TBitmap;
begin
  Bm.pixelformat := pf32bit;

  help := TBitmap.create;
  help.pixelformat := pf32bit;

  b := bm.height;
  h := bm.width;

  help.SetSize (b, h);
  PEnd := Bm.scanline[bm.height-1];

  for y := 0 to h-1 do begin

    rowout := help.scanline [y];
    p := PEnd;
    inc (p, y);

    for x := 0 to (b-1) do begin
      rowout [x] := p^;
      inc (p, h);
    end;

  end;

  bm.Assign (help);
  help.free;
end;
Ich dachte, OK die nehmen auch für eine 24-Bit-Bitmap eine 32-Bitmap, weil der Compiler die 32-Bits in einem Register direkt kopieren kann. Wenn man sich das in der CPU ansieht, dann stimmt das wohl auch, bei 24 muss er wohl erst 16-Bit und dann noch mal 8 kopieren, dadurch 2 Befehle mehr (siehe anliegenden Screenshot).

Nur: Bei sehr großen Bilder (teste gerade eins im Format 21600 x 10800) braucht er, aber um die 32-Bit-Hilfsbit anzulegen, statt 660 MB bei einer 24 Bit, bei der 32-Bit Bitmap ca. 1 GB zusätzlichen Arbeitsspeicher.

Daher meine Überlegung, statt die Routine mit 32-Bit zu verwenden, bei einer 24-Bit es mit einer 24-Bit routine zu machen.

Delphi-Quellcode:
procedure Drehe90Rechts24 (bm: TBitmap);
type
  TMyHelp = array [0..0] of TRGBTriple;
var
  P, Pend : PRGBTriple; x, y, b, h: Integer; RowOut: ^TMyHelp; help: TBitmap;
begin
  help := TBitmap.create;
  help.pixelformat := pf24bit;

  b := bm.height;
  h := bm.width;

  help.SetSize (b, h);
  PEnd := Bm.scanline[bm.height-1];

  for y := 0 to h-1 do begin

    rowout := help.scanline [y];
    p := PEnd;
    inc (p, y);

    for x := 0 to b-1 do begin
      rowout [x] := p^;
      inc (p, h);
    end;

  end;

  bm.Assign (help);
  help.free;
end;

Leider funktioniert die nicht. Komme leider nicht dahinter warum (es fehlen 1-2 Pixel) und bei Bildgrößen mit ungeraden werten geht es nach dem 2. Drehen ganz schief.

Hat einer eine Idee, wie man es richtig machen könnte?

Zu Drehe90Rechts24 (bm: TBitmap);


Vor der ersten Schleife wird PEnd auf das erste Pixel der letzten Zeile in BM gestellt
In der Y Schleife wird
1) P = PEnd gesetzt, also auf Pixel[0,letzte Zeile]
2) P um Y erhöht, also auf Pixel[Y,letzte Zeile gestellt]
In der X Schleife wird
1) Pixel P^ nach Help kopiert
2) P um H Pixel erhöht.
Das soll P auf die jeweils nächste Zeile in BM stellen.
Das funktioniert bei 32 Bit-Bitmaps, aber nicht bei 24 Bit, weil hier
der Offset zur nächsten Zeile (in Bytes) nicht Breite*3 ist, sondern immer ein
vielfaches von 4 ist. (Breite*3+3) div 4*4 sollte die Anzahl Bytes je Zeile ergeben.
Workaround:
var Offs:NativeInt;
1) Vor der Y Schleife
PEnd := Bm.scanline[bm.height-1];
Offs := NativeInt(Bm.scanline[bm.height-2])-NativeInt(PEnd);
2) In der X Schleife
Inc (NativeInt(p), Offs); // statt inc (p, h);

Harry Stahl 18. Okt 2020 17:49

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Danke, Du hast mir den Tag gerettet :thumb:, da knabber ich schon ein paar Stunden dran rum...

Super, so braucht die Drehung "nur" zusätzlichen Arbeitsspeicher von 699 MB, statt 933 MB.
Erstaunlicherweise ist der Vorgang auch etwas schneller, als mit der 32-Bit-Variante (gemessen in der 64-Bit-Programm-Variante meines Programms). Wird wahrscheinlich durch die krass hohe Anzahl der Bytes, die weniger kopiert werden müssen (ca. 300 Mio) bewirkt...

Harry Stahl 18. Okt 2020 17:52

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Zitat:

Zitat von venice2 (Beitrag 1475680)
Ich kenne solche Probleme nicht.
Liegt wohl daran das ich GDI+ verwende
Delphi-Quellcode:
GdipImageRotateFlip(img, Rotate90FlipNone);

Ist GDI+ denn wirklich schneller?

Hatte mal in der Vergangenheit ein zwei Sachen ausprobiert und war enttäuscht... Schließlich muss man ja immer erst diesen GDI-Kontext erzeugen und das Bild übergeben (was ja wohl auch einiges an Arbeitsspeicher braucht). Wie verhält sich GDI bei so großen Bildern (also die anfangs erwähnten 21600x10800)?

striderx 18. Okt 2020 19:14

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Zitat:

Zitat von Harry Stahl (Beitrag 1475683)
Ist GDI+ denn wirklich schneller?

Wie verhält sich GDI bei so großen Bildern (also die anfangs erwähnten 21600x10800)?

Schneller kann ich nicht sagen aber m. E. schon recht schnell. Allerdings gibt es für RotateFlip eine Größenbeschränkung. die irgendwo bei 65 Mio Pixel (bei einer 24 Bit-Bitmap) liegt; darüber ist kaum etwas im Netz zu finden. Nett ist auch, dass dann kein Fehler erzeugt wird und einfach nichts passiert.

Das kann man aber wie folgt umgehen:

Delphi-Quellcode:
function GDIPRotateFlipBitmap(ABitmap: tBitmap; Mode: RotateFlipType): Boolean;

var
  BM:        tBitmap;
  GR:        tGPGraphics;
  AGPBitmap: tGPBitmap;
  AStatus:   Status;

begin
  if Mode = RotateNoneFlipNone then begin
     Result := True;
     Exit;
  end;
 
  BM := tBitmap.Create;                                                        
  BM.Assign(ABitmap);                                                          
  AGPBitmap := tGPBitmap.Create(BM.Handle, 0);                                
 
  AGPBitmap.RotateFlip(Mode);
  GR := tGPGraphics.Create(ABitmap.Canvas.Handle);
  GR.DrawImage(AGPBitmap, 0, 0);
  AStatus := GR.GetLastStatus;
 
  Result := (AStatus = OK);
 
  AGPBitmap.Free;
  BM.Free;
  GR.Free;
end;
Wie sich das jetzt auf die Geschwindigtkeit auswirkt, muss Du dann mal ausprobieren.

Harry Stahl 18. Okt 2020 20:39

AW: 24-Bit Bitmap um 90 grad drehen - Resourcen-Optimierung
 
Ich habe in meinem Programm eine Performance-Test Anzeige eingebaut (die man aktivieren muss) und habe Deine Funktion mal im Vergleich getestet:

Beide Funktionen sind in etwa gleich schnell. Wobei die GDI-Funktionen offensichtlich nur mit 32-Bit-Dateien funktionieren, hier muss ich also Datei erst in 32-Bit umwandeln, verbrauche dafür also schon mal mehr Arbeitsspeicher. Im Vergleich zu der 24-Bit-Drehung ist "meine" Funktion bei 660 Mio. Pixeln ca. 500 ms schneller (insgesamt hier auf dem PC ca. 6 Sekunden).

Aber man kann (zumindest mit der 64-Bit-Version) auch die 660 bzw. über 900 Mio. Pixel mit der GDI-Funktion benutzen, hier konnte ich keine Einschränkung feststellen.

Ich werde allerdings die Drehfunktion noch (wenn das geht) in eine Funktion mit Workerthreads umgestalten, wo jede CPU einen Teilbereich bearbeitet.

Das bringt echt noch mal Speed: Für die Funktion zur Änderung der Helligkeit habe ich das schon mal gemacht, während die Funktion für das oben erwähnte große Bild bei einer CPU z.B. 1,6 Sekunden braucht, um die Helligkeit zu ändern, geht es mit den Workerthreads (mit TTask) (ja nach Anzahl der CPUS) mit 6 CPUS z.B. in 300 MS. Das ist schon ein spürbarer Unterschied....


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:44 Uhr.
Seite 1 von 5  1 23     Letzte » 

Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf