Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)? (https://www.delphipraxis.net/188328-pruefung-eines-bitmaps-auf-transparenz-gehts-noch-schneller.html)

Harry Stahl 21. Feb 2016 18:04

Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Derzeit verwende ich folgende Methode, um ein 32-bit-Bitmap auf Vorhandensein eines (Teil-) Transparenten Pixels zu prüfen:

Delphi-Quellcode:
function HasTransparentRGBAValues (const bm:TBitmap): Boolean;
var
  x, z: Integer; RGBA: pRGBALine;
begin
  Result := FALSE;
  RGBA := bm.Scanline[bm.Height-1];
  z := bm.Width * bm.Height;

  for x := 0 to z-1 do begin
    if RGBA^[x].rgbReserved <> 255 then begin
      EXIT (TRUE);
    end;
  end;
end;
Bei einem Nicht transparentem Bild in den Ausmaßen 4244x2819 Pixel benötigt die Routine hier auf meinem Rechner ca. 80 MS um das ganze Bitmap zu prüfen, was ziemlich viel Zeit ist.

Kennt jemand da evtl. eine noch schnellere Methode als die oben dargestellte?

Delphi-Laie 21. Feb 2016 18:08

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Harry Stahl (Beitrag 1330912)
Delphi-Quellcode:
function HasTransparentRGBAValues (const bm:TBitmap): Boolean;
var
  x, z: Integer; RGBA: pRGBALine;
begin
  Result := FALSE;
  RGBA := bm.Scanline[bm.Height-1];
  z := bm.Width * bm.Height;

  for x := 0 to z-1 do begin
    if RGBA^[x].rgbReserved <> 255 then begin
      EXIT (TRUE);
    end;
  end;
end;

Warum hat die Funktion nie die Möglichkeit, true zurückzuliefern?

Edit: OK, das wird wohl im Exitparameter realisiert - diese Möglichkeit war mir neu.

Der schöne Günther 21. Feb 2016 18:50

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Parallelisieren?

Harry Stahl 21. Feb 2016 19:36

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1330914)
Parallelisieren?

Bin mir da nicht so sicher, wegen des Overheads. Hatte ich zwar schon mal gemacht, allerdings zeigt mir AQtime seltsame Messwerte an (0,3 MS, was nicht sein kann) und bei einem Extra-Test-Projekt schwanken die einzelnen Testergebnisse zwischen 28 und 56 MS:

Delphi-Quellcode:
type
  TRGB32 = packed record
    B, G, R, A: Byte;
  end;

  TRGB32Array = packed array[0..MaxInt div SizeOf(TRGB32)-1] of TRGB32;
  PRGB32Array = ^TRGB32Array;

function HasTransparentRGBAValuesx (const bm:TBitmap): Boolean;
var
  LineLen: Integer; found: boolean;
  FirstLine : PRGB32Array;
begin
  Found := false;
  if bm.Width = 0 then exit;

  FirstLine := bm.ScanLine[0];
  LineLen := - bm.width;

  TParallel.For (0, bm.Height-1, procedure (y:Integer; S: TParallel.TLoopState)
  var
    x, p: Integer;
  begin
    if not s.ShouldExit then begin
      P := y*LineLen;
      for x := 0 to bm.Width-1 do begin
        if FirstLine[P+x].A <> 255 then begin
          found := true;
          s.Break;
          break;
        end;
      end;
    end;
  end);

  Result := found;
end;
Insofern wäre mir hier eine Variante ohne Parallelisierung mit eindeutig besseren Ergebnissen lieber:wink:

Luckie 21. Feb 2016 19:38

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Bitmap komplett in den Speicher laden, damit man keine Festplattenzugriffe hat und dann mit zwei Threads von vorne und hinten prüfen?

jaenicke 21. Feb 2016 20:00

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Ich hätte schon Ideen, aber dafür wäre ein Beispielbild sehr gut. Ohne Photoshop kenne ich auf Anhieb kein Tool, mit dem ich einer Bitmap einen Alphakanal hinzufügen könnte.

Uwe Raabe 21. Feb 2016 20:56

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Das
Delphi-Quellcode:
TParallel.For
über die 2819 Zeilen verursacht wahrscheinlich mehr Overhead als die Parallelisierung wettmachen kann. Wenn das ganze Bild ca. 80 ms braucht, dann sind das pro Zeile (also pro Iteration) ca. 0,03 ms. Das ist ein ganz schlechtes Verhältnis. Besser auf deutlich weniger Durchläufe beschränken und jede Iteration über ca. 100-500 Zeilen laufen lassen. Das sind dann pro Task ca. 3-15ms. Mit genügend freien Kernen sollte das merkbar sein.

Harry Stahl 21. Feb 2016 21:15

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von jaenicke (Beitrag 1330921)
Ich hätte schon Ideen, aber dafür wäre ein Beispielbild sehr gut. Ohne Photoshop kenne ich auf Anhieb kein Tool, mit dem ich einer Bitmap einen Alphakanal hinzufügen könnte.

