AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

FloodFill Rekursiv

Ein Thema von Neutral General · begonnen am 19. Jun 2007 · letzter Beitrag vom 21. Jun 2007
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#11

Re: FloodFill Rekursiv

  Alt 19. Jun 2007, 14:11
Hi,

Hätte nochmal ne Frage:
Ich fülle jetzt die Fläche mehrfarbig.. Also mit verschiedenen Helligkeiten der angegebenen Farbe. Und da funktioniert ja folgende Abbruchbedingung nicht mehr:

(ABmp.Canvas.Pixels[x,y] <> AColor) Wie mach ich das denn?

Gruß
Neutral General
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Benutzerbild von Tormentor32
Tormentor32

Registriert seit: 27. Okt 2005
Ort: Düsseldorf
369 Beiträge
 
Delphi XE5 Professional
 
#12

Re: FloodFill Rekursiv

  Alt 19. Jun 2007, 14:14
Dann musst du dir die Pixel merken, an denen du schon warst!
Richard Mahr
  Mit Zitat antworten Zitat
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#13

Re: FloodFill Rekursiv

  Alt 19. Jun 2007, 14:17
grml das hatte ich mir fast gedacht das es nicht einfacher geht... Geht das ganze FloodFillen eigentlich auch schneller? Hab das Gefühl das das FloodFill von Windows um einiges schneller ist...

Gruß
Neutral General
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Benutzerbild von dizzy
dizzy

Registriert seit: 26. Nov 2003
Ort: Lünen
1.932 Beiträge
 
Delphi 7 Enterprise
 
#14

Re: FloodFill Rekursiv

  Alt 19. Jun 2007, 14:19
Wenn der Hintergrund einfarbig ist, und diese Farbe nicht in deinem Füllmuster vorkommt, prüfe auf Gleichheit mit der Referenzfarbe (Startpixel, der ja üblich auf dem Hintergrund liegt).
Wenn du nicht sicherstellen kannst, dass die Farbe nicht in der Füllung vor kommt, bleibt als ultimative Lösung noch, ein 2D Array aus Bools, mit den selben Ausmaßen wie dein Bitmap. Setze dort alles auf false wo der korespondierende Pixel im Bitmap deine Randfarbe ist, den Rest auf true. Dann setzte immer false, wenn du einen Punkt füllst. Dann kannst du in diesem Array nachsehen, ob du einen Pixel bereits bearbeitet hast, oder ob du am Rand bist.

\\Edith vermisst den roten Kasten, und ist zudem der Meinung, dass Canvas.Pixels[] der langsamst mögliche Zugriff auf Pixeldaten ist. Canvas.Scanline geht da schon ganz anders ab, und das dürfte den ärgsten Flaschenhals ausmachen bei dir.
Eine weitere Optimierung wäre noch möglich, indem du den 4 Folgeaufrufen mitteilst, welchen der 4 weiteren nicht mehr zu betrachten sind, da du ja daher kommst. Wenn man das einfach mit 0..3 durchnummeriert, kannst du mit einer einfachen case-Anweisung ein paar Vergleiche sparen. Aber Pixels ist ganz sicher der weiiiit größere Hammer.
Fabian K.
INSERT INTO HandVonFreundin SELECT * FROM Himmel
  Mit Zitat antworten Zitat
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#15

Re: FloodFill Rekursiv

  Alt 19. Jun 2007, 14:22
Ja ich muss es schon mit einem Array machen... Der Hintergrund ist nämlich mehrfarbig. Aber wie siehts mit meiner zweiten Frage aus: Warum ist mein Floodfill langsamer als das von Canvas (auch einfarbig!) ? Kann ich meins irgendwie schneller machen?
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#16

Re: FloodFill Rekursiv

  Alt 20. Jun 2007, 17:55
...muss euch nochmal um Hilfe bitten bei der Sache:
Das ganze sieht jetzt so aus:

Delphi-Quellcode:
procedure FloodFill(ABmp: TBitmap; x,y: Integer; AColor: TColor; Border: TColor; var Map: TBoolmap);
var l,i: Integer;
    r,g,b: Byte;
    c: TColor;
