Delphi-PRAXiS
Seite 1 von 2  1 2   

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)

KodeZwerg 10. Mai 2021 08:53

Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Guten morgen Gemeinde!

Ein Neuer Tag, ein neues problem :-)

Ich versuche aus einem Bitmap eine Durchschnittsfarbe zu ermitteln.

Bei dummzeuch wurde ich zwar fündig was mir bereits sehr half es zu beschleunigen, aber seine letzte Optimierung bekomme ich einfach nicht hin.

Hier ist das was ich aus seinem guten Beispiel #2 gemacht habe, es funktioniert, aber doch recht langsam:
Delphi-Quellcode:
function TfrmMain.GetAvgBmpColor: 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;
  fs: TFileStream;
  wic: TWICImage;
  img: TImage;
begin
  Filename := 'X:\Pfad\Bildname.ext';
  if not FileExists(Filename) then
    Exit;
  bmp := TBitmap.Create;
  wic := TWICImage.Create;
  img := TImage.Create(Self);
  fs := TFileStream.Create(Filename, fmOpenRead);
  try
    bmp.PixelFormat := pf24bit;
    fs.Position := 0;
    wic.LoadFromStream(fs);
    Img.Picture.Assign(wic);
    bmp.Width := Img.Picture.Width;
    bmp.Height := Img.Picture.Height;
    bmp.Canvas.Draw(0, 0, Img.Picture.Graphic);
    r := 0; g := 0; b := 0;
    Assert(bmp.PixelFormat = pf24bit);
    for y := 0 to Pred(bmp.Height) do
      begin
        for x := 0 to Pred(bmp.Width) do
          begin
            Pixel := PRgbTripleArray(bmp.Scanline[y])^[x];
            r := r + Pixel.Red;
            g := g + Pixel.Green;
            b := b + Pixel.Blue;
          end;
      end;
    r := r div (bmp.Width * bmp.Height);
    g := g div (bmp.Width * bmp.Height);
    b := b div (bmp.Width * bmp.Height);
  finally
    Result := RGB(r, g, b);
    bmp.Free;
    fs.Free;
    wic.Free;
    img.Free;
  end;
end;
Sorry für meine schludrige code gestaltung, ist erst alpha phase.


Weiß jemand wie man das eventuell mit diesem Code #3 hinbekommt?
Delphi-Quellcode:
// if you are using Delphi 2007 or older you need to correct the NativeInt declaration from 8 bytes to 4 bytes:
{$IF SizeOf(Pointer) = 4}
type
  NativeInt = Integer;
{$IFEND}

function AddToPtr(const _Ptr: Pointer; _Offset: NativeInt): Pointer; inline;
begin
  Result := Pointer(NativeInt(_Ptr) + _Offset);
end;

function PtrDiff(const _Ptr1, _Ptr2: Pointer): NativeInt; inline;
begin
  Result := NativeInt(_Ptr1) - NativeInt(_Ptr2);
end;

var
  BytesPerPixel: NativeInt;
  InScanLine0: Pointer;
  InBytesPerLine: NativeInt;
  OutScanLine0: Pointer;
  InBytesPerLine: NativeInt;
  InPixel: PRgbTriple;
  OutPixel: PRgbTriple;
// ...
  BytesPerPixel := SizeOf(Pixel)
  InScanLine0 := InBmp.ScanLine[0];
  InBytesPerLine := NativeInt(_InBmp.ScanLine[1]) - NativeInt(InScanLine0);
  OutScanLine0 := _OutputBmp.ScanLine[0];
  OutBytesPerLine := NativeInt(_OutBmp.ScanLine[1]) - NativeInt(OutScanLine0);
  OutPixel := OutScanLine0;
  for y := 0 to Height - 1 do begin
    for x := 0 to Width - 1 do begin
      InPixel := AddToPtr(InScanLine0, InBytesPerLine * y + x * BytesPerPixel);
      Pixel := InPixel^;
      doSomething(Pixel);
      OutPixel := AddToPtr(OutScanLine0, OutBytesPerLine * y + x * BytesPerPixel);
      OutPixel^ := Pixel;
    end;
  end;
