Registriert seit: 17. Nov 2005
Ort: Hamburg
1.061 Beiträge
Delphi XE2 Professional
|
AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
11. Mai 2021, 16:16
Guten morgen Gemeinde!
Ein Neuer Tag, ein neues problem
...
Ich versuche aus einem Bitmap eine Durchschnittsfarbe zu ermitteln.
...
Hier ist das was ich aus seinem guten Beispiel #2 gemacht habe, es funktioniert, aber doch recht langsam:
...
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.
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;
Gruß, Klaus
Die Titanic wurde von Profis gebaut,
die Arche Noah von einem Amateur.
... Und dieser Beitrag vom Amateurprofi....
Geändert von Amateurprofi (12. Mai 2021 um 11:11 Uhr)
Grund: Fehker in FUNCTION GetAvgColor(Dsn:String):TColor; korrigiert
|