Delphi-PRAXiS
Seite 4 von 6   « Erste     234 56      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Durchschnittsfarbe eines Bitmap "schnell" ermitteln (https://www.delphipraxis.net/207856-durchschnittsfarbe-eines-bitmap-schnell-ermitteln.html)

Amateurprofi 11. Mai 2021 18:59

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1489169)
Zitat:

Zitat von Amateurprofi (Beitrag 1489163)
Hallo KodeZwerg:
Ich hab dir mal etwas zusammengestoppelt.
Aufruf mit GetAvgColor(Dateiname) oder GetAvgColor(Bitmap)
Mit TestGetAvgColor; hab ich das Ergebnis und die Performance getestet und mit der Funktion aus #3 verglichen.
Die zurückgegebenen Durchschnittsfarben sind identisch, die Ausführungszeiten sind dagegen höchst unterschiedlich.

Danke Amateurprofi, dein Code arbeitet zwar schneller aber liefert bei mir kein Ergebnis.
Ich habe ihn gegen den Turbo von TiGü antreten lassen, siehe Anhang.

Muss ich noch eine besondere Einstellung vornehmen damit mir Dein Code einen Farbwert über 0 liefert?

Getestet mit Delphi Rio, 32-bit build, Release, unter Windows 10 64bit aktuellste patches.

Oh, das ist mir jetzt aber peinlich.
In der Funktion
Delphi-Quellcode:
GetAvgColor(Dsn:String):TColor;
steht in der vorletzten Zeile
Code:
GetAvgColor(Bmp);
Ändere das bitte in
Code:
Result:=GetAvgColor(Bmp);
Hintergrund ist, dass ich ursprünglich die Funktion
Delphi-Quellcode:
GetAvgColor(Bmp:TBitmap):TColor;
hatte, also die, mit einer Bitmap als Parameter.
Die Funktion mit dem Dateinamen als Parameter habe ich später hinzugefügt, aber nicht mehr getestet.
Dummerweise hatte ich da vergessen, Result zu setzen.

Rollo62 11. Mai 2021 19:10

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von himitsu (Beitrag 1489198)
Nein, man muß gucken, wie oft 256 Platz hat,
denn die 0 ist auch ein gültiger Wert.

1..255 bzw. 0..254 wäre Platz für 255.

So sehe ich das auch, siehe 2^8 = Platz für 256 Werte.

Michael II 11. Mai 2021 19:28

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1489177)
@Michael II
Nun passiert da was... aber mein Ergebniss ist falsch.
Delphi-Quellcode:
function GetAvgGDIColor(const Filename: string): TColor;
var
  gr: TGPGraphics;
  Bmap: TGPBitmap;
  col: TGPColor;
begin
  Result := 0;
  bmap := TGPBitmap.Create(Filename);
  try
    gr := TGPGraphics.Create( bmap );
    try
      gr.ScaleTransform( 1/bmap.GetWidth, 1/bmap.GetHeight );
      gr.DrawImage( bmap, 0,0 );
      bmap.GetPixel(0,0, col);
      Result := RGB(GetRValue(col), GetGValue(col), GetBValue(col));
    finally
      gr.Free;
    end;
  finally
    bmap.Free;
  end;
end;
Falls Du Dich nochmal reinklinken könntest


Dein Code ist fast voll ok so.
Einzig bei der Auswertung von col....
Du musst darauf achten, dass TGPColor (anders als TColor!) wie folgt die Farbe speichert:
ARGB d.h. ALPHA ROT GRÜN BLAU
Und die von dir verwendeten Winapi.Windows TColor Funktionen GetRValue... rechnen mit (A)BGR=(ALPHA) BLAU GRÜN ROT.

Du willst ein Resultat in TColor:
Wenn du GetRValue auf col : TGPColor anwendest wirst du den Blauanteil der Farbe erhalten. Entsprechend: Wenn du GetBValue auf eine TGPColor anwendest wirst du den Rotanteil erhalten.

Testen könntest du die Sache zum Beispiel so.
Du erstellst eine hbit: TBitMap von der Grösse 500x500. Wir setzen alle Pixel auf RGB(255,128,64). Das Pixel (0,0) auf RGB(0,0,0).

Delphi-Quellcode:
hbit := TBitMap.Create;
  hbit.SetSize( 500, 500 );

for x := 0 to hbit.Height-1 do
    for y := 0 to hbit.Width-1 do
      hbit.Canvas.Pixels[x,y] := rgb(255,128,64);

hbit.Canvas.Pixels[0,0] := rgb(0,0,0);