Na, sowas sollte ja wohl jedes Grafikbearbeitungsprogramm drauf haben (in meinem Programm - PixPower - wählt man dazu einfach im Dropdown-Schalter "Farbe" unter Menü "Farbtiefe" 32 Bit, siehe anliegenden Screenshot).

Photoshop braucht man dafür wirklich nicht.:wink:

Aber wie auch immer, habe mal die Demo so verändert, dass die Grafik zur Laufzeit erzeugt wird (will hier keine 45 MB-Grafikdatei beifügen).

Mit den Schaltern Standard und Parallel kann man die Geschwindigkeit testen, wenn keine Transparenz vorhanden ist. Mit dem Schalter "SetTransparency" macht man eine Zeile transparent und kann dann die Routinen noch mal ausführen.

Projekt anliegend.

Harry Stahl 21. Feb 2016 21:29

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1330924)
Das
Delphi-Quellcode:
TParallel.For
über die 2819 Zeilen verursacht wahrscheinlich mehr Overhead als die Parallelisierung wettmachen kann. Wenn das ganze Bild ca. 80 ms braucht, dann sind das pro Zeile (also pro Iteration) ca. 0,03 ms. Das ist ein ganz schlechtes Verhältnis. Besser auf deutlich weniger Durchläufe beschränken und jede Iteration über ca. 100-500 Zeilen laufen lassen. Das sind dann pro Task ca. 3-15ms. Mit genügend freien Kernen sollte das merkbar sein.

Ja, wäre eine mögliche Verbesserung. Doch damit steigt die Komplexität wieder an und es wird so ein unschönes unleserliches Ungetüm draus. Und ob es schneller ist?

Insofern wäre mir eine Standard-Lösung lieber, z.B. per ASM.

Kenne mich mit Assembler aber leider nicht so gut aus.
Aber hier muss ja letztlich nur über einen zusammenhängenden Speicherbereich, jeweils in Abständen von 4 Byte geprüft werden, ob das Byte dort <> 255 ist.

Nichts anderes macht ja im Prinzip meine Standard-Methode. Aber gibt es da nicht so einen Super ASM-Befehl, der das in einem Rutsch ohne Iteration erledigen kann?

BUG 21. Feb 2016 21:35

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Das mit der gröberen Aufteilung sollte man probieren.
Eventuell könnte man mit den Vektoreinheiten (SSE) noch was heraushole. Das wird dann aber haariger (kA. wie weit Delphi von alleine vektorisiert).

Edit:
Zitat:

Zitat von Harry Stahl (Beitrag 1330928)
Doch damit steigt die Komplexität wieder an und es wird so ein unschönes unleserliches Ungetüm draus. Und ob es schneller ist?[...] Insofern wäre mir eine Standard-Lösung lieber, z.B. per ASM.

Frei nach Herb Sutter "Free lunch is over". Parallelisieren ist die Standard-Lösung.
Und dein Problem ist wirklich noch einfach zu parallelisieren :wink:

Harry Stahl 21. Feb 2016 21:55

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Oder gibt es was in der Grafik-32 Bibliothek?

Oder DirectDraw? Irgendeine Lösung, welche die schnellere GPU nutzt, wäre natürlich auch willkommen.

jaenicke 21. Feb 2016 22:46

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Die Zeit sind hier 40 Millisekunden. Davon entfallen ca. 2 Millisekunden auf das Scanline und den Teil außerhalb der Schleife, 24 Millisekunden rein auf die Schleife, 8 Millisekunden auf den Vergleich und 6 Millisekunden auf den Zugriff auf den aktuellen Wert in der Schleife.

