Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Zwei transparente Bitmaps miteinader verrechnen (https://www.delphipraxis.net/192942-zwei-transparente-bitmaps-miteinader-verrechnen.html)

himitsu 4. Jun 2017 19:31

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Es gibt ja mindestens zwei Varianten wie man sowas verrechnet.

* Entweder die Bilder zu 50:50 vemischen, also als wenn man zwei "flüssige" Farben zusammen schüttet.
Das ergibt praktisch ganz billig 'nen Mittelwert und es ist egal was oben oder unten ist
x := (x + y) / 2
Die Transparent natürlich umgekehrt
x := 255 - ((255 - x) + (255 - y)) / 2

* Oder man macht es richtig, so als wenn man zwei bemalte Glasscheiben übereinander legt
die Gesamttransparenz zusammenrechnen (transparenz-unten * trasparenz-oben ... 50% * 50% = 25%)
und bei den Farben die Transparenz des oberen Bildes nehmen, damit den eigenen Farbanteil berechnen (255 - tansparenz)
dann den Farbanteil mit der Transparenz des unteren Bildes (ebenfalls 255 - tansparenz)
dann nochmal von unten den oben durchscheinenden Teil (tansparenz ... also (255 - transparenz-unten) * trasparenz-oben)
und zum Schluß beide Farben addieren



und egal was du tust, du mußt immer auf Überläufe achten, sonst gibt es solche "Farbverfälschungen", wo z.B. aus zwei Mal fast 0% plötzlich 100% Transparenz entsteht

Michael II 4. Jun 2017 19:41

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Harry

die Formel darf dich nicht abschrecken. Du musst eigentlich nur zwei Dinge tun für jedes Pixel:

1. Den neuen Alphawert berechnen
2. Die neuen Farbwerte für R,G,B.

Ich hab's rasch getan und das Resultat sieht wirklich gut aus.

In diesem Beispiel siehst du, wie du es für zwei gleich grosse Bitmaps A, B tun kannst, welche direkt übereinander gelegt werden.
( Du willst dann natürlich Code schreiben für A an Position x, y über B legen oder ähnlich. )

Delphi-Quellcode:
procedure AueberB( a, b, c : TBitMap );

var lineA, lineB, lineC : PRGB32Array;
    x, y : integer;
    alphaA, alphaB, alphaC : extended;
begin
  a.PixelFormat := pf32Bit;
  b.PixelFormat := pf32Bit;
  c.PixelFormat := pf32bit;

  for y := 0 to a.Height-1 do
  begin
    lineA := a.ScanLine[y];
    lineB := b.ScanLine[y];
    lineC := c.ScanLine[y];

    for x := 0 to a.Width-1 do
    begin
       // Test
      (* lineA[x].A := 255;
      lineB[x].A := 0; *)
      alphaA := lineA[x].A/255;
      alphaB := lineB[x].A/255;
      // 0 = Transparent -  1 = instransparent
      alphaC := alphaA + ( 1 - alphaA )*alphaB; // (den Fall alphaA=0 UND alphaB=0 musst du hier noch sep. behandeln)
      lineC[x].A := round(alphaC*255);
      lineC[x].R := round(1/alphaC*(alphaA*lineA[x].R + (1-alphaA)*alphaB*lineB[x].R ));
      lineC[x].G := round(1/alphaC*(alphaA*lineA[x].G + (1-alphaA)*alphaB*lineB[x].G ));
      lineC[x].B := round(1/alphaC*(alphaA*lineA[x].B + (1-alphaA)*alphaB*lineB[x].B ));
    end;
  end;

end;

Testen:
So rufe ich die Prozedur auf:
Ich lade in diesem Beispiel in A und B das gleiche Bild, in A um 10,10 versetzt.
Ins Bild A schreibe ich den Buchstaben 'A' rein, ins Bild B den Buchstaben 'B'. (Damit sehe ich im Resultatbild C in etwa, wie A und B zum Resultat C beitragen.)

In der Prozedur AueberB (siehe Code oben) habe ich zwei Zeilen
lineA[x].A := 255;
lineB[x].A := 0;
"auskommentiert". Dort kannst du globale Alpha-Werte setzen und sehen, wie sich diese Werte aufs Resultatbitmap C auswirken.


Delphi-Quellcode:
procedure TForm100.Button1Click(Sender: TObject);
var a,b,c : TBitMap;
begin
  a := TBitMap.Create;
  b := TBitMap.Create;
  c := TBitMap.Create;

  a.LoadFromFile( 'C:\Users\Michael\Desktop\thun.bmp' );
  b.LoadFromFile( 'C:\Users\Michael\Desktop\thun.bmp' );
  a.Canvas.Draw( 10,10,b );

  a.Canvas.Font.Size := 100;
  a.Canvas.TextOut( 10,10, 'A' );
  b.Canvas.Font.Size := 100;
  b.Canvas.TextOut( 10,210, 'B' );
  c.SetSize( a.Width, a.Height );

  AueberB( a, b, c );

  c.SaveToFile( 'C:\Users\Michael\Desktop\thun2.bmp');

  a.Free;
  b.Free;
  c.Free;
end;

Natürlich kann man die ganze Sache auch in den "Integer Bereich" verlegen. Bei modernen Prozessoren gewinnt man aber (leider ;-)) häufig gar nicht mehr viel Zeit; auf älteren Kisten jedoch sehr...

