Alle Pixel derselben Farbe ändern

Für Fragen von Einsteigern und Programmieranfängern...

Alle Pixel derselben Farbe ändern

Beitragvon 400kmh » 22. Dez 2018, 08:36 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.
400kmh
 
Beiträge: 78
Registriert: 25. Mär 2010, 04:03

Beitragvon wp_xyz » 22. Dez 2018, 10:40 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;
wp_xyz
 
Beiträge: 2862
Registriert: 8. Apr 2011, 08:01

Beitragvon 400kmh » 22. Dez 2018, 12:42 Re: Alle Pixel derselben Farbe ändern

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?
400kmh
 
Beiträge: 78
Registriert: 25. Mär 2010, 04:03

Beitragvon Warf » 22. Dez 2018, 13:09 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
Warf
 
Beiträge: 1081
Registriert: 23. Sep 2014, 16:46
Wohnort: Aachen
OS, Lazarus, FPC: Mac OSX 10.11 | Win 10 | FPC 3.0.0 | L trunk | 
CPU-Target: x86_64, i368, ARM
Nach oben

Beitragvon kupferstecher » 22. Dez 2018, 17:21 Re: Alle Pixel derselben Farbe ändern

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.
kupferstecher
 
Beiträge: 196
Registriert: 17. Nov 2016, 11:52

Beitragvon wp_xyz » 22. Dez 2018, 18:59 Re: Alle Pixel derselben Farbe ändern

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).
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
wp_xyz
 
Beiträge: 2862
Registriert: 8. Apr 2011, 08:01

Beitragvon siro » 22. Dez 2018, 19:43 Re: Alle Pixel derselben Farbe ändern

Am schnellsten geht es wenn man den entsprechenden Paletteneintrag ändert. :wink:
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...
siro
 
Beiträge: 333
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 22. Dez 2018, 19:50 Re: Alle Pixel derselben Farbe ändern

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.
wp_xyz
 
Beiträge: 2862
Registriert: 8. Apr 2011, 08:01

• Themenende •

Zurück zu Einsteigerfragen



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste

cron
porpoises-institution
accuracy-worried