Sonderlich viel Optmierungspotential sehe ich dort deshalb leider nicht. Ich habe auch den Assemblercode angeschaut, der ist schon durch den Compiler gut optimiert. Auch ein einfaches Verschieben des Pointers auf das vierte Byte und dann jeweils um 4 Byte hat nichts gebracht, das war meine Idee. :(

Ob die GPU hier helfen kann, bin ich nicht so sicher, die ist eigentlich eher bei Fließkommaoperationen sehr schnell.

Die Lösung mit TParallel ist bei mir aber schon deutlich schneller als die Standardvariante. Allerdings erst ab dem zweiten Durchlauf, dann braucht es aber konstant etwas weniger als die halbe Zeit.

noisy_master 22. Feb 2016 07:00

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Moin,

eine Winzigkeit fällt mir noch ein, um das Ganze "minimal " schneller zu bekommen: dreh die Schleife um und lass alle Berechnungen daraus weg:

dec(z);
for x := z downto 0 do begin


Der Vergleich auf 0 ist "einfacher" und damit schneller als der Vergleich auf einen festen (berechneten) Wert.

Dürfte zwar nicht DER Bringer sein, aber wenn's auch nur ein Wenig hilft..

Gruß
Dirk

Neutral General 22. Feb 2016 07:31

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von noisy_master (Beitrag 1330936)
eine Winzigkeit fällt mir noch ein, um das Ganze "minimal " schneller zu bekommen: dreh die Schleife um und lass alle Berechnungen daraus weg:

Delphi optimiert das normalerweise (wenn möglich) von selbst beim compilieren.

himitsu 22. Feb 2016 09:20

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Aber gibt es da nicht so einen Super ASM-Befehl, der das in einem Rutsch ohne Iteration erledigen kann?
Nein, auch da sind schleifen nötig.

Delphi speichert den "Endwert", also 2 Variablen, was bei fester 0 nicht nötig ist, zusätzlich zum
Delphi-Quellcode:
DEC x; JZ p;
, statt einem
Delphi-Quellcode:
DEC x; CMP x, z; JE p;
, als Abbruchbedingung (oder so in etwa)
Delphi-Quellcode:
IstUngleich0 := False;
for x := z - 1 downto 0 do begin
  if PixelArrayPointerDerAktuellenZeileInPByte[x * 4 + 3] <> 0 then begin // das + 3 besser vorher drauf rechnen
    IstUngleich0 := True;
    Break;
  end;
Einzelbytezugriffe sind eh schlechter, also ...
Delphi-Quellcode:
IstUngleich0 := False;
for x := z - 1 downto 0 do begin
  if PixelArrayPointerDerAktuellenZeileInPLongWordOderTColor[x] and $ff000000 <> 0 then begin
    IstUngleich0 := True;
    Break;
  end;
geht nur bei gerade Pixelanzahl
Delphi-Quellcode:
IstUngleich0 := False;
for x := z div 2 - 1 downto 0 do begin
  if PixelArrayPointerDerAktuellenZeileInPLargeWord[x] and $ff000000ff000000 <> 0 then begin
    IstUngleich0 := True;
    Break;
  end;
Bei einem 32-Bit-Bitmap kann man auch alle Zeilen zusammen in dieser einen Schleife behandeln, da die Zeilen im Speicher hintereinander liegen, ohne ein Offset/Align.

Oder ohne Offsetberechnungen mit einer For/While-Schleife, von PointerErstesPixel bis PointerLetztesPixel.

frankyboy1974 22. Feb 2016 09:59

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
hallo,

ich kenne jetzt ja die fachlichen Anforderungen nicht genau, aber man könnte natürlich auch sagen, wer würde bei einem Bitmap von circa 10 Mio. Pixeln schon ein einziges Pixel auf transparent setzen. Oder anders formuliert, muss du wirklich jedes Pixel auf Transparenz überprüfen, oder würde es nicht reichen 1 Pixel zu überprüfen und dann ein gewisse Anzahl zu überspringen, bis du das nächste Pixel überprüfst.:wink:

Ansonsten würde ich auch die Variante empfehlen, dass du je nach Anzahl der verfügbaren Computerkerne, eine ensprechende Anzahl parallele Handungsabläufe erzeugst.


mfg

Neutral General 22. Feb 2016 10:11

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Hallo,

Ich habe mal ne (Teil-)Assemblerversion gebaut:

Delphi-Quellcode:
function HasTransparentRGBAValues(const bmp: TBitmap): Boolean;
label schleife, ende, hasTrans;
var z: Integer; RGBA: PRGBQuad;
begin
  Result := false;
  RGBA := bmp.Scanline[bmp.Height-1];
  z := bmp.Width * bmp.Height;
  asm
    mov esi, rgba
    mov ecx, z
schleife:
    lodsd
    shr eax, 24
    inc al
    jnz hasTrans
    loop schleife
    jmp ende
hasTrans:
    mov result, true
ende:
  end;
end;
Ich habs nicht mit anderen Lösungen verglichen. Müsste man mal ausprobieren.
Ist in der Form nicht für 64-Bit geeignet, das wäre ein Stück mehr Arbeit.

Der schöne Günther 22. Feb 2016 10:20

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Wenn wir jetzt schon den Assembler rausholen, sollten wir dann nicht wenigstens alle die gleiche(n) Beispieldatei(en) haben um die Ergebnisse überhaupt vergleichen zu können? Harry, lad doch mal ein, zwei typische Bilder hoch bei denen du das prüfen möchtest.

Uwe Raabe 22. Feb 2016 10:21

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Neutral General (Beitrag 1330953)
Ich habe mal ne (Teil-)Assemblerversion gebaut:

Das gibt einen ganz ulkigen Effekt auf meinem System, wenn man das Beispielprojekt mit DEBUG oder RELEASE compiliert:

Standard DEBUG: 23-24 ms
Assembler DEBUG: 20-21 ms
Standard RELEASE: 7-8 ms
Assembler RELEASE: 23-24 ms

Spricht irgendwie gegen Assembler...

Neutral General 22. Feb 2016 10:27

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Wow, da würde mich aber mal interessieren wie der generierte Assemblercode im Release aussieht :shock:
Hab hier grad nur Delphi 7 und mein Assemblercode hat deutlich weniger Anweisungen und kein cmp - Hätte da zumindest keinen so großen Vorsprung erwartet (wenn ich schon verliere :mrgreen: )

Uwe Raabe 22. Feb 2016 11:04

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Der Effekt tritt schon auf, wenn man in den Compileroptionen die Optimierung aktiviert (und natürlich ein Rebuild des Projekts macht). Interessanterweise ist die ASM-Variante mit aktiver Optimierung offenbar langsamer also ohne.

Code:
005C61C7 8BC7             mov eax,edi
Unit13.pas.143: for x := 0 to z-1 do begin
005C61C9 48               dec eax
005C61CA 85C0             test eax,eax
005C61CC 7C14             jl $005c61e2
005C61CE 40               inc eax
005C61CF 33D2             xor edx,edx
Unit13.pas.144: if RGBA[x].rgbReserved <> 255 then begin
005C61D1 807C9303FF      cmp byte ptr [ebx+edx*4+$03],$ff
005C61D6 7406             jz $005c61de
Unit13.pas.145: EXIT (TRUE);
005C61D8 C645FF01         mov byte ptr [ebp-$01],$01
005C61DC EB04             jmp $005c61e2
Unit13.pas.147: end;
005C61DE 42               inc edx
Unit13.pas.143: for x := 0 to z-1 do begin
005C61DF 48               dec eax
005C61E0 75EF            jnz $005c61d1

Namenloser 22. Feb 2016 11:33

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Assembler-Befehle wie LOOP sollte man heutzutage nicht mehr verwenden, die sind auf modernen CPUs ziemlich langsam. Vermutlich liegt es daran.

Wenn schon Assembler, dann wäre eher was mit SIMD interessant.

Ansonsten würde mir noch folgende reine Pascal-Alternative einfallen:

Delphi-Quellcode:
function HasTransparentRGBAValues (const bm:TBitmap): Boolean;
var
  x, z: Integer;
  pixel: PLongWord;
begin
  Result := FALSE;
  pixel := bm.Scanline[bm.Height-1];
  z := bm.Width * bm.Height;

  for x := 0 to z-1 do begin
  begin
    if (pixel^ and $FF000000) <> $FF000000 then
      EXIT (TRUE);
    inc(pixel);
  end;
end;
Statt auf den Pointer einen Offset von 3 Bytes hinzuzuaddieren, werden die 32 Bit einfach maskiert. Ob das schneller ist, keine Ahnung.

Uwe Raabe 22. Feb 2016 11:48

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Namenloser (Beitrag 1330966)
Ansonsten würde mir noch folgende reine Pascal-Alternative einfallen:

Mal abgesehen, daß die sich wegen eines zusätzlichen
Delphi-Quellcode:
begin
nicht compilieren lässt, ist sie zeitlich gleichauf mit der ursprünglichen Routine. Insofern ist bisher noch der einzige erfolgreiche Ansatz zur Optimierung das Aktivieren der Optimierung in den Compileroptionen.

Neutral General 22. Feb 2016 12:34

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Hab ne andere Idee:
Delphi-Quellcode:
function HasTransparentRGBAValues (const bm:TBitmap): Boolean;
var
  x, z: Integer;
  pixel: PRGBQuad;
  sum: Cardinal;
begin
  pixel := bm.Scanline[bm.Height-1];
  z := bm.Width * bm.Height;

  sum := 0;
  for x := 0 to z-1 do
  begin
    sum := sum + pixel^.rgbReserved;
    inc(pixel);
  end;

  Result := sum < (z * 255);
end;
Ob das schneller ist, ist natürlich die Frage weil die ganze Schleife durchlaufen muss.
Dafür spart man sich ggf. ne Menge Vergleiche. Ein Versuch ists Wert denke ich.

himitsu 22. Feb 2016 14:47

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Bei Assambler kann keine Codeoptimierung greifen. :stupid:

Harry Stahl 22. Feb 2016 16:21

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von frankyboy1974 (Beitrag 1330949)
hallo,

ich kenne jetzt ja die fachlichen Anforderungen nicht genau, aber man könnte natürlich auch sagen, wer würde bei einem Bitmap von circa 10 Mio. Pixeln schon ein einziges Pixel auf transparent setzen. Oder anders formuliert, muss du wirklich jedes Pixel auf Transparenz überprüfen, oder würde es nicht reichen 1 Pixel zu überprüfen und dann ein gewisse Anzahl zu überspringen, bis du das nächste Pixel überprüfst.:wink:

Ansonsten würde ich auch die Variante empfehlen, dass du je nach Anzahl der verfügbaren Computerkerne, eine ensprechende Anzahl parallele Handungsabläufe erzeugst.


mfg

Ja, es geht hier um Bilder, das aus mehreren Ebenen besteht (z.B. aus Photoshop bzw. meinem eigenen Dateiformat, siehe mal zur Demo anliegenden Screenshot). Da muss ich halt prüfen, ob ein Ebenenbild ein oder mehrere Pixel mit Transparenz hat, denn das muss ich dann mit dem darunterliegenden verrechnen (ohne Transparenz kann man es einfach drüberschreiben).

Da die Verrechnungsvariante auf jeden Fall deutlich länger dauert, als die Variante des Drüberkopierens, lohnt es sich, zunächst rauszufinden, ob das Bild eine Transparenz hat oder nicht.

Oft ist es bei Bildkompositionen so, dass die Hintergrundebene ohne Transparenz ist und die drüberliegenden Bilder mit Teiltransparenzen. I.d.R. ist dann auch das Hintergrundbild das größte Bild von allen und da muss ich dann halt eben einmal ganz durchprüfen, braucht aber relativ viel Zeit, die ich gerne noch etwas reduzieren möchte.

Neutral General 22. Feb 2016 16:26

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Hast du meinen letzten Vorschlag mal probiert?
Würde mich echt interessieren ob das schneller ist :)