begin
  if Length(Map) = 0 then
  begin
    SetLength(Map,Abmp.Height);
    for i:= 0 to ABmp.Height-1 do
      SetLength(Map[i],ABmp.Width);
  end;

  if Map[y,x] then
    exit;

  if (ABmp.Canvas.Pixels[x,y] <> Border) and (x >= 0) and (x <= ABmp.Width-1)
    and (y <= ABmp.Height-1) and (y >= 0)
  then
  begin
    l := GetRValue(ABmp.Canvas.Pixels[x,y]);
    r:= cut(GetRValue(AColor)-(255-l));
    g:= cut(GetGValue(AColor)-(255-l));
    b:= cut(GetBValue(AColor)-(255-l));
    c := RGB(r,g,b);

    ABmp.Canvas.Pixels[x,y] := c;
    Map[y,x] := true;

    FloodFill(ABmp,x,y+1,AColor,Border,Map);
    FloodFill(ABmp,x,y-1,AColor,Border,Map);
    FloodFill(ABmp,x+1,y,AColor,Border,Map);
    FloodFill(ABmp,x-1,y,AColor,Border,Map);
  end;
end;
Seitdem ich das mehrfarbige Floodfill hab gibts en Stackoverflow. Dann hab ich halt das BooleanArray hinzugefügt aber es kommt immernoch ein Stack Overflow -.-

Wodran liegt das denn jetzt bitte ? -.-

Gruß
Neutral General
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Benutzerbild von dizzy
dizzy

Registriert seit: 26. Nov 2003
Ort: Lünen
1.932 Beiträge
 
Delphi 7 Enterprise
 
#17

Re: FloodFill Rekursiv

  Alt 21. Jun 2007, 01:10
Zuerst ein paar Anmerkungen zum Code, wo Dinge auftauchen, die ich für umständlich bzw. komisch gelöst halte:

1) In diesem Fall würde ich eine Nested-Function verwenden, um u.a. das mehrmalige Übergeben der Map zu umgehen. Das verhindert auch, dass die Map von aussen mitgegeben werden muss, was ja garkeinen Sinn ergibt, da sie rein interne Angelegenheit des Floodfills ist. Strukturell in etwa so:
Delphi-Quellcode:
procedure FloodFill(ABmp: TBitmap; x, y: Integer; AColor: TColor; Border: TColor);
var
  Map: TBoolmap;
  ix, iy: Integer;

  procedure DoPixel(ax, ay: Integer);
  begin
    if ({ax und ay im gültigen Bereich}) and not Map[ax, ay] then
    begin
      ABmp.Canvas.Pixels[ax, ay] := Zielfarbe;
      Map[ax, ay] := true;
      DoPixel(ax+1, ay );
      DoPixel(ax , ay+1);
      DoPixel(ax-1, ay );
      DoPixel(ax , ay-1);
    end;
  end;

begin
  // Bei SetLength gehen so viele Argumente wie dein Array Dimensionen hat ;)
  // Zudem ist es so "richtig herum", erst x dann y Koordinate. Bei dir ists vertauscht
  // und damit anders herum als bei Pixels. Unschön und fehlerträchtig.
  SetLength(Map, ABmp.Width, ABmp.Height);

  // Map initialisieren, und dabei gleich die Border abtragen. Erspart die zusätzliche
  // Abfrage auf diese für jedes Pixel nachher.
  for iy := 0 to ABmp.Heigt-1 do
    for ix := 0 to ABmp.Width-1 do
      if ABmp.Canvas.Pixels[ix, iy] = Border then
        Map[ix, iy] := true
      else
        Map[ix, iy] := false;

  // Eigentichen Fill starten
  DoPixel(x, y);
end;
So in etwa würd ich das vermutlich machen =)


2)(x <= ABmp.Width-1) and (y <= ABmp.Height-1) Naja, ich hab zwar geschrieben, dass es bis Width/Height-1 geht, aber performanter und lesbarer wäre es gewesen statt obigem folgendes zu machen:
(x < ABmp.Width) and (y < ABmp.Height) Selber Effekt, ohne noch zusätzliche Arithmetik. Und wenn ich nicht irre, ist ein Vergleich auf echt kleiner oder größer sogar noch ne Picosekunde schneller als auf kleiner/größer-gleich


3) Dein Stack overflow ist im übrigen interessant! Ich hätte eher eine Bereichsverletzung, oder mindestens ne AV erwartet. Bei:
Delphi-Quellcode:
  if Map[y,x] then
    exit;