fn := 'C:\Users\micha\Desktop\test.bmp';
hbit.SaveToFile( fn );
hbit.Free;
Nun lässt du deine Funktionen auf hbit (mit anderen Farben auch prüfen!) los.
TiGü via scanline liefert - da dort nach dem Summieren div anzahlpixels (statt trunc(summe/anzahlpixels+0.5) verwendet wird - als Durchschnittsfarbe (254,127,63).

Nun lässt du die Funktion auf grizzlys GDI+ Idee, bzw. deine Lösung los: Du erhältst wie erwartet (255,128,64).

Amateurprofi 11. Mai 2021 19:44

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
@KodeZwerg:

In #16 wurde in den Raum geworfen, ob man nicht einfach ein Resize auf 1x1 Pixel machen kann.
Ich hab das mal geprüft und in meiner Funktion TestGetAvgColor; vor dem Bmp.Free folgendes eingefügt:

Delphi-Quellcode:
   T3:=GetTickCount;
   Bmp2:=TBitmap.Create;
   Bmp2.PixelFormat:=pf24Bit;
   Bmp2.SetSize(1,1);
   SetRect(R,0,0,1,1);
   Bmp2.Canvas.StretchDraw(R,Bmp);
   CL3:=Bmp2.Canvas.Pixels[0,0];
   Bmp2.Free;
   T3:=GetTickCount-T3;
Das ShowMessage am Ende hab ich abgeändert in:

ShowMessage('$'+IntToHex(CL1,8)+' '+IntToStr(T1-T0)+#13+
'$'+IntToHex(CL2,8)+' '+IntToStr(T2-T1)+#13+
'$'+IntToHex(CL3,8)+' '+IntToStr(T3));

Scheint zu funktionieren, was aber auch daran liegen könnte, dass in der Testprozedur alle Pixel die gleiche Farbe haben.
Korrektur: Hab es gerade mit einem echten Bild geprüft.
Die Methode, das Bild auf 1x1 Pixel zu reduzieren, liefert eine andere Durchschnittsfarbe.

Michael II 11. Mai 2021 19:46

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von himitsu (Beitrag 1489198)
Nein, man muß gucken, wie oft 256 Platz hat,
denn die 0 ist auch ein gültiger Wert.

1..255 bzw. 0..254 wäre Platz für 255.



Aber keine Sorge, auch andere vergessen die 0 gern.
Daher wurde die Zahl 0 in vielen Kulturen auch erst sehr spät erfunden.


Nein das stimmt nicht. Kulturen hin oder her.

Schau noch einmal in den Code, um welchen es geht. Dort wird eine Variable r vom Typ integer auf 0 gesetzt. Und danach wird Pixel für Pixel geschaut, welcher Rotanteil vorliegt. Dieser Rotanteil wird jedes Mal zu r addiert.

Nun wollen wir herausfinden, wann r frühestens überläuft. r läuft in Kulturen wo der Rotanteil aller Pixel immer 0 ist gar nie über, da r konstant 0 bleibt. D.h. in solchen Kulturen kannst du unendlich viele Rotanteile zu r addieren.

Es gibt aber auch Kulturen (zum Beispiel bei gewissen Kulturen im Pferdekopfnebel), in welchen der Rotanteil aller Pixel immer maximal maxbyte = 11111111(bin) = 255(dec) ist.
Wenn du dir jetzt überlegen willst, wann es bei der Summenbildung frühestens knallt, dann musst du einen Taschenrechner zur Hand nehmen und eine 0 eintippen.

Nun addierst du 255 für Pixel 1, 255 für Pixel 2, 255 für Pixel 3...
Nach p Pixeln bist du bei p*255 angelangt.

Irgendwann fragst dich, wann p*255 > maxint erreicht wird.

Diese Ungleichung kannst du lösen:

p>maxint/255.

Die Lösung lautet: Nach p>maxint/255 Pixeln läuft r über.

Michael II 11. Mai 2021 19:54

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Amateurprofi (Beitrag 1489206)
@KodeZwerg:

Die Methode, das Bild auf 1x1 Pixel zu reduzieren, liefert eine andere Durchschnittsfarbe.


Ich nehme auch an, dass GDI+ beim Skalieren auf 1x1 nicht oft die gleiche Durchschnittsfarbe berechnet. Du könntest ja auch auf ein 10x10 oder ähnlich skalieren und dort rechnen.

Die 1x1 Bitmap Farbe hängt natürlich u.a. vom verwendeten Skalier-Algorithmus ab.

Bliebe die Frage: Welche Farbe ein Mensch als die "bessere" Durchschnittsfarbe bewerten würde.

Hast du irgendwo einen Link auf wissenschaftliche Literatur (wäre interessant), wo sowas wie eine "Durchschnittsfarbe" besprochen wird. Ich nehme an, dass in der Grafikbranche nicht einfach über RGB addiert und der Mittelwert genommen wird. Da werden doch sicher andere Modelle "bemüht"?

KodeZwerg 11. Mai 2021 19:55

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
@Amateurprofi: Ich war da auch blind, es klappt nun bestens!
@Michael II: Ich habe es nun hinbekommen das alles funktioniert! Danke mit den Alpha-Kanal Tipp, das hat mich irgendwie auf die richtige Spur geführt auch wenn ich teilweise nur Bahnhof verstanden habe ;)