Harry Stahl 22. Feb 2016 16:28

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hey, Danke erst mal an Alle, die sich hier an den Überlegungen beteiligt haben.

In der Tat muss man den Einwand von Uwe berücksichtigen, ob mit Debug oder Release kompiliert.

Da die Auslieferung des Programms natürlich in Release erfolgt, ist das letztlich der Maßstab (hatte mein Demo aber im Debug-Modus getestet).

Anliegend daher die hier aufgeführten Varianten einmal zum Testen eingebaut, die jeweilige Methode kann man aus der Drop-Downbox auswählen.

Die ASM-Variante ist die schnellste im Debug-Modus (ca. 32 MS, im Gegensatz zu meiner Standard-Methode hier mit ca. 50 Ms).

Im Release Modus bleibt die ASM-Variante bei 32 MS und meine Standard-Methode kommt auf ca. 24 MS.

Harry Stahl 22. Feb 2016 16:31

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Neutral General (Beitrag 1330998)
Hast du meinen letzten Vorschlag mal probiert?
Würde mich echt interessieren ob das schneller ist :)

Ja, Deine Nachfrage und meine Antwort haben sich hier gerade überschnitten.

Also im Release-Modus leider nicht schneller.

Wenn es da den SUPER-ASM-Befehl nicht gibt, müsste man sich vielleicht in den DirectX-Funktionen noch einmal umsehen, evtl. gibt es da etwas, was per GPU-Unterstützung diese Frage beantworten kann.

