AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Zwei transparente Bitmaps miteinader verrechnen

Ein Thema von Harry Stahl · begonnen am 4. Jun 2017 · letzter Beitrag vom 9. Jun 2017
Antwort Antwort
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#1

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 5. Jun 2017, 12:50
Beeindruckend
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von Harry Stahl
Harry Stahl

Registriert seit: 2. Apr 2004
Ort: Bonn
2.561 Beiträge
 
Delphi 12 Athens
 
#2

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 5. Jun 2017, 13:42
@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)!
  Mit Zitat antworten Zitat
Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
778 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 6. Jun 2017, 14:36
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
Michael Gasser
  Mit Zitat antworten Zitat
Benutzerbild von Harry Stahl
Harry Stahl

Registriert seit: 2. Apr 2004
Ort: Bonn
2.561 Beiträge
 
Delphi 12 Athens
 
#4

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 6. Jun 2017, 23:02
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.
Angehängte Dateien
Dateityp: zip ImgMix.zip (831,7 KB, 12x aufgerufen)
  Mit Zitat antworten Zitat
Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
778 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 7. Jun 2017, 19:27
Hallo Harry

danke fürs Posten der angepassten Version. Ich weiss nun wieso du die Korrekturfunktion einbauen musst.

Du hast alphaA und alphaB vertauscht und auch bei der Berechnung der Farbwerte eine Vertauschung vorgenommen.

Wenn du noch einmal die Formel auf https://de.wikipedia.org/wiki/Alpha_Blending anschaust (oder den von mir geposteten Delphi Code) für das Überblenden A über B, dann findest du

alphaC := alphaA + (1-alphaA)*alphaB
und für die Berechnung des Farbwerts gilt
C=(alphaA*A + alphaB*B - alphaA*alphaB*B)/alphaC [0.]


Du hast aber berechnet:
alphaA = B/255 // in alphaA steht also bei dir der Alphakanal Wert von B (statt A)
alphaB = A/255 // und in alphaB steht der Alphakanal Wert von A (statt B)

Da die Funktion zur Berechnung von alphaC symmetrisch [f(x,y)=f(x,y)=x+y-xy) ist, spielt deine Vertauschung der alpha-Werte hier keine Rolle. Der von dir ermittelte Wert von alphaC ist korrekt.

Bei der Berechnung der Farbe rechnest du
C=(alphaA*B + (1-alphaA)*alphaB*A)/alphaC [1.]

Hier hast du nun auch noch A und B vertauscht (aber nicht so, dass sich die "AlphaVertauschung" und die "FarbVertauschung" aufheben) . Du rechnest so, als wäre A unten und B oben.

Vor der Berechnung der Farbwerte korrigierst du die Farbwerte von B wie folgt:
B=alphaB*(A-B)+B [2.]

Wenn wir [2]. in [1]. einsetzen, erhalten wir:

C=(alphaA*(alphaB*(A-B)+B) + (1-alphaA)*alphaB*A)/alphaC
= alphaA*alphaB*A - alphaA*alphaB*B + alphaA*B + alphaB*A-alphaA*alphaB*A)/alphaC
= (alphaB*A + alphaA*B - alphaA*alphaB*B)/alphaC

Wenn du jetzt noch berücksichtigst, dass du alphaA und alphaB vertauscht hast, erhältst du exakt [0.]. Du hast also durch deine Korrektur [2.] die Vertauschungen korrigiert.


Deine Korrektur [2.] ist aber etwas heikel umgesetzt. Du rechnest in deinem Code
B := (ao * (bo - b) shr 8 + b); // hier ergänzt
B ist als Byte definiert und die rechte Seite kann grösser werden als 255. Die Korrektur könnte deshalb in gewissen Fällen misslingen.

Ich würde deshalb deine Vertauschunen zurücktauschen und stattdessen die Originalformel verwenden, also:

alphaC := alphaA + (1-alphaA)*alphaB
C=(alphaA*A + alphaB*B - alphaA*alphaB*B)/alphaC

also etwa so:

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

// 0 = "normal"
// 1 = Alpha Korrektur
// Berechne A über B - Resultat in C

var lineA, lineB, lineC : PRGB32Array;
    x, y : integer;
    alphaA, alphaB, alphaC : extended;
label weiter;

begin
  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
      alphaA := lineA[x].A/255;
      alphaB := lineB[x].A/255;

      // 0 = Transparent - 1 = intransparent
      if ( alphaA + alphaB = 0 ) then
          lineC[x] := lineA[x]
      else
      if ( lineA[x].A = 255 ) then // A intransparent => A nehmen
          lineC[x] := lineA[x]
      else
      if ( lineA[x].A = 0 ) then // A transparent => B nehmen
          lineC[x] := lineB[x]
      else
      if ( lineB[x].B = 0 ) then // B transparent => A nehmen
          lineC[x] := lineA[x]
      else
      begin // A und B verrechnen:
           alphaC := alphaA + ( 1 - alphaA )*alphaB;
           lineC[x].A := round(alphaC*255);

           // falls die Farbwerte von A und von B "vormultipliziert mit Alpha" vorliegen:
          (* lineC[x].R := EnsureRange( round((lineA[x].R + (1-alphaA)*lineB[x].R )), 0, 255 );
          lineC[x].G := EnsureRange( round((lineA[x].G + (1-alphaA)*lineB[x].G )), 0, 255 );
          lineC[x].B := EnsureRange( round((lineA[x].B + (1-alphaA)*lineB[x].B )), 0, 255 );
            *)


           // falls Farbwerte nicht vormultipliziert mit Alpha vorliegen:
           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; {for x := 0 to a.Width-1 do}
  end;{for y := 0 to a.Height-1 do}
end;

Tipp: Du musst keine EnsureRange Funktion nutzen. Die Berechnungen von alpha und Farbwert sind ja genau so konstruiert, dass Sie im Intervall [0..1] (bzw. [0..255]) liegen.

Ich habe gesehen, dass du deine Bitmaps in PNGs umwandelst und dann erst die PNG ins Image lädst. Dies hat wahrscheinlich technische Gründe. TImage kann auch direkt Bitmaps mit AlphaKanal darstellen.

In deinem Code müsstest du ergänzen:
Delphi-Quellcode:
  bmres.PixelFormat := pf32bit; // bisher
  bmres.AlphaFormat := afDefined; // neu
..
..
  // Ausgabe bmRes:
  imgres.Transparent := false; //neu
  imgres.Picture.Assign( bmres ); //neu

Gruss
Michael
Michael Gasser
  Mit Zitat antworten Zitat
Benutzerbild von Harry Stahl
Harry Stahl

Registriert seit: 2. Apr 2004
Ort: Bonn
2.561 Beiträge
 
Delphi 12 Athens
 
#6

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 9. Jun 2017, 00:21
Danke für den Tipp mit dem TImage.

Ansonsten werde ich mir morgen mal in Ruhe ansehen, ob ich möglicherweise etwas vertauscht habe, könnte so sein. Ergebnis in meinem Programm stimmt jedenfalls (habe ich inzwischen mit dutzenden semitransparenten Überblendsituationen in Photoshop und bei mir verglichen). Die PSP-Datei lade ich mit der ImageEn-Komponente und übernehme daraus die Bitmaps und Alphawerte (letztere sind dort i.d.R. in einem 8-Bit-Bimtap abgelegt) für die Erstellung meiner 32-Alpha-Bitmap, mit der ich intern arbeite und die ganzen Berechnungen vornehme.
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:35 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