Flood-Fill Flood-fill • Used in interactive paint systems. • The user specify a seed by pointing to the interior of the region to initiate a flood operation Recursive Flood-Fill Flood-Fill from Seed • Fill a image-space region with some intensity • Start from the seed and floods the region (color) value until a boundary is met. • How to define the region? A simple recursive algorithm can be used: • Fill Until vs. Fill While void floodFill(int x, int y, int fill, int old) { • 4-connectivity vs. 8-connectivity if ((x < 0) || (x >= width)) return; if ((y < 0) || (y >= height)) return; if (getPixel(x, y) == old) { setPixel(fill, x, y); floodFill(x+1, y, fill, old); floodFill(x, y+1, fill, old); floodFill(x-1, y, fill, old); floodFill(x, y-1, fill, old); } } 8-connected vs. 4-connected Recursive Flood-Fill Algorithm The algorithm is very simple, however it is: • highly recursive - requiring a huge number of S procedural calls; • can cause recursion stack to overflow • no mechanism to determine whether the visited pixels have been tested before An 8-connected flood is able to flood through corners that a 4-connected flood cannot. 1
Fill Until vs. Fill While Flood Until void floodUntil(int x, int y, int n_color, int B_color) { if ((x < 0) || (x >= width)) return; if ((y < 0) || (y >= height)) return; c = getPixel(x, y); if (c != n_color && c != B_color) { setPixel(new_color, x, y); floodFill(x+1, y, n_color B_color); floodFill(x, y+1, n_color B_color); seed floodFill(x-1, y, n_color B_color); floodFill(x, y-1, n_color B_color); } } Flood While With global variables void floodWhile(int x, int y, int n_color, int void floodWhile(int x, int y) old) { { if ((x < 0) || (x >= width)) return; if ((x < 0) || (x >= width)) return; if ((y < 0) || (y >= height)) return; if ((y < 0) || (y >= height)) return; if (getPixel(x, y) == old_color) { if (getPixel(x, y) == old) { setPixel(n_color, x, y); setPixel(fill, x, y); floodFill(x+1, y); floodFill(x+1, y, n_color, old); floodFill(x, y+1); floodFill(x, y+1, n_color, old); floodFill(x-1, y); floodFill(x-1, y, n_color, old); floodFill(x, y-1); floodFill(x, y-1, n_color, old); } } } } Use a stack ��������������� Queue q = Ø p r o c e d u r e F l o o d W h i l e ( x , y : i n t e g e r ; o l d V a l , n a w V a l : c o l o r ) ; b e g i n Add the seed to q i f R e a d P i x e l ( x , y ) = o l d V a l t h e n b e g i n W r i t e P i x e l ( x , y , n e w V a l ) ; While(!q.empty()) { F l o o d W h i l e ( x , y - 1 , o l d V a l , n e w V a l ) ; F l o o d W h i l e ( x , y + 1 , o l d V a l , n e w V a l ) ; F l o o d W h i l e ( x - 1 , y , o l d V a l , n e w V a l ) ; F l o o d W h i l e ( x + 1 , y , o l d V a l , n e w V a l ) ; P = q.pop(); e n d e n d ; p r o c e d u r e F l o o d U n t i l ( x , y : i n t e g e r ; b o u n d a r y V a l , n a w V a l : For (x = P’s neighboring pixels) { c o l o r ) ; v a r c : c o l o r b e g i n If (getPixel(x) == old) { c : = r e a d P i x e l ( x , y ) ; i f ( c < > b o u n d a r y V a l ) a n d ( c < > n e w V a l ) t h e n b e g i n setPixel(x, new_color); W r i t e P i x e l ( x , y , n e w V a l ) ; F l o o d U n t i l ( x , y - 1 , b o u n d a r y V a l , n e w V a l ) ; F l o o d U n t i l ( x , y + 1 , b o u n d a r y V a l , n e w V a l ) ; q.push(x); F l o o d U n t i l ( x - 1 , y , b o u n d a r y V a l , n e w V a l ) ; F l o o d U n t i l ( x + 1 , y , b o u n d a r y V a l , n e w V a l ) ; e n d } e n d ; } } 2
Serial Recursion is Depth-First Breath-first Traversal Queue q = Ø So the fill algorithm will continue Add the seed to q in one direction until a boundary is reached. While(!q.empty()) { P = q.removefirst(); It will then change directions momentarily and attempt to For (x = P’s neighboring pixels) { continue back in the original direction. If (getPixel(x) == old) { setPixel(x, fill); Potential problem of stack q.insert(x); overflow. How to avoid it? } } } Recursive Flood-Fill Algorithm Row Coherence • Can also have an “until” version, defining region by boundary Push address of seed pixel onto stack while(stack is not empty) • Recursive flood-fill is somewhat blind and many pixels may { be retested several times Pop the stack to provide next seed • Tag a pixel with a direction and avoid redundant calls… Fill in the run defined by the seed • Row coherence can improve performance dramatically In the adjacent rows (above and below) find the reachable interior runs, and push the address of their rightmost pixels } Floodfill in runs Floodfill in runs C S B A C B A Stack 3
Floodfill in runs Floodfill in runs B A B A D A D A Stack Stack Floodfill in runs Floodfill in runs A A Stack Stack Row Coherence Row Coherence a S b 4
Row Coherence Row Coherence The Stack then contains: a,c,d,f,g a a b g e d c d c f F l o o d F i l l A l g o r i t h m p r o c e d u r e F i l l ( x , y : i n t e g e r ; o l d V a l , n e w V a l : c o l o r ) ; b e g i n p u s h ( x , y ) ; w h i l e s t a c k i s n o t e m p t y b e g i n p o p ( x , y ) ; o p e n _ u p = F A L S E ; o p e n _ d o w n = F A L S E ; w h i l e c o l o r [ x - - , y ] = = o l d V a l ; / * m o v e t o m o s t l e f t p i x e l * / w h i l e c o l o r [ x + + , y ] = = o l d V a l b e g i n c o l o r [ x , y ] = n e w V a l ; i f o p e n _ u p = = F A L S E b e g i n i f c o l o r [ x , y + 1 ] = = o l d V a l b e g i n p u s h ( x , y + 1 ) ; o p e n _ u p = T R U E ; e n d ; e n d e l s e i f c o l o r [ x , y + 1 ] < > o l d V a l o p e n _ u p = F A L S E ; i f o p e n _ d o w n = = F A L S E b e g i n i f c o l o r [ x , y - 1 ] = = o l d V a l b e g i n p u s h ( x , y - 1 ) ; o p e n _ d o w n = T R U E ; e n d ; e n d e l s e i f c o l o r [ x , y - 1 ] < > o l d V a l o p e n _ d o u n = F A L S E ; e n d ; e n d ; e n d ; e n d ; 5
Recommend
More recommend