![]() |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
64 Bit compiliert, kannst Int64 benutzen und hast genug Platz.
Hier wird es ja von der CPU behandelt. In Win32 machen das Funktionen in der System.pas. OK, die Addition geht dennoch recht schnell und am Ende das eine DVI, da stört der etwas langsamere Code kaum. |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Bitte alle Kirchen im Dorf lassen, er verwendetet das für Desktophintergründe.
|
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Zitat:
Zitat:
Schneller als via Scanline geht's fast nicht. Bei Verwendung von int64 muss man - wie himitsu schreibt - schon kurz nachdenken, wie das Verhalten in Bezug auf Speed unter 32 bzw. 64 bit ist. Bei Verwendung von integer für die rgb Werte stösst die Funktion auch bei diesem "Kirche im Dorf Verwendungszweck" zu rasch an ihre Grenzen. r kann maximal den Wert maxint=2^31-1 speichern. Wenn in jedem Pixel der Bitmap der Rotwert 255 beträgt, kannst du also maximal trunc((2^31-1)/255) Pixelwerte zusammenzählen. Bei einer quadratischen Bitmap bist du bei einer Seitenlänge s > sqrt((2^31-1)/255)) am Ende der Fahnenstange angelangt. Eine 2901*2901 Bitmap ist also gerade noch berechenbar. Grösser darf sie nicht sein. Oder direkt auf den Anwendungszweck bezogen: 4K Monitor: r erreicht maximal 3840*2160*255 = 2’115’072’000 < 2^31-1, geht gerade noch. Bereits bei meinem 8K Monitor, den ich nicht habe, fliegt's einem um die Ohren. Es ist mir bewusst, dass wir hier einen "Nebenschauplatz" diskutieren. Aber es ist wichtig, dass Code, welcher die eigenen vier Wände verlässt so geschrieben wird, dass er auch in einer Flugzeugsoftware eingebaut zuverlässig funktioniert. |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Bei einem einfarbigen Hintergrund gibt es aber kein Bitmap-Wallpaper, da wird ein RGB-Wert aus der Registry gelesen.
Wer macht sich denn einen 8K Hintergrund als einfarbiges (!) Bitmap? Wer wirklich sicher gehen will, der ändert sich den Code halt. Kirchen -> Dörfer! |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Ja @TiGü, ich nutze das ausschließlich für das Theming Projekt aber gebe natürlich Recht das es diese "8k * 1 Farbe" Möglichkeit irgendwie geben könnte. Sinnfrei aber ja.
Auf Int64 ist es bereits umgestellt, aber auch das hat seine Grenzen. Mir fehlt da die Erfahrung wie man es "abfangen" könnte damit die Berechnung einfach ab einer gewissen Zahl aufhört weiterzuzählen, würde es aber gerne zur Sicherheit mit einbauen.
Delphi-Quellcode:
Macht man das so in etwa? (Nur hier im Edit getippst...)
var
r: Int64; i: Cardinal; begin r := 0; for i := 0 to High(Cardinal) do begin if r < High(Int64) then r := r + 1; end; end; |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Interessante Diskussion. Da kommt mir aber noch eine abstruse Idee: Man macht ein "Resize" des Bildes auf 1x1 Pixel großes Bild und schaut sich dann nur noch dieses Pixel an. Läuft das Resize auf der GPU wäre das auch ganz schon flott.
Ich habe natürlich keine Ahnung, wieviel Mühe sich so ein Bildverkleinerungsalgo macht, wenn das Ziel nur noch 1 Pixel groß ist... Viele Grüße Michael :duck: |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Zitat:
|
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Zitat:
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.
Delphi-Quellcode:
FUNCTION AvgColor(P,LO,W,H:NativeInt):TColor;
// P : Zeiger auf das erste Pixel der ersten Zeile einer Bitmap // LO : Offset (in Bytes) auf die jeweils nächste Zeit // W : Breite der Bitmap // H : Höhe der Bitmap {$IFDEF CPUX86} const OfsBlueLo=0; OfsBlueHi=OfsBlueLo+4; OfsGreenLo=OfsBlueHi+4; OfsGreenHi=OfsGreenLo+4; OfsRedLo=OfsGreenHi+4; OfsRedHi=OfsRedLo+4; OfsCount=OfsRedHi+4; OfsH=OfsCount+4; OfsLO=OfsH+4; OfsStack=OfsLO+4; {$ENDIF} asm {$IFDEF CPUX86}// EAX=P, EDX=LO, ECX=W, Stack=H // Register retten push ebx push edi push esi // LO, H und Anzahl Pixel auf Stack legen push edx // LO mov ebx,H push ebx // H imul ebx,ecx push ebx // Anzahl Pixel // Summen auf Stack push 0 push 0 push 0 push 0 push 0 push 0 // ESI hinter erste Zeile lea ebp,[ecx+ecx*2] lea esi,[eax+ebp] neg ebp // Summen ermitteln @Loop1: mov edi,ebp xor ebx,ebx xor ecx,ecx xor edx,edx @Loop2: movzx eax,byte[esi+edi] // Blue add ebx,eax movzx eax,byte[esi+edi+1] // Green add ecx,eax movzx eax,byte[esi+edi+2] // Red add edx,eax add edi,3 jl @Loop2 // Nächstes Pixel add [esp+OfsBlueLo],ebx // Summe Blue adc [esp+OfsBlueHi],0 add [esp+OfsGreenLo],ecx // Summe Green adc [esp+OfsGreenHi],0 add [esp+OfsRedLo],edx // Summe Red adc [esp+OfsRedHi],0 // Zeiger auf nächste Zeile add esi,[esp+OfsLO]; dec [esp+OfsH] jnz @Loop1 // AvgWerte erbitteln mov eax,[esp+OfsBlueLo] mov edx,[esp+OfsBlueHi] div [esp+OfsCount] movzx ecx,al shl ecx,16 mov eax,[esp+OfsGreenLo] mov edx,[esp+OfsGreenHi] div [esp+OfsCount] mov ch,al mov eax,[esp+OfsRedLo] mov edx,[esp+OfsRedHi] div [esp+OfsCount] mov cl,al mov eax,ecx // Result=AvgColor // Stack bereinigen add esp,OfsStack // Register wieder herstellen pop esi pop edi pop ebx {$ELSE} // RCX=P, RDX=LO, R8=W, R9=H push r12 push r13 push r14 // Anzahl Pixel in R13 mov r13,R8 imul R13,R9 // R11 hinter erste Zeile, R12=-W*3 lea r12,[r8+r8*2] lea r11,[rcx+r12] neg r12 // Summen ermitteln xor rcx,rcx // Summe Blue xor r8,r8 // Summe Green xor r10,r10 // Summe Red @Loop1: mov r14,r12 @Loop2: movzx rax,byte[r11+r14] // Blue add rcx,rax movzx rax,byte[r11+r14+1] // Green add r8,rax movzx rax,byte[r11+r14+2] // Red add r10,rax add r14,3 jl @Loop2 // Nächstes Pixel // Zeiger auf nächste Zeile add r11,rdx; dec r9 jnz @Loop1 // AvgWerte erbitteln mov rax,rcx // Blue xor rdx,rdx div r13 movzx rcx,al shl rcx,16 mov rax,r8 // Green xor rdx,rdx div r13 mov ch,al mov rax,r10 xor rdx,rdx div r13 mov cl,al mov rax,rcx // Result=AvgColor // Register wieder herstellen pop r14 pop r13 pop r12 {$ENDIF} end;
Delphi-Quellcode:
FUNCTION GetAvgColor(Bmp:TBitmap):TColor; overload;
var LO,P:NativeInt; begin Assert(Bmp.PixelFormat=pf24bit); Assert(Bmp.Width>0); Assert(Bmp.Height>0); P:=NativeInt(Bmp.ScanLine[0]); LO:=NativeInt(Bmp.ScanLine[1])-P; Result:=AvgColor(P,LO,Bmp.Width,Bmp.Height); end;
Delphi-Quellcode:
FUNCTION GetAvgColor(Dsn:String):TColor; overload;
var Bmp:TBitmap; begin Result:=0; if not FileExists(Dsn) then raise Exception.Create('Datei "'+Dsn+'" nicht gefunden'); Bmp:=TBitmap.Create; Bmp.LoadFromFile(Dsn); Result:=GetAvgColor(Bmp); Bmp.Free; end;
Delphi-Quellcode:
// Aus #3 TiGü (Filename hier als Parameter statt lokale Variable)
function GetAvgBmpColor(Filename:String): TColor; type TRgbTriple = packed record // do not change the order of the fields, do not add any fields Blue: Byte; Green: Byte; Red: Byte; end; TRgbTripleArray = packed array[0..MaxInt div SizeOf(TRgbTriple) - 1] of TRgbTriple; PRgbTripleArray = ^TRgbTripleArray; var x, y: Integer; r, g, b: Integer; Pixel: TRgbTriple; Bmp: TBitmap; // Filename: string; wic: TWICImage; Resolution: Integer; ScanLinePtr: Pointer; begin Result := 0; //Filename := 'Der magische Pfad'; if not FileExists(Filename) then Exit; bmp := TBitmap.Create; wic := TWICImage.Create; try wic.LoadFromFile(Filename); bmp.Assign(wic); bmp.PixelFormat := pf24bit; r := 0; g := 0; b := 0; Assert(bmp.PixelFormat = pf24bit); for y := 0 to Pred(Bmp.Height) do begin ScanLinePtr := bmp.Scanline[y]; // der springende Punkt! for x := 0 to Pred(Bmp.Width) do begin Pixel := PRgbTripleArray(ScanLinePtr)^[x]; r := r + Pixel.Red; g := g + Pixel.Green; b := b + Pixel.Blue; end; end; Resolution := (bmp.Width * bmp.Height); r := r div Resolution; g := g div Resolution; b := b div Resolution; Result := RGB(r, g, b); finally bmp.Free; wic.Free; end; end;
Delphi-Quellcode:
PROCEDURE TestGetAvgColor;
const Width=2900; Height=2900; Color=$010203; var T0,T1,T2:Cardinal; R:TRect; Bmp:TBitmap; CL1,CL2:TColor; Dsn:String; begin Bmp:=TBitmap.Create; Bmp.PixelFormat:=pf24Bit; Bmp.SetSize(Width,Height); SetRect(R,0,0,Bmp.Width,Bmp.Height); Bmp.Canvas.Brush.Color:=Color; Bmp.Canvas.FillRect(R); Dsn:=ExtractFilePath(ParamStr(0))+'Test.bmp'; Bmp.SaveToFile(Dsn); Bmp.Free; T0:=GetTickCount; CL1:=GetAvgColor(Dsn); T1:=GetTickCount; CL2:=GetAvgBmpColor(Dsn); T2:=GetTickCount; ShowMessage('$'+IntToHex(CL1,8)+' '+IntToStr(T1-T0)+#13+ '$'+IntToHex(CL2,8)+' '+IntToStr(T2-T1)); end; |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Zitat:
vom Prinzip her so:
Delphi-Quellcode:
r := 0;
Resolution := (bmp.Width * bmp.Height); for i := to do r := r + Pixelwert[i].red / Resolution; r := round(r); |
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Mathematisch, von den Grenzen der Floats, macht es leider keinen Unterschied, ob man jedes Pixel dividiert und addiert, oder ob man erst alles addiert und erst am Ende dividiert.
Lezteres ist aber schneller, da "ganz" oft dividieren natürlich langsamter ist, als nur Einmal. Bei ganz ganz ganz vielen Pixeln ist der Wert irgendwann so groß, dass die nächste Addition des kleinen Wertes, abgeschnitten wird. Aber Double hat mit 52 Bit (52+11) mehr als der 32 Bit-Integer, somit dauert es da länger, bis zum Rechenfehler/überlauf, und gegenüber 64 Bit, und dessen Integeroperation (64 Bit-compilat), ist Float in der FPU dann wieder langsamer. (ja, eine einzelne FPU gibt es physisch nicht mehr) Single mit 23 Bits (23+8) raucht schon zu etwas früher ab. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:57 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