mensch72 4. Jun 2017 19:44

AW: Zwei transparente Bitmaps miteinader verrechnen
 
also gilt:

AN:=255-BYTE(DWORD( (DWORD(AU)*DWORD(AO)) div (DWORD(AU)+DWORD(AO)) ));

Gemäß Beispiel
50% & 50% -> 25% ...
(128*128) / (128+128) -> 64 was ja 25% entspricht:)

Jens01 4. Jun 2017 19:48

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Graphhics32?
https://github.com/graphics32/graphi...g/TextureBlend

himitsu 4. Jun 2017 20:20

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Da, wo die zwei Bilder nicht übereinander liegen, dann kann man da für das "fehlende" Bild als Bummywert ein Pixel mit 100% Transparenz verwenden und damit weiterrechen.

Nach Wegkürzen aller Parameter bleibt dann das Pixel des anderen Bildes 1:1 übrig.

Harry Stahl 5. Jun 2017 00:27

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, danke erst mal für die Hinweise und die erste Umsetzung der Formel.

Habe daran noch ein wenig weiter gearbeitet, das sieht nun so aus:

Delphi-Quellcode:
  alphaA := A/255;
  alphaB := AO/255;
  alphaC := alphaA + (1 - alphaA )* AlphaB;

  R := round((1/alphaC) * (alphaA* (R/255) + (1-alphaA) * alphaB * (Ro/255) ) * 255);
  G := round((1/alphaC) * (alphaA* (G/255) + (1-alphaA) * alphaB * (Go/255) ) * 255);
  B := round((1/alphaC) * (alphaA* (B/255) + (1-alphaA) * alphaB * (Bo/255) ) * 255);

  A := round (AlphaC * 255);
Funktioniert soweit, nur ist die Verrechnung der Farben anscheinend noch nicht ausreichend. Daher habe ich das hier noch mit meiner Ursprungslösung ergänzt:

Delphi-Quellcode:
  alphaA := A/255;
  alphaB := AO/255;
  alphaC := alphaA + (1 - alphaA )* AlphaB;

  B := (ao * (bo - b) shr 8 + b); // hier ergänzt
  G := (ao * (go - g) shr 8 + g); //
  r := (ao * (ro - r) shr 8 + r); //

  R := round((1/alphaC) * (alphaA* (R/255) + (1-alphaA) * alphaB * (Ro/255) ) * 255);
  G := round((1/alphaC) * (alphaA* (G/255) + (1-alphaA) * alphaB * (Go/255) ) * 255);
  B := round((1/alphaC) * (alphaA* (B/255) + (1-alphaA) * alphaB * (Bo/255) ) * 255);

  A := round (AlphaC * 255);
Mit dieser Ergänzung wird das Ergebnis exakt so angezeigt, wie Photoshop die Datei errechnet.

Anliegend mal ein Screenshot, wo eine PSP-Layer-Datei auf eine Ebene reduziert wurde und als PNG-Datei geladen (linke Seite).
Rechts daneben die Original-PSP-Datei, mit den geladenen Layern, wo das Ergebnis dann berechnet mit der o.g. Routine im Programm angezeigt wird (rechte Seite).

stahli 5. Jun 2017 12:50

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Beeindruckend :thumb:

Harry Stahl 5. Jun 2017 13:42

AW: Zwei transparente Bitmaps miteinader verrechnen
 
@Stahli

Danke, aber das Kompliment geht an all die netten Kollegen, die mir hier geholfen haben, alleine hätte ich das nicht hinbekommen.
Hatte zuvor bestimmt einen halben Tag lang versucht, das selbst zu backen, aber ohne Erfolg.

Da sieht man mal wieder, wie super hilfreich und unverzichtbar dieses Forum ist (insofern mal ein herzliches Danke an Daniel und die anderen Forenunterstützer)!:thumb:

Michael II 6. Jun 2017 14:36

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Harry

du hast hier mal ein Testprogramm gepostet. - Kannst du (falls dein Projekt nun nicht geheim eingestuft ;-)) dein aktualisiertes Projekt hochladen (oder mir per PN senden)? Besten Dank.


Zitat:

Funktioniert soweit, nur ist die Verrechnung der Farben anscheinend noch nicht ausreichend.
Das hängt von den Quellbildern ab.

In deinen Beispielbildern A und B wurde der weiche Rand erzeugt, indem
1. der Alphawert am Rand abnimmt UND zusätzlich
2. mit einem Farbverlauf Farbe -> schwarz

Oder etwas anders ausgedrückt: Der Originalfarbwert wurde wahrscheinlich mit dem Alphawert multipliziert - und dieser Farbwert wurde in A, B gespeichert.

Du könntest also in solchen Fällen vor dem Berechnen des Zielbilds für die Bitmaps A und B die Originalfarben [die Farben für intransparente Versionen von A und B] ermitteln.

_____
Manchmal liegen A und B mit Alphakanal aber auch in Originalfarben vor (also nicht wie bei deinen Beispielbildern "Farbe=alpha*Originalfarbe"); in solchen Fällen (grösserer Farbraum fürs Bild möglich) müsstest du natürlich auf den oben erwähnten Vorprozess verzichten.

[Wenn deine Grafikobjekte keine Auskunft (oder keine korrekte) über das AlphaFormat geben, hilft nur testen (oder raten, indem du AlphaVerläufe und Farbverläufe miteinander vergleichst).]

Gruss
Michael

Harry Stahl 6. Jun 2017 23:02

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Michael,

klar, kein Problem, habe das Demo kurzerhand mit meiner Endlösung aktualisiert und hier noch mal hochgeladen.

Ja, das Problem mit den vormultiplizierten Farbwerten basierend auf den Alphawerten ist mir bekannt und führt immer wieder zu Verwirrungen. In meinem Projekt verwende ich keine vorberechneten Farbwerte auf Alphabasis, sondern die reinen Farbwerte und berechne die Ausgabe dann auf der Basis der Alphawerte.

Wie bereits oben mal erwähnt, gibt es hier beim Demoprojekt im Ergebnis eine Farbverfälschung, weil ich die PNG-Dateien aus den TImage-Containern über einen Umweg wieder in das TBitmap holen musste. In meinem Programm tritt das nicht auf, zum Laden der Dateien verwende ich die ImageEn-Komponenten, die das soweit alles schon richtig berücksichtigen.

Nur konnte ich das hier nicht posten, weil die meisten Anwender diese Kompos wohl eher nicht haben.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:59 Uhr.
Seite 2 von 3     12 3      

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