Blup 22. Feb 2016 16:59

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Hast du an andere Wege der Optimierung gedacht, wird die Information für jede Bildebene irgendwo gespeichert?

Harry Stahl 22. Feb 2016 18:58

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Blup (Beitrag 1331011)
Hast du an andere Wege der Optimierung gedacht, wird die Information für jede Bildebene irgendwo gespeichert?

Ja, für jede Ebene gibt es ein Ebenen-Objekt, wo verschiedene Informationen zu den Bildern der Ebene gespeichert werden.

Aber bei einem 32-Bitmap-Bild sich einmal zu merken, ob es (Teil-)transparente Pixel hat, bringt wahrscheinlich nicht soviel, denn man müsste bei jeder Änderung, die potentiell Transparenz erzeugen kann (Löschen, Einfügen, Filter, Verläufe, Malen mit unterschiedlicher Deckkraft, usw.) wieder jedes mal prüfen, ob das Bild weiterhin ohne Transparenz ist. Das ist aufwändig und bei der Kombination der unterschiedlichen Ebenen müsste auch wieder geprüft werden, ob irgendwo eine Transparenz eines Ebenenbildes eine Transparenz auch für das Ergebnisbild erzeugt.

Da rechne ich das lieber gleich einmal durch, wenn ich das Ebenenbild verwende.

Die ganze Berechnung und Verrechnung der Ebenen, auch in räumlicher Anordnung, Transparenzen, Filter und Masken und Einstellungsebenen etc. ist eh schon sehr komplex und stößt bei mir allmählich an die Grenze der logischen Verarbeitungsfähgikeit. Da bin ich eigentlich ganz froh über jede simple und schnelle Lösung, die mir hilft ohne eine weitere Erhöhung der Komplexität auszukommen.

Amateurprofi 23. Feb 2016 00:18

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
@Harry:
Versuche mal das:

Code:
FUNCTION IsPartlyTransparentAsm(P0,P1:Pointer; W,H:NativeInt):Boolean;
asm
               sub     edx,eax
               js      @BottomUp
               imul    edx,H
               add     eax,edx
@BottomUp:    lea     eax,[eax+ecx*4+3]
               imul    ecx,H
               neg     ecx
@Loop:        cmp     byte[eax+ecx*4],$FF
               jne     @True
               add     ecx,1
               jl      @Loop
               xor     al,al
               jmp     @End