Ich bekomme immer den Fehler das "Pixel" nicht mit "InPixel^" kompatibel ist und weiß gerade nicht weiter.

Renate Schaaf 10. Mai 2021 09:21

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1488977)

Ich bekomme immer den Fehler das "Pixel" nicht mit "InPixel^" kompatibel ist und weiß gerade nicht weiter.

Ich finde ums Verrecken nicht, wo du Pixel deklariert hast. Müsste TRGBTriple sein. Ansonsten: OutPixel brauchst du nicht, einfach die Pixel-Werte aufsummieren wie vorher etc., brauch ich wohl nicht zu erklären.

TiGü 10. Mai 2021 09:22

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Delphi-Quellcode:
function TForm3.GetAvgBmpColor: 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;

dummzeuch 10. Mai 2021 09:24

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1488977)
Weiß jemand wie man das eventuell mit diesem Code #3 hinbekommt?
Delphi-Quellcode:
// if you are using Delphi 2007 or older you need to correct the NativeInt declaration from 8 bytes to 4 bytes:
{$IF SizeOf(Pointer) = 4}
type
  NativeInt = Integer;
{$IFEND}

function AddToPtr(const _Ptr: Pointer; _Offset: NativeInt): Pointer; inline;
begin
  Result := Pointer(NativeInt(_Ptr) + _Offset);
end;

function PtrDiff(const _Ptr1, _Ptr2: Pointer): NativeInt; inline;
begin
  Result := NativeInt(_Ptr1) - NativeInt(_Ptr2);
end;

var
  BytesPerPixel: NativeInt;
  InScanLine0: Pointer;
  InBytesPerLine: NativeInt;
  OutScanLine0: Pointer;
  InBytesPerLine: NativeInt;
  InPixel: PRgbTriple;
  OutPixel: PRgbTriple;
// ...
  BytesPerPixel := SizeOf(Pixel)
  InScanLine0 := InBmp.ScanLine[0];
  InBytesPerLine := NativeInt(_InBmp.ScanLine[1]) - NativeInt(InScanLine0);
  OutScanLine0 := _OutputBmp.ScanLine[0];
  OutBytesPerLine := NativeInt(_OutBmp.ScanLine[1]) - NativeInt(OutScanLine0);
  OutPixel := OutScanLine0;
  for y := 0 to Height - 1 do begin
    for x := 0 to Width - 1 do begin
      InPixel := AddToPtr(InScanLine0, InBytesPerLine * y + x * BytesPerPixel);
      Pixel := InPixel^;
      doSomething(Pixel);
      OutPixel := AddToPtr(OutScanLine0, OutBytesPerLine * y + x * BytesPerPixel);
      OutPixel^ := Pixel;
    end;
  end;
Ich bekomme immer den Fehler das "Pixel" nicht mit "InPixel^" kompatibel ist und weiß gerade nicht weiter.

Wo ist denn die Variable Pixel deklariert und als was? Wenn es die irgendwo in der RTL/VCL oder sonstigen Units gibt, die Du einbindest, könnte das die Fehlermeldung erklären.

KodeZwerg 10. Mai 2021 09:32

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Renate Schaaf (Beitrag 1488984)
Zitat:

Zitat von KodeZwerg (Beitrag 1488977)

Ich bekomme immer den Fehler das "Pixel" nicht mit "InPixel^" kompatibel ist und weiß gerade nicht weiter.

Ich finde ums Verrecken nicht, wo du Pixel deklariert hast. Müsste TRGBTriple sein. Ansonsten: OutPixel brauchst du nicht, einfach die Pixel-Werte aufsummieren wie vorher etc., brauch ich wohl nicht zu erklären.

Zitat:

Zitat von dummzeuch (Beitrag 1488986)
Wo ist denn die Variable Pixel deklariert und als was? Wenn es die irgendwo in der RTL/VCL oder sonstigen Units gibt, die Du einbindest, könnte das die Fehlermeldung erklären.


Der erste code ist aus seinem blog nur halt für mich angepasst.
Der zweite code ist 1:1 aus seinem blog, es sind 3 teile die aufeinander aufbauen.

//edit
ich bin ja auch noch nicht ganz wach, hallo dummzeuch, DANKE für deine blog!!

@TiGü: WAHNSINN!! Danke für code-optimierung plus das einarbeiten des dritten codes!!

Michael II 10. Mai 2021 09:50

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
In einigen der hier geposteten Beispiele befürchte ich bei grossen Bitmaps Integerüberlauf in r,g,b.

Wenn zum Beispiel jedes Pixel einer Bitmap einen Rotwert=255 aufweist, dann läuft r nach >maxint/255 Pixeln über.

Oder etwas anders geschrieben: Eine quadratische Bitmap mit >2901 Pixel Seitenlänge und Rotwert=255 wäre nicht gut...

TiGü 10. Mai 2021 10:54

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Du kannst dir noch ein paar Millisekunden sparen, wenn du nicht den "Umweg" über TBitmap machst:

Delphi-Quellcode:
uses
    Winapi.Wincodec;

type
    TWICImageHelper = class helper for TWICImage
        function GetAverageColor: TColor;
    end;

function TForm3.GetAvgBmpColor: TColor;
var
  Filename: string;
  wic: TWICImage;
begin
  Result := 0;
  Filename := 'Dein Pfad zur Bilddatei';
  if not FileExists(Filename) then
    Exit;
  wic := TWICImage.Create;
  try
    wic.LoadFromFile(Filename);
    Result := wic.GetAverageColor;
  finally
    wic.Free;
  end;
end;

{ TWICImageHelper }

function TWICImageHelper.GetAverageColor: TColor;
type
  // copy from Vcl.Graphics
  PRGBQuadArray = ^TRGBQuadArray;
  TRGBQuadArray = array [Byte] of Winapi.Windows.TRGBQuad;
var
  LWicBitmap: IWICBitmapSource;
  Stride: Cardinal;
  Buffer: array of Byte;
  x, y: Integer;
  BGRAPixel: TRGBQuad;
  r, g, b, Resolution, LBytesPerScanline: Integer;
  ScanLinePtr: Pointer;
begin
  Result := 0;
  with Self do
  begin
    if FWicBitmap = nil then
      Exit;

    FWicBitmap.GetSize(FWidth, FHeight);

    Stride := FWidth * 4;
    SetLength(Buffer, Stride * FHeight);

    WICConvertBitmapSource(GUID_WICPixelFormat32bppBGRA, FWicBitmap, LWicBitmap);
    LWicBitmap.CopyPixels(nil, Stride, Length(Buffer), @Buffer[0]);

    r := 0;
    g := 0;
    b := 0;
    LBytesPerScanline := BytesPerScanline(FWidth, 32, 32);
    for y := 0 to FHeight - 1 do
    begin
      ScanLinePtr := PByte(@Buffer[0]) + y * LBytesPerScanline;
      for x := 0 to FWidth - 1 do
      begin
        BGRAPixel := PRGBQuadArray(ScanLinePtr)^[x];
        r := r + BGRAPixel.rgbRed;
        g := g + BGRAPixel.rgbGreen;
        b := b + BGRAPixel.rgbBlue;
      end;
    end;
    Resolution := FWidth * FHeight;
  end;
  r := r div Resolution;
  g := g div Resolution;
  b := b div Resolution;
  Result := RGB(r, g, b);
end;
Kam bei mir zumindest das gleiche Ergebnis bei rum, bitte nachprüfen!

KodeZwerg 10. Mai 2021 11:18

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Michael II (Beitrag 1488991)
In einigen der hier geposteten Beispiele befürchte ich bei grossen Bitmaps Integerüberlauf in r,g,b.

Wenn zum Beispiel jedes Pixel einer Bitmap einen Rotwert=255 aufweist, dann läuft r nach >maxint/255 Pixeln über.

Oder etwas anders geschrieben: Eine quadratische Bitmap mit >2901 Pixel Seitenlänge und Rotwert=255 wäre nicht gut...

Danke für diesen Hinweis, mit sowas habe ich nicht gerechnet!
Könnte eine Änderung auf
Delphi-Quellcode:
var r, g, b: Int64;
da helfen?

Zitat:

Zitat von TiGü (Beitrag 1488998)
Du kannst dir noch ein paar Millisekunden sparen, wenn du nicht den "Umweg" über TBitmap machst:
Kam bei mir zumindest das gleiche Ergebnis bei rum, bitte nachprüfen!

Ein paar ms?? Du willst mich jetzt veralbern, das Ding ist enorm schnell! (habe es jetzt nicht gebencht, vorher konnte ich noch zur Tasse greifen, nun ist es bereits nach dem anklicken ausgeführt, Hammer!) Bei mir sind die Werte auch passend/stimmen mit ur-version plus deinem update + deinem update!
Vom Geschwindigkeitsvergleich, meine ur-ur Version war ein Trabbi, danach fand ich dummzeuchs blog und ich hatte einen Sportwagen. Deine erste version war ein Tiefergelegter aufgemotzter Sportwagen, nun ist es ein Formel-1 Wagen!

Ich nahm in Post #1 nur das WIC weil ich vorher nicht wissen kann was für ein Bildformat reinkommt, so als universal Empfänger halt, Konvertierung zu bmp nahm ich weil ich da wusste das es ein ScanLine gibt für die Punkte.

Damit hast Du mir echt eine riesen Freude gemacht! Ganz herzlich vielen Dank für die Mühe, das ist Top! :thumb:

TiGü 10. Mai 2021 11:41

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Jo, gerne!
Was dem einen sein Problem, ist mein tägliches Code Kata. :wink:

Man kann das bestimmt noch weiter optimieren.
Bei der Addition der drei 32-Bit-Zahlen (oder 64-Bit, wenn man in keinen Überlauf will) gibt es bestimmt irgendeine kluge x64-Assemblerfunktion.
Oder man schreibt die Schleifen um, so das nur jedes zweite oder vierte Pixel angeguckt wird.
Je nachdem, wie genau man den Durchschnitt braucht.

Rollo62 11. Mai 2021 11:19

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Michael II (Beitrag 1488991)
In einigen der hier geposteten Beispiele befürchte ich bei grossen Bitmaps Integerüberlauf in r,g,b.

Ja das sprang mir auch gerade ins Auge.
Trotzdem, theoretisch gibt es Überläufe erst ab Bildgrößen von 24000x24000.
Ich denke es ist noch ein bischen Zeit bis dahin :stupid:

Aber ich würde auch einen Test o.ä. anlegen, der aufpoppt falls solche Größen mal erreicht werden.

himitsu 11. Mai 2021 13:09

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.

TiGü 11. Mai 2021 13:21

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Bitte alle Kirchen im Dorf lassen, er verwendetet das für Desktophintergründe.

Michael II 11. Mai 2021 14:03

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Rollo62 (Beitrag 1489128)
Zitat:

Zitat von Michael II (Beitrag 1488991)
In einigen der hier geposteten Beispiele befürchte ich bei grossen Bitmaps Integerüberlauf in r,g,b.

Ja das sprang mir auch gerade ins Auge.
Trotzdem, theoretisch gibt es Überläufe erst ab Bildgrößen von 24000x24000.
Ich denke es ist noch ein bischen Zeit bis dahin :stupid:

Aber ich würde auch einen Test o.ä. anlegen, der aufpoppt falls solche Größen mal erreicht werden.

Zitat:

Zitat von TiGü (Beitrag 1489137)
Bitte alle Kirchen im Dorf lassen, er verwendetet das für Desktophintergründe.

Und genau deshalb ist es wichtig, dass der Fragende eine wie verlangt schnelle und funktionierende Lösung hat.
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.

TiGü 11. Mai 2021 15:31

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!

KodeZwerg 11. Mai 2021 15:47

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:
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;
Macht man das so in etwa? (Nur hier im Edit getippst...)

grizzly 11. Mai 2021 15:57

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:

Michael II 11. Mai 2021 16:01

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von TiGü (Beitrag 1489151)
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!

OK ich hatte überlesen, dass KodeZwerg den Durchschnittswert aus einer einfarbigen Bitmap errechnen wollte. Wenn dem echt so ist (ich verstand sein Anliegen so, dass er den RGB-Durchschnittswert eines Bild berechnen wollte; das macht ja deine Funktion auch), dann muss KodeZwerg den Durchschnitt nicht berechnen. Bei einem einfarbigen Bitmap reicht es irgend ein Pixel zu nehmen, da ja dann alle gleich sind. Dann gibt es auch beim neuen "TrillionK Monitor" in der Nähe von Alpha Centauri exakt 0 Probleme. Ich glaube wir schreiben aneinander vorbei, ich habe das Problem offensichtlich komplett überschätzt, ich weiss nicht wieso, ich bin hier raus, die meisten sind ja total glücklich ;-).

Amateurprofi 11. Mai 2021 16:16

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1488977)
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;

Jens01 11. Mai 2021 16:24

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Ja das sprang mir auch gerade ins Auge.
Trotzdem, theoretisch gibt es Überläufe erst ab Bildgrößen von 24000x24000.
Ich denke es ist noch ein bischen Zeit bis dahin
Man kann auch einfach den Durchschnittswert aufaddieren.

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);

himitsu 11. Mai 2021 16:52

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.

KodeZwerg 11. Mai 2021 16:57

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
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.

Michael II 11. Mai 2021 16:59

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von grizzly (Beitrag 1489158)
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:

Halt doch noch rasch dazu:
Coole Idee... ;-).

Funktioniert auch gut... und sicher schnell, u.v.a. auch auf dem TrillionK Monitor. Ich habe keine Zeit fürs Messen.

Die Werte sind (was ich auch erwartet habe, GrafikerInnen berechnen den Durchschnittswert wahrscheinlich eher über ein anderes Modell und etwas anders) nicht ganz gleich (wie beim RGB DS Rechnen). Bei einfarbigen Bitmaps aber natürlich identisch.

Man könnte natürlich statt auf ein 1x1 Pixel zu skalieren eine etwas grössere Zielbitmap wählen.

Der Code wird ultrakurz:


GDI+
Delphi-Quellcode:
uses GDIPOBJ, GDIPAPI;

  gr := TGPGraphics.Create( bmap );
  gr.ScaleTransform( 1/bmap.GetWidth, 1/bmap.GetHeight );
  gr.DrawImage( bmap, 0,0 );
  bmap.GetPixel(0,0,col);

Jens01 11. Mai 2021 17:02

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Bei ganz ganz ganz vielen Pixeln ist der Wert irgendwann so groß, dass die nächste Addition des kleinen Wertes, abgeschnitten wird.
r wird nicht größer als 255, oder?

KodeZwerg 11. Mai 2021 17:08

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
So wie ich es verstanden habe fügt sich ein RBG wert aus 3 DWORDs zusammen.

himitsu 11. Mai 2021 17:21

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Es ist ein DWORD, dass aus 3+1 Bytes besteht :wink:

KodeZwerg 11. Mai 2021 17:35

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
@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 um mich zu korrigieren, das wäre nett!


Zitat:

Zitat von himitsu (Beitrag 1489174)
Es ist ein DWORD, dass aus 3+1 Bytes besteht :wink:

Mein Fehler!

dummzeuch 11. Mai 2021 18:28

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1489156)
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.

Man könnte auf einen möglichen Überlauf prüfen und Zwischen-Mittelwerte bilden, die dann hinterher zusammengerechnet werden.

Mathematisch gilt ja:
Code:
 a1 + a2 + a3 + a4    a1    a2    a3    a4     a1 + a2     a3 + a4
------------------- = --- + --- + --- + --- = --------- + ---------
      4                4     4     4     4        4           4
Und bei hohen Zahlen sind evtl. Rundungsfehler eher unwichtig.

Rollo62 11. Mai 2021 19:01

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Michael II (Beitrag 1489144)
maxint=2^31-1 speichern.
s > sqrt((2^31-1)/255))

Oha verrechnet, Du hast ja Recht :oops:
Habe auf die Schnelle mit Cardinal gerechnet, und mangels Taschenrechner nur geschätzt.

Stimmt Integer ist signifikant kleiner, trotzdem würde ich mit dem vollen Umfang und 8-Bit rechnen, wieso -1 ?
s > sqrt( (2^31)/256 ) = sqrt((2^31)/2^8 ) = sqrt( (2^31-8) ) = sqrt( 2^23 ) = 2^( 23 / 2 ) = 2^11.5 = 2896
Also ich komme auf 2896x2896, ist wirklich zu wenig.

Michael II 11. Mai 2021 19:53

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
Zitat:

Zitat von Rollo62 (Beitrag 1489191)
Zitat:

Zitat von Michael II (Beitrag 1489144)
maxint=2^31-1 speichern.
s > sqrt((2^31-1)/255))

Oha verrechnet, Du hast ja Recht :oops:
Habe auf die Schnelle mit Cardinal gerechnet, und mangels Taschenrechner nur geschätzt.

Stimmt Integer ist signifikant kleiner, trotzdem würde ich mit dem vollen Umfang und 8-Bit rechnen, wieso -1 ?
s > sqrt( (2^31)/256 ) = sqrt((2^31)/2^8 ) = sqrt( (2^31-8) ) = sqrt( 2^23 ) = 2^( 23 / 2 ) = 2^11.5 = 2896
Also ich komme auf 2896x2896, ist wirklich zu wenig.

Cardinal ist in D32bit und D64bit 32Bits lang, ohne Vorzeichen. Du hast also rund zwei Mal so viele Zahlen>0 wie bei integer - und damit sqrt(2) mal mehr maximale Bitmap-Seitenlänge.

Zu deiner Frage wegen wieso -1. Du hast das Vorzeichenbit vergessen. Die grösste positive integer Zahl (maxint) sieht so aus m = 011111111 11111111 11111111 11111111. Wenn du zu m 1 addierst, hättest du 10000000 00000000 00000000 00000000 = 2^32. Die Zahl vor 2^32 hat damit den Wert maxint=2^32-1. 2^32 entspricht bei integer dem negativen Wert -2^32.
Oder wenn du's lieber via geometrische Reihe rechnen willst:
011111111 11111111 11111111 11111111 hat den Wert
s = 2^0+2^1+2^2+....+2^30
und 2s = 2^1+...+2^31
Subtrahierst du von Zeile 2 Zeile 1 ergibt sich s=2^31-2^0 = 2^31-1.

Zu deinem Einwand, man sollte durch 2^8=256 teilen. Kurze Antwort: Nein. Lange Antwort: r liegt im Bereich [0..255] und nicht im Bereich [0..256]. Du musst dir also überlegen wie oft 255 in 2^32-1 Platz hat => (2^32-1)/255 Mal.

himitsu 11. Mai 2021 19:58

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln
 
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.

Amateurprofi 11. Mai 2021 19: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 20: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 20: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 20: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 20: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 20: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 20: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 21: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 21: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 21: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 14:13 Uhr.
Seite 1 von 2  1 2   

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