Alle Pixel derselben Farbe ändern
Alle Pixel derselben Farbe ändern
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.
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.
Re: Alle Pixel derselben Farbe ändern
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;
Re: Alle Pixel derselben Farbe ändern
Danke für die Antwort.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;
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?
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Alle Pixel derselben Farbe ändern
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
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
- kupferstecher
- Beiträge: 431
- Registriert: Do 17. Nov 2016, 11:52
Re: Alle Pixel derselben Farbe ändern
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.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.
Bei der pixelweisen Bearbeitung muss man aufpassen, nicht fuer jedes Pixel einen (langsamen) Api-Aufruf zu verursachen.
Re: Alle Pixel derselben Farbe ändern
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).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.
- Dateianhänge
-
ReplaceColors.zip
- (3.06 KiB) 115-mal heruntergeladen
-
- Beiträge: 758
- Registriert: Di 23. Aug 2016, 14:25
- OS, Lazarus, FPC: Windows 11
- CPU-Target: 64Bit
- Wohnort: Berlin
Re: Alle Pixel derselben Farbe ändern
Am schnellsten geht es wenn man den entsprechenden Paletteneintrag ändert. 

Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...
Re: Alle Pixel derselben Farbe ändern
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.400kmh hat geschrieben:Kann man wenn man TLazIntfImage benutzt mehr Polygone ohne merkliche Zeitverzögerungen zeichnen als mit TBitmap?