AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia Delphi Durchschnittsfarbe eines Bitmap "schnell" ermitteln
Thema durchsuchen
Ansicht
Themen-Optionen

Durchschnittsfarbe eines Bitmap "schnell" ermitteln

Ein Thema von KodeZwerg · begonnen am 10. Mai 2021 · letzter Beitrag vom 12. Mai 2021
Antwort Antwort
Seite 1 von 2  1 2      
Amateurprofi

Registriert seit: 17. Nov 2005
Ort: Hamburg
1.111 Beiträge
 
Delphi XE2 Professional
 
#1

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 19:44
@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.
Gruß, Klaus
Die Titanic wurde von Profis gebaut,
die Arche Noah von einem Amateur.
... Und dieser Beitrag vom Amateurprofi....
  Mit Zitat antworten Zitat
Michael II

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

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 19:54
@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"?
Michael Gasser
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 20:00
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.
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Amateurprofi

Registriert seit: 17. Nov 2005
Ort: Hamburg
1.111 Beiträge
 
Delphi XE2 Professional
 
#4

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 21:13
@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"?
Nein, ich hab darüber keine Literatur.
In einem anderen Projekt habe ich auch mal Durchschnittswerte gebildet, indem ich jedes Pixel in HSB umgerechnet, und die AvgWerte für H, S, B ermittelt und anschließend die Avg-HSB Werte wieder in RGB umgerechnet habe.
Egal wie ich die Avg-Werte errechnet habe, irgendwie wirkte die Avg-Farbe immer, wie soll ich es ausdrücken, "schmutzig". Aber vielleicht ist das einfach so bei einer Durchschnittsfarbe.
Gruß, Klaus
Die Titanic wurde von Profis gebaut,
die Arche Noah von einem Amateur.
... Und dieser Beitrag vom Amateurprofi....
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 22:33
@Michael II: Ich habe eine mini Erklärung hier gefunden, aber verstehe den Inhalt nicht so ganz. Da geht es um die Farbannäherung zweier Pixel.
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 22:42
Hier habe ich noch einen Code gefunden, aber wie es ausschaut machen die den gleichen Ansatz:
Delphi-Quellcode:
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Runtime.InteropServices
 
Public Class Form1
 
    Private WithEvents pb As New PictureBox
    Private WithEvents cb As New CheckBox
 
    Sub New()
 
        ' This call is required by the Windows Form Designer.
InitializeComponent()

' Add any initialization after the InitializeComponent() call.
        Me.Controls.Add(pb)
        Me.Controls.Add(cb)
        pb.Size = New Size(Me.ClientSize.Width - 50, Me.ClientSize.Height - 50)
        pb.Location = New Point(25, 25)
        pb.Anchor = AnchorStyles.Left Or AnchorStyles.Bottom Or AnchorStyles.Right Or AnchorStyles.Top
        pb.SizeMode = PictureBoxSizeMode.StretchImage
        pb.BackColor = Color.Gray
        cb.Location = New Point(2, 2)
        cb.Text = "Use Lockbits Technique"
        cb.Checked = True
        Me.Text = "click the picturebox"
        Me.Size = New Size(640, 480)
        Me.BackColor = SystemColors.Control
    End Sub
 
    Private Sub pb_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles pb.Click
        Using ofd As New OpenFileDialog
            ofd.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyPictures
            ofd.Filter = "Image Files(*.Bmp;*.Jpg;*.Gif;*.Png)|*.Bmp;*.Jpg;*.Gif;*.Png|All files (*.*)|*.*"
            ofd.Title = "Select a picture"
            Dim result As DialogResult = ofd.ShowDialog
            If result = Windows.Forms.DialogResult.OK Then
                Dim bm As Bitmap
                Try
                    bm = DirectCast(Bitmap.FromFile(ofd.FileName), Bitmap)
                Catch ex As OutOfMemoryException
                    MessageBox.Show("Couldn't load that file")
Exit Sub
Catch ex As FileNotFoundException
MessageBox.Show("Couldn
't find that file")
                    Exit Sub
                End Try
                pb.Image = bm
                If cb.Checked Then
                    Me.BackColor = GetAverageColor1(bm)
                Else
                    Me.BackColor = GetAverageColor2(bm)
                End If
                Me.Text = Me.BackColor.ToString
            End If
        End Using
    End Sub
 
    Private Function GetAverageColor1(ByVal bm As Bitmap) As Color
 
        If bm.PixelFormat <> PixelFormat.Format24bppRgb Then
            MessageBox.Show("Image was not 24bppRgb")
            Return Color.Black
        End If
        Dim bounds As New Rectangle(0, 0, bm.Width, bm.Height)
        Dim bmd As BitmapData = bm.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
        ' The stride is the width of 1 row of pixels in bytes. As 1 pixels requires 3 bytes of color
' information, you would think this would always be 3 * bm.Width - But it isn't. Each row of
' pixels is aligned so that it starts at a 4 byte boundary, this is done by padding rows with
        ' extra bytes if required. (might be 8 byte boundary on x64)
Dim stride As Integer = bmd.Stride
' An array to store the color information:
        Dim pixels(bmd.Stride * bm.Height - 1) As Byte
        ' Copy it all out of the bitmap:
Marshal.Copy(bmd.Scan0, pixels, 0, pixels.Length)
bm.UnlockBits(bmd)
Dim totalR As UInteger
Dim totalG As UInteger
Dim totalB As UInteger
For y As Integer = 0 To bm.Height - 1
For x As Integer = 0 To bm.Width - 1
' Get the index of a pixel in the array.
                ' The index will be the number of bytes in all the rows above the pixel,
' which is (y * stride)
                ' plus the number of bytes in all the pixels to the left of it
' so add x*3:
                Dim index As Integer = (y * stride) + (x * 3)
                totalB += pixels(index)
                totalG += pixels(index + 1)
                totalR += pixels(index + 2)
            Next
        Next
        ' Average the components
Dim pixelCount As Integer = bm.Width * bm.Height
Dim averageR As Integer = CType(totalR \ pixelCount, Integer)
Dim averageG As Integer = CType(totalG \ pixelCount, Integer)
Dim averageB As Integer = CType(totalB \ pixelCount, Integer)
Return Color.FromArgb(averageR, averageG, averageB)
End Function

Private Function GetAverageColor2(ByVal bm As Bitmap) As Color
' Slower, but simpler, way.
        Dim totalR As UInteger
        Dim totalG As UInteger
        Dim totalB As UInteger
        For y As Integer = 0 To bm.Height - 1
            For x As Integer = 0 To bm.Width - 1
                totalR += bm.GetPixel(x, y).R
                totalG += bm.GetPixel(x, y).G
                totalB += bm.GetPixel(x, y).B
            Next
        Next
        Dim pixelCount As Integer = bm.Width * bm.Height
        Dim averageR As Integer = CType(totalR \ pixelCount, Integer)
        Dim averageG As Integer = CType(totalG \ pixelCount, Integer)
        Dim averageB As Integer = CType(totalB \ pixelCount, Integer)
        Return Color.FromArgb(averageR, averageG, averageB)
    End Function
 
    Private Sub cb_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cb.CheckedChanged
        pb.Image = Nothing
        Me.BackColor = SystemColors.Control
    End Sub
End Class
Quelle
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Amateurprofi

Registriert seit: 17. Nov 2005
Ort: Hamburg
1.111 Beiträge
 
Delphi XE2 Professional
 
#7

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 12. Mai 2021, 10:18
Hier habe ich noch einen Code gefunden, aber wie es ausschaut machen die den gleichen Ansatz:
Ja, außer dass die vor dem eigentlichen "Zählen" die Daten aus der Bitmap in ein Array kopieren.
Gruß, Klaus
Die Titanic wurde von Profis gebaut,
die Arche Noah von einem Amateur.
... Und dieser Beitrag vom Amateurprofi....
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 12. Mai 2021, 15:22
Danke skybibo und Amateurprofi ich werde es nach her mal durch-testen!

So werde ich vorgehen, hoffentlich ist das soweit okay, habe es selbst noch nicht getestet da ich gerade bemüht bin dutzende von methoden aus einer vcl-haupt-unit in eine seperate unit auszulagern.

Hier mein Code der vor Eurem geschaltet wird:
Delphi-Quellcode:
function FilenameToBmp(const AFilename: string; out ABmp: TBitmap): Boolean;
var
  wic: TWICImage;
begin
  if ((not FileExists(AFilename)) or (nil = ABmp)) then
    Exit(false);
  wic := TWICImage.Create;
  try
    ABmp.Dormant;
    ABmp.FreeImage;
    ABmp.ReleaseHandle;
    wic.LoadFromFile(AFilename);
    ABmp.Assign(wic);
    ABmp.PixelFormat := pf24bit;
    Result := Assigned(ABmp);
  finally
    wic.Free;
  end;
end;
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 20:15
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.
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
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Michael II

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

AW: Durchschnittsfarbe eines Bitmap "schnell" ermitteln

  Alt 11. Mai 2021, 20:40
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.
Michael Gasser
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 19:02 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