Alle Pixel derselben Farbe ändern

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
400kmh
Beiträge: 100
Registriert: Do 25. Mär 2010, 04:03

Alle Pixel derselben Farbe ändern

Beitrag von 400kmh »

Hallo, ich habe eine Frage zur Änderung von Pixeln derselben Farbe auf einer Bitmap.

Ich möchte alle Pixel, die eine bestimmte Farbe auf einer Bitmap haben, eine bestimmte andere Farbe geben. Die Pixel einer Farbe sind nicht alle zusammenhängend. Das heißt ich kann nicht einfach nur eine XY-Kooradinate angeben und dann mit Canvas.Floodfill alles mit der anderen Farbe ausfüllen, sondern muss auf viele unterschiedliche XY-Koordinaten Floodfill anwenden. Daher meine Frage, ob es nicht eine Funktion gibt, mit der man alle Pixel mit derselben Farbe auf einer Bitmap eine andere Farbe geben kann.

wp_xyz
Beiträge: 4885
Registriert: Fr 8. Apr 2011, 09:01

Re: Alle Pixel derselben Farbe ändern

Beitrag von wp_xyz »

Dazu musst du das Bitmap Pixel für Pixel durchlaufen, prüfen ob das aktuelle Pixel die betreffende Farbe hat und, wenn ja, durch die neue Farbe ersetzen. Eine Möglichkeit dafür wäre folgendes:

Code: Alles auswählen

uses
  LCLType, fpimage, intfgraphics;
 
procedure ReplaceColorInBitmap(ABitmap: TBitmap; AColor, ANewColor: TColor);
var
  intfImg: TLazIntfImage;
  clr, newclr: TFPColor;
  imgHandle, imgMaskHandle: HBITMAP;
  i, j: Integer;
begin
  if ABitmap = 0 then exit;
  intfimg := ABitmap.CreateIntfImage;
  try
    clr := TColorToFPColor(AColor);
    newclr := TColorToFPColor(ANewColor);
    for i:=0 to intfimg.Width-1 do
      for j := 0 to intfimg.Height - 1 do
        if intfimg.Colors[i, j] = clr then
          intfimg.Colors[i, j] := newclr;
    intfimg.CreateBitmaps(imgHandle, imgMaskHandle, false);
    ABitmap.Handle := imgHandle;
    ABitmap.MaskHandle := imgMaskHandle;
  finally
    intfimg.Free;
  end;
end;

400kmh
Beiträge: 100
Registriert: Do 25. Mär 2010, 04:03

Re: Alle Pixel derselben Farbe ändern

Beitrag von 400kmh »

wp_xyz hat geschrieben:Dazu musst du das Bitmap Pixel für Pixel durchlaufen, prüfen ob das aktuelle Pixel die betreffende Farbe hat und, wenn ja, durch die neue Farbe ersetzen. Eine Möglichkeit dafür wäre folgendes:

Code: Alles auswählen

    for i:=0 to intfimg.Width-1 do
      for j := 0 to intfimg.Height - 1 do
        if intfimg.Colors[i, j] = clr then
          intfimg.Colors[i, j] := newclr;

Danke für die Antwort.

Ich habe es jetzt so gelöst, dass vor der Laufzeit alle Pixel durchgegangen werden, und für jede vorkommende Farbe für jeweils alle zusammenhängenden Flächen eine X/Y-Position gespeichert wird. Zur Laufzeit übermale ich dann nicht alle Pixel einzeln, sondern jeweils einen zusammenhängenden Bereich mit Canvas.Floodfill.

Frage: Macht es eigentlich zeitlich irgendeinen Unterschied Pixel einzeln umzufärben oder mit Floodfill?

Bei meinem jetzigen Programm gibt es zwar bislang keine Zeitprobleme beim Zeichnen, aber bei einem anderen Programm bin ich zuletzt an gewisse Grenzen gestoßen. Da konnte ich nur eine begrenzte Anzahl Polygone übereinander auf eine Bitmap zeichnen ohne dass es zu Verzögerungen bei der Ausgabe kam, die alle 40 Millisekunden vorgehen war. Dazu noch eine Frage: Kann man wenn man TLazIntfImage benutzt mehr Polygone ohne merkliche Zeitverzögerungen zeichnen als mit TBitmap?

Warf
Beiträge: 1909
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Alle Pixel derselben Farbe ändern

Beitrag von Warf »