@True:        mov     al,True
@End:
end;
Delphi-Quellcode:
FUNCTION IsPartlyTransparent(Bmp:TBitMap):Boolean;
begin
   with Bmp do
      Result:=IsPartlyTransparentAsm(ScanLine[0],ScanLine[1],Width,Height);
end;

Medium 23. Feb 2016 00:52

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Ich bin gerade etwas verwirrt bzgl. des konkreten Anwendungsfalls. Wenn doch im eigenen Programm ganz klar definiert ist welche Operationen das Zufügen von Transparenz bewirken können, ist es doch ein vergleichsweise simples Unterfangen eine entsprechende OnChange-Methode zu implementieren, die ein simples Flag setzt, dass man dann beim Zeichnen bloß immer wieder nur auswerten muss. Dabei sollten dann selbst die ursprünglichen 80ms kaum noch ins Gewicht fallen.

Wenn aber doch, dann wäre ggf. ein Blick auf die Zeichenroutinen am Ende viel zielführender als solch eine "quasi-early-out" Prüfung. Im Zweifel schaue man sich mal an, wie die GR32 dies bei ihrer Implementierung von Layers im TImage32 macht. Diese Lib ist da imho mit das schnellste was man ohne Parallelisierung bekommen kann (obwohl hier SIMD-Routinen in ASM eingesetzt werden). (Wenn ich mich recht erinnere benutzen die zudem u.a. ein per-Pixel-Skipping bei nicht- und volltransparenten Pixeln um das Verrechnen zu sparen.)
Es sollte auf halbwegs aktuellen PCs ein sprichwörtlicher Pups sein, zahlreiche Layer halbtransparent übereinander zu zeichnen. Eine Vorab-Prüfung kann meiner Einschätzung nach fast nur eine verschlechternde "Optimierung" werden, egal wie pfiffig man sie implementiert.
(Bei der GR32 spielt sicherlich auch eine sehr große Rolle, dass alle Rechnungen Integer-Basiert sind! Vielleicht lohnt sich am Ende sogar der Einsatz der ganzen Lib.)

Nur um das nochmals klar zu stellen: Wenn es rein um die Prüfung ginge, wären die hier gemachten Anstrengungen sicherlich sinnvoll. Für die Anwendung des Übereinanderzeichnens hingegen, meiner Einschätzung nach, nicht.

Rollo62 23. Feb 2016 10:36

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Mich würde generell interessieren ob man solche Abläufe nicht auch in der GPU machen kann.

Ich denke das ist genau der Gund warum GPU-unterstützte Software soviel schneller ist.
Habe aber 0-Erfahrung damit.

Als Vorraussetzung wäre dann vielleicht das die GPU direkt im Haupsspeicher (shared) arbeiten kann, weil sonst wieder kopiert werden müssten.

Kennt sich da jemand aus ?

Rollo

Neutral General 23. Feb 2016 12:52

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
So ich habe mich mal an SSE versucht.
Es scheint zu funktionieren (hab jetzt keine Bilder wo ichs richtig testen könnte), aber wie schnell/langsam das ist und ob der
Code wirklich fehlerfrei ist kann ich nicht sagen. Habe bisher noch nie was mit SSE gemacht.

Und das Bitmap sollte ein Vielfaches von 4 Pixeln groß sein, sonst knallts wahrscheinlich bzw. man müsste das abfangen.

Delphi-Quellcode:
function IsPartlyTransparent(fromScanline: Pointer; toScanline: Pointer): Boolean;
const pattern: Array[0..3] of Cardinal = ($FF000000, $FF000000, $FF000000, $FF000000);
label schleife, transparent, ende;
var IsTransparent: Array[0..15] of Byte; // "GigaBool" (128 Bit)
asm
  movups xmm2, pattern
schleife:
  movups xmm1, [eax]
  ANDPS xmm1, xmm2
  CMPEQPS xmm1, xmm2
  movntps IsTransparent, xmm1
  cmp dword ptr IsTransparent, 0
  jz transparent
  add eax, $10
  cmp eax, edx
  jb schleife
  mov Result, false
  jmp ende
transparent:
  mov Result, true
ende:
end;
Aufruf:

Delphi-Quellcode:
IsPartlyTransparent(bmp.ScanLine[bmp.Height-1],bmp.ScanLine[0])
Du kannst ja einfach mal schauen was dabei rum kommt.

Harry Stahl 23. Feb 2016 18:17

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Vielen Dank für die weiteren Bemühungen.

Deine vorletzte Variante ist nun ca. 1 MS schneller als meine.
Die letzte Variante stürzt immer ab.

Du kannst übrigens auch mit Delphi 2010 mein Demo, dass ich hier hochgeladen habe, verwenden, entfernst halt nur System.Threading und System.syncobjects und die Variante mit TParallel, dann hast Du eine 32-Bit-Bitmap und kannst diese per Schalter auch mit Transparenz versehen.

Harry Stahl 23. Feb 2016 18:28

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Medium (Beitrag 1331039)
Ich bin gerade etwas verwirrt bzgl. des konkreten Anwendungsfalls. Wenn doch im eigenen Programm ganz klar definiert ist welche Operationen das Zufügen von Transparenz bewirken können, ist es doch ein vergleichsweise simples Unterfangen eine entsprechende OnChange-Methode zu implementieren, die ein simples Flag setzt, dass man dann beim Zeichnen bloß immer wieder nur auswerten muss. Dabei sollten dann selbst die ursprünglichen 80ms kaum noch ins Gewicht fallen.

Es ist halt ein simples Flag, das an vielen Stellen gesetzt werden müsste und wenn ich jetzt nur eine Stelle übersehe, dann kann es schon falsch laufen. Außerdem könnten letztlich viele Funktionen aufeinanderwirken, so dass ich letztlich auch mehrmals prüfen müsste, ob eine Grafik einer Bildebene nun teiltransparent ist oder nicht und dann stellt sich die Frage, ob hier in der Summe von mehr oder weniger gleichzeitig stattfindenden Bildbearbeitungsfunktionen und Bildanzeige noch ein Geschwindigkeitsvorteil entsteht.

Aber danke, dass Du dies als mögliche Lösungsvariante ins Spiel gebracht hast. Generell ist das ja auch eine richtige Überlegung, Berechnungen zu vermeiden, wo sie nicht nötig sind. Aber irgendwann muss die Prüfung geschehen und wenn sie dann schneller geht, ist das halt willkommen.

Medium 23. Feb 2016 22:16

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Hmm, ich glaube da ist dann dein interner Aufbau im Nachgang dafür etwas ungünstig ausgelegt. In meinem Kopf stellte sich die Struktur ganz grob so dar:
Delphi-Quellcode:
type
  TMyLayeredImage = class;

  TMyLayer = class
  public
    procedure Changed; // Führt Prüfung aus und setzt FHasTransparency und FChanged in der folgenden Image-Klasse (Es muss hierfür eine Owner-Beziehung geben, daher die Forward-Deklaration)
    property BMP: TBitmap read FBMP;
    property HasTransparency: Boolean read FHasTransparency;
    property Bounds: TRect read GetBounds write SetBounds; // SetBounds würde Changed im Owner auslösen, nicht aber eine Prüfung für den Layer selbst
  end;

  TMyLayeredImage = class
  private
    FImage: TBitmap; // Container für das fertige Bild
    FLayers: TObjectlist; // Beinhaltet die TMyLayer-Instanzen, Index 0 ist die unterste
    procedure Compose; // Fügt die Layer in FImage zusammen
  public
    property Image: TBitmap read GetImage; // In GetImage() dann "if FChanged then Compose; result := FImage;"
    // Und dann noch so nette Helfer (Komfortfunktionen halt) wie:
    property LayerAtPoint[x, y: Integer]: TMyLayer read GetLayerAtPoint; // Gibt den obersten Layer zurück, der den Punkt (x,y) beinhaltet, oder nil bei keinem
  end;
Wie gesagt ganz ganz grob. Jedes Layer würde einfach sein Transparenz-Bit mitführen, jede Operation auf dem Layer löst das Changed Ereignis aus, welches auch an den Owner (die zusammenfügende LayeredImage Klasse) gereicht wird. Beim Zeichnen wird dann einfach das fertige Image angefordert, und allen am Bild beteiligten ist klar ob überhaupt neu Komponiert werden muss, und welche Layer zu diesem Zeitpunkt Transparenzen haben.
Ob sich sowas jetzt noch überhaupt in deine Struktur hineinfiedel lässt kann ich natürlich nicht beurteilen. Aber das wäre in etwa mein erste Gedanke wenn ich an ein Layered-Image gehen würde. Die Prüf-Routine würde lediglich nur in genau den gerade geänderten Layern gebraucht.

Letztlich würde ich aber, wenn so etwas anstünde, direkt zur GR32 greifen und die einfach immer stumpf alles zeichnen lassen - weil das ist schnell genug, wenn man nicht gerade mehrere hundert Layer in 12 Megapixel hat. (Dafür gäbe es aber auch eigentlich schon keine performante Lösung mehr, egal wie. Speicher wird dann auch schnell rare Ware.)


@Rollo62:
Gehen würde das mit der GPU, und sogar sehr flott. Aber du hast ein Problem dabei bereits bemerkt: Es bringt kaum etwas, wenn man NUR diese Funktion in der GPU machen würde, da man stets alle Daten immer hin und her schaufeln müsste. (Ein Sharing gibt es nicht.) Zwar geht das meist schon irre schnell, gemessen an den Mengen, aber der Gewinn steht dem Mehraufwand fast nur dann in gutem Verhältnis gegenüber, wenn man praktisch alles über die GPU macht.
Mittlerweile ist das sogar viel viel einfacher als früher. Zu Zeiten von DirectX 9 und so musste man Pixel-Shader bemühen, und recht fummelig ein Quasi-3D Programm bauen um das von hinten durch die Brust zweckzuentfremden. Dank CUDA und OpenCL stehen hier mittlerweile aber Werkzeuge zur Verfügung, bei denen man sich um die Herkunft der GPU als reinen 3D- und Shader-Knecht nicht mehr scheren muss, und sie wie einen echten Vektorprozessor ansprechen kann. Leider weiss ich nicht wie es um Implementierungen der beiden großen APIs für Delphi steht. Und man muss sich an eine doch sehr andere Art der Programmierung gewöhnen, als man sie von Delphi im Alltag gewohnt ist.

Amateurprofi 23. Feb 2016 23:42

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Zitat:

Zitat von Neutral General (Beitrag 1331092)
So ich habe mich mal an SSE versucht.
Es scheint zu funktionieren (hab jetzt keine Bilder wo ichs richtig testen könnte), aber wie schnell/langsam das ist und ob der
Code wirklich fehlerfrei ist kann ich nicht sagen. Habe bisher noch nie was mit SSE gemacht.

Und das Bitmap sollte ein Vielfaches von 4 Pixeln groß sein, sonst knallts wahrscheinlich bzw. man müsste das abfangen.

Delphi-Quellcode:
function IsPartlyTransparent(fromScanline: Pointer; toScanline: Pointer): Boolean;
const pattern: Array[0..3] of Cardinal = ($FF000000, $FF000000, $FF000000, $FF000000);
label schleife, transparent, ende;
var IsTransparent: Array[0..15] of Byte; // "GigaBool" (128 Bit)
asm
  movups xmm2, pattern
schleife:
  movups xmm1, [eax]
  ANDPS xmm1, xmm2
  CMPEQPS xmm1, xmm2
  movntps IsTransparent, xmm1
  cmp dword ptr IsTransparent, 0
  jz transparent
  add eax, $10
  cmp eax, edx
  jb schleife
  mov Result, false
  jmp ende
transparent:
  mov Result, true
ende:
end;
Aufruf:

Delphi-Quellcode:
IsPartlyTransparent(bmp.ScanLine[bmp.Height-1],bmp.ScanLine[0])
Du kannst ja einfach mal schauen was dabei rum kommt.

Hallo Neutral General,

m.E. kann die Funktion nur sehr eingeschränkt funktionieren.

1) Überprüfter Bereich der Bitmap:
Als Parameter werden der Funktion die Adressen des jeweils ersten Pixels der ersten und letzten Zeile übergeben.
Du prüfst die Pixel ab Anfang der "ersten" Zeile bis zum Anfang der "letzten" Zeile.
Wenn also ein transparentes Pixel irgendwo in der letzten Zeile ist, wird das nicht erkannt.

2) Prüfung von jeweils 4 Pixeln auf Transparenz:
Du liest 4 Pixel=16 Bytes in XMM1 machst ein AND mit dem Pattern und vergleichst das Ergebnis mittels CMPEQPS mit dem Pattern.
Bei diesem Vergleich werden alle Bits der Singles, bei denen der Vergleich True ergibt, = 1 gesetzt, andernfalls = 0.
Dann schreibst Du XMM1 in das 16 Byte Array "IsTransparent" und prüfst das erste DWord dieses Array auf 0.
Das heißt du prüfst immer nur das erste von jeweils 4 Pixeln auf Transparenz, die übrigen 3 von 4 Pixeln bleiben ungeprüft.

Amateurprofi 23. Feb 2016 23:45

AW: Prüfung eines Bitmaps auf Transparenz (gehts noch schneller)?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Harry:
Ich hab mir mal die Mühe gemacht, mehrere ASM-Routinen zu schreiben, die alle recht flink arbeiten, und zusätzlich ein kleines Testprogramm.

Zu den Asm-Routinen:
1) IsPartlyTransparentAsm
Als 32 Bit und als 64 Bit Version vorhanden.
Die Routine prüft ganz simpel Pixel für Pixel

2) IsPartlyTransparentAsmQW
Nur als 64 Bit Version vorhanden.
Die Routine nutzt die 64 Bit Register und prüft jeweils 2 Pixel auf einmal.

3) IsPartlyTransparentAsmSSE
Als 32 Bit und als 64 Bit Version vorhanden.
Die Routine nutzt sie 128 Bit Register und prüft jeweils 4 Pixel auf einmal.

Bei einer Bitmap in der von dir vorgeschlagenen Größe von 4244 x 2819 werden folgende Zeiten genannt:

8 ms IsPartlyTransparentAsm 32 Bit
4 ms IsPartlyTransparentAsmSSE 32 Bit
8 ms IsPartlyTransparentAsm 64 Bit
5 ms IsPartlyTransparentAsmQW 64 Bit
4 ms IsPartlyTransparentAsmSSE 64 Bit

Vielleicht schaust Du dir das mal an.
Ich habe die Routinen mit verschiedenen Parametern getestet und keine Fehler bemerkt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:30 Uhr.
Seite 1 von 2  1 2      

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