Im Anhang, für alle Interessierten, meine Testergebnisse bei einem Durchlauf incl Abbildung des GDI+ codes. (GetRed() GetGreen() GetBlue() war die Lösung, so hoffe ich jedenfalls)

Ich muss fairer Weise sagen ich die Topic falsch betitelt habe, es müsste nicht Bitmap sondern Bild heißen.

Ich werde es nochmal testen wenn ich eine "TBitmap bzw HBITMAP" Konvertierung eingepflanzt habe damit es mehr als nur reine Bitmaps annehmen kann.
Ausgangspunkt sollte in allen Fällen ein WIC-Image sein, damit ich alles was Microsoft verarbeitet auch unterstützen kann.
Diese Konvertierung von WIC nach ein passendes Bitmap-Format wird sich dann natürlich negativ auswirken und wahrscheinlich den jetzigen Vorsprung zumindest etwas egalisieren.

Danke Euch beiden für diese sehr Interessanten Beiträge!!

KodeZwerg 11. Mai 2021 20:00

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Michael II (Beitrag 1489208)
Hast du irgendwo einen Link auf wissenschaftliche Literatur (wäre interessant), wo sowas wie eine "Durchschnittsfarbe" besprochen wird. Ich nehme an, dass in der Grafikbranche nicht einfach über RGB addiert und der Mittelwert genommen wird. Da werden doch sicher andere Modelle "bemüht"?

Nein leider nicht, ich habe mich da auf mein Bauchgefühl verlassen das man es so machen könnte, einfach angefangen zu tippsen ohne Recherche(!), rein Augenscheinlich betrachtet, liefert mir TiGü's verfahren (mit meiner simplen berechnung) sehr gute Ergebnisse.
Reine Bitmaps habe ich gar keine zum Testen damit ich auch Eure (Assembler und GDI+) besser testen kann.

KodeZwerg 11. Mai 2021 20:15

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Amateurprofi (Beitrag 1489206)
In #16 wurde in den Raum geworfen, ob man nicht einfach ein Resize auf 1x1 Pixel machen kann.
Ich hab das mal geprüft und in meiner Funktion TestGetAvgColor; vor dem Bmp.Free folgendes eingefügt:
Scheint zu funktionieren, was aber auch daran liegen könnte, dass in der Testprozedur alle Pixel die gleiche Farbe haben.
Korrektur: Hab es gerade mit einem echten Bild geprüft.
Die Methode, das Bild auf 1x1 Pixel zu reduzieren, liefert eine andere Durchschnittsfarbe.

Danke für den Test! Das schonmal vorweg. :thumb:
Anstelle auf 1x1, wäre meine Überlegung ein sinnvolles Resize erst dann durchzuführen wenn Int64 für die Berechnung nicht mehr ausreicht.
Es wurden zwar viele Zahlen in den Raum geworfen, aber wie sollte man da Sinnvoll vorgehen?...

Ein Bild besteht ja aus zwei Dimensionen, ein Int64 ist nur eine.
Was ich meine, gibt es eine logik die so etwas berechnen kann, ein bild kann ja 100 million pixel Hoch aber nur 1 pixel breit sein.
Andersrum genauso.
Oder eben in beide Dimensionen sehr sehr viele Pixel besitzen.
Also es gäbe halt mehr als nur eine Möglichkeit diese berechnung hier zum platzen zu bringen.
Ein Resize auf eine Dimension die es nicht zum platzen bringt, das wäre das Sahnetörtchen ;-)

Michael II 11. Mai 2021 20:40

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1489212)
Resize erst dann durchzuführen wenn Int64 für die Berechnung nicht mehr ausreicht.

Da deine RGB Werte nie negativ sind kannst du statt Int64 auch UInt64 verwenden. Dann hast du alle 64Bit (und nicht nur 63Bit) zur Verfügung für die Summenbildung.

High(Uint64)=2^64-1=18446744073709551615

Da du momentan RGB Werte im Bereich 0..255 verwendest, kannst du nach dem grössten p (Anzahl Pixel deiner Bitmap) suchen, welches
p*255 <= 1844674407370955165
erfüllt.

p(max)=72’340’172’838’076’673

D.h. du kannst enorm grosse Bitmaps (mit maximal p(max) Pixeln) verarbeiten.


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:25 Uhr.
Seite 4 von 6   « Erste     234 56      

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