Klar gibt’s nen Unterschied zwischen Pixel durchgehen und floodfill
Floodfill ruft im Hintergrund gdi (oder x11) auf, welcher die Aktion mit hardwarebeschläunigung (und optimiert) zeichnet. Wenn du über die Pixel iterierst machst du alles single threaded über den Prozessor. Canvas calls sollten praktisch immer schneller sein als selbst auf den rohen Pixeln zu hantieren.

Ansonsten gibt’s noch BGRABitmaps, die zeichnen direkt mit OpenGL, die sollten nochmal ne Ecke schneller sein als Canvas

Benutzeravatar
kupferstecher
Beiträge: 422
Registriert: Do 17. Nov 2016, 11:52

Re: Alle Pixel derselben Farbe ändern

Beitrag von kupferstecher »

Warf hat geschrieben:Klar gibt’s nen Unterschied zwischen Pixel durchgehen und floodfill
Floodfill ruft im Hintergrund gdi (oder x11) auf, welcher die Aktion mit hardwarebeschläunigung (und optimiert) zeichnet. Wenn du über die Pixel iterierst machst du alles single threaded über den Prozessor.

Allerdings ist der Floodfill-Algorithmus deutlich komplizierter als das lineare Durchlaufen der Pixel. Es muessen ja jeweils benachbarte Pixel durchsucht werden, durchsuchte Pixel muessen auch noch gespeichert werden, Rekursion ist eine Moeglichkeit. Die Ausfuehrungszeit wird dadurch von der zu fuellenden Kontur abhaengig.

Bei der pixelweisen Bearbeitung muss man aufpassen, nicht fuer jedes Pixel einen (langsamen) Api-Aufruf zu verursachen.

wp_xyz
Beiträge: 4885
Registriert: Fr 8. Apr 2011, 09:01

Re: Alle Pixel derselben Farbe ändern

Beitrag von wp_xyz »

Warf hat geschrieben:Klar gibt’s nen Unterschied zwischen Pixel durchgehen und floodfill
Floodfill ruft im Hintergrund gdi (oder x11) auf, welcher die Aktion mit hardwarebeschläunigung (und optimiert) zeichnet. Wenn du über die Pixel iterierst machst du alles single threaded über den Prozessor. Canvas calls sollten praktisch immer schneller sein als selbst auf den rohen Pixeln zu hantieren.

Das mag für einen reinen FloodFill richtig sein, bei der Fragestellung des OP habe ich aber meine Bedenken: er kennt die Startpunkte des FloodFill-Algorithmus ja gar nicht und muss die Pixel notgedrungen durchlaufen, um die Startpunkte zu ermitteln. Der anschließende Floodfill macht aber genau dasselbe nochmals. Das beigefügte Testprogramm zeigt entsprechend, dass mein oben angegebener Code grob doppelt so schnell ist wie das kombinierte Verfahren mit FloodFill. Und wenn's wirklich zeitkritisch werden sollte, kann man meine Farbtausch-Prozedur auch noch schneller machen (ScanLine, RawImage.GetDataLineStart).
Dateianhänge
ReplaceColors.zip
(3.06 KiB) 105-mal heruntergeladen

siro
Beiträge: 731
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Alle Pixel derselben Farbe ändern

Beitrag von siro »

Am schnellsten geht es wenn man den entsprechenden Paletteneintrag ändert. :wink:
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

wp_xyz
Beiträge: 4885
Registriert: Fr 8. Apr 2011, 09:01

Re: Alle Pixel derselben Farbe ändern

Beitrag von wp_xyz »

400kmh hat geschrieben:Kann man wenn man TLazIntfImage benutzt mehr Polygone ohne merkliche Zeitverzögerungen zeichnen als mit TBitmap?

Der Umweg über LazIntfImage oben wurde genommen, um einigermaßen schnell auf die einzelnen Pixel zugreifen zu können, ohne Annahmen über das Pixelformat machen zu müssen (was bei ScanLine nötig ist). Es gibt natürlich auch TBitmap.Pixels[x,y], aber das ist sehr langsam. Größere Operationen würde ich immer direkt auf dem Bitmap selbst machen, es sei denn, ich brauche den Alpha-Kanal. Und wenn's wirklich schnell werden soll, solltest du dir spezielle auf Geschwindigkeit gezüchtete Bitmaps, wie BGRABitmap oder Graphics32, ansehen.

Antworten