Greifst du auf Elemente zu, die evtl. garnicht mehr im Bereich sind. Ob x und y zwischen 0 und Width/Height sind, prüfst du erst später.



Gruss,
Fabian
Fabian K.
INSERT INTO HandVonFreundin SELECT * FROM Himmel
  Mit Zitat antworten Zitat
Benutzerbild von dizzy
dizzy

Registriert seit: 26. Nov 2003
Ort: Lünen
1.932 Beiträge
 
Delphi 7 Enterprise
 
#18

Re: FloodFill Rekursiv

  Alt 21. Jun 2007, 01:43
Nochmal zum Overflow:

Wie groß ist dein Bild, bzw. der zu füllende Bereich ca.? Diese Methode zum Füllen tendiert nämlich dazu, seeeehr tief in die Rekursion zu gehen. Die Erste "Füll-Ameise" läuft ja durch, bis sie in einer Sackgasse landet. Das kann im Optimalfall schon der gesamte Füllbereich sein! Je nach Größe, Form und Startpunkt wandern dann mehrere tausend Aufrufe aufm Stack.

Wenn das der Fall ist, und andere Quellen für den Overflow auszuschließen sind, gäbe es zwei Auswege:
  1. Umsetzung in iteratives Vorgehen. Es wird angenommen, dass jede Rekursion in eine Iteration überführbar ist. Für den Umgekehrten Fall ist es bereits allgemein bewiesen, ob so rum mittlerweile auch weiss ich grad nicht.
    Da jeder Rekursionsschritt gleich 4 neue auslöst wird das allerdings vermute ich etwas aufwändiger was das Rahmenwerk an Zählern und Bedingungen angeht.
  2. Festlegen einer maximalen Rekursionstiefe. DoPixel() würde einen Parameter mehr bekommen, einen Zähler der für jeden neuen Aufruf um je eins erhöht wird. In der Prozedur dann ab einer gewissen Tiefe nicht mehr weiter machen.
    Im Ergebnis können zwei Dinge passieren:
    1) Die Tiefe reicht aus, deine Form und Startpunkt waren glücklich gewählt, so dass die Fläche trotdem ganz erfasst wird.
    2) Die Füllung erhält Lücken.
    Im Fall 2 könntest du dann her gehen, und in der Map verbleibende Flecken suchen, und dort einen neuen Floodfill ansetzen, und das ganze so lange wiederholen, bis sich keine Flecken mehr finden. Dieses Verfahren wird jedoch auch etwas Aufwendiger, und sicherlich nicht weniger anspruchsvoll wie die Umsetzung in eine Iteration.

Es gibt noch einen 3. Ausweg, der aber extrem unschön ist, und nur Symptome bekämpft: In den Projektoptionen den Stack vergrößern. Auf den ersten Blick eine tolle Idee, weil einfach und super schnell gemacht, aber sehr unfeiner Stil und wenn jemand ein noch etwas größeres Bild nimmt, schon wieder im Eimer.


Zunächst gilt es aber alle anderen Fehlerquellen als "zu groß" auszuschließen. (Zumal das dann wirklich schon nen ganz schön großer Bereich sein müsste. So Fullscreen-Fill oder sowas )


Anmerkung: Witziger weise ist es was die Rekursionstiefe angeht besser für den Algo, wenn die zu füllende Form möglichst komplex ist, mit schön zerklüfteten Rändern. Ein ausgefranstes Tribal-Tatoo z.B. wäre prima . Während ein einfaches Viereck z.B. schlimmstenfalls in einer einzigen Spirale gefüllt würde, was einer Rekursionstiefe von Breite*Höhe des Vierecks in Pixeln entspräche. (Je nach Wahl der Reihenfolge der zu untersuchenden Nachbarpixel, und dem Startpixel. Es kann auch etwas günstiger ausfallen.)

Alles in allem ist Floodfill recht interessant, und weit weniger trivial, als man zunächst annehmen möchte finde ich =). Man kann viel optimieren, verallgemeinern, rumdoktorn - herrlich!
Fabian K.
INSERT INTO HandVonFreundin SELECT * FROM Himmel
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


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 14:56 Uhr.
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