Algorithm Introduction | Flood fill Algorithm

Algorithm aliases:

Flood filling algorithm, seed filling algorithm (Seed Fill)

effect:

It is used to determine the area connected to the given node in the multidimensional array, which can be used to mark or separate a part of the image, and realize the automatic area selection function in PS.

Basic idea:

As the name suggests, it is like flooding, filling a connected area.

Of course, certain conditions must be met in order for the water to overflow. It can be understood that the place where the conditions are met is a low-lying place, and the water can flow through.

In image processing, a seed point is given as a starting point, spread to nearby adjacent pixel points, find out all points with the same or similar color, and fill them with new colors, and these points form a connected area.

Algorithm parameters:

  1. start node
  2. target color
  3. replacement color

Algorithm implementation:

The most common implementations of the flood filling algorithm are the four-neighborhood pixel filling method, the eight-neighborhood pixel filling method, and the filling method based on scan lines . According to the code implementation method, it can be divided into recursive and non-recursive.

1. Four-neighborhood recursive implementation

Color the top, bottom, left, and right four points around the pixel (x, y) respectively.

//Recursive 4-way floodfill, crashes if recursion stack is full
public void floodFill4(int x, int y, int newColor, int oldColor)  
{  
    if(x >= 0 && x < width && y >= 0 && y < height   
         && getPixel(x, y) == oldColor && getPixel(x, y) != newColor)   
    {   
        setPixel(x, y, newColor); //set color before starting recursion  
        floodFill4(x + 1, y, newColor, oldColor);  
        floodFill4(x - 1, y, newColor, oldColor);  
        floodFill4(x, y + 1, newColor, oldColor);  
        floodFill4(x, y - 1, newColor, oldColor);  
    }     
}

2. Eight-neighborhood recursive implementation

Color the top, bottom, left, right, top left, bottom left, top right, and bottom right of a pixel.

public void floodFill8(int x, int y, int newColor, int oldColor)  
{  
    if(x >= 0 && x < width && y >= 0 && y < height &&   
            getPixel(x, y) == oldColor && getPixel(x, y) != newColor)   
    {   
        setPixel(x, y, newColor); //set color before starting recursion  
        floodFill8(x + 1, y, newColor, oldColor);  
        floodFill8(x - 1, y, newColor, oldColor);  
        floodFill8(x, y + 1, newColor, oldColor);  
        floodFill8(x, y - 1, newColor, oldColor);  
        floodFill8(x + 1, y + 1, newColor, oldColor);  
        floodFill8(x - 1, y - 1, newColor, oldColor);  
        floodFill8(x - 1, y + 1, newColor, oldColor);  
        floodFill8(x + 1, y - 1, newColor, oldColor);  
    }     
} 

3. Scanline Fill algorithm (Scanline Fill):

Take advantage of fill lines to speed up the algorithm, instead of pushing each potential future pixel coordinate on the stack, it checks adjacent lines (previous and next) to find adjacent segments that might be filled in a future pass, the coordinates of the line segment (start or end) is pushed onto the stack. In most cases, the scanline algorithm is at least an order of magnitude faster than the per-pixel algorithm. 

The process of the algorithm is: first scan the connected pixels in one row or one column, and then scan up and down rows or left and right columns, which can reduce the depth of the recursive stack.

Recursive way:

The recursive implementation algorithm is easy to understand, but when the connected area is large, it is likely to cause stack overflow .

void floodFillScanline(int x, int y, int newColor, int oldColor){
	if(newColor==oldColor) return;
	if(screen[x][y]!=oldColor) return;
	int x1=x;
	while(x1<w&&screen[x1][y]==oldColor){
	    screen[x1][y]=newColor;
	    x1++;
	}
	x1=x-1;
	while(x1>=0&&screen[x1][y]==oldColor){
	    screen[x1][y]=newColor;
	    x1--;
	}
	x1=x;
	while(x1<w&&screen[x1][y]==newColor){
	    if(y<h-1&&screen[x1][y+1]==oldColor) floodFillScanline(x1,y+1,newColor,oldColor);
	    x1++;
	}
	x1=x-1;
	while(x1>0&&screen[x1][y]==newColor){
	    if(y>0&&screen[x1][y+1]==oldColor) floodFillScanline(x1,y+1,newColor,oldColor);
	    x1--;
	}
	x1=x;
	while(x1<w&&screen[x1][y]==newColor){
	    if(y<h-1&&screen[x1][y-1]==oldColor) floodFillScanline(x1,y+1,newColor,oldColor);
	    x1++;
	}
	x1=x-1;
	while(x1>0&&screen[x1][y]==newColor){
	    if(y>0&&screen[x1][y-1]==oldColor) floodFillScanline(x1,y+1,newColor,oldColor);
	    x1--;
	}
}

Non-recursive way:

void floodFillScanline(int x, int y, int newColor, int oldColor){
	if(newColor==oldColor) return;
	if(screen[x][y]!=oldColor) return;
	emptyStack();
	int x1;
	bool spanAbove, spanBelow;
	if(!push(x,y)) return;
	while(pop(x,y)){
	    x1=x;
	    while(x1>0&&screen[x1][y]==oldColor) x1--;
	    x1++;
	    spanAbove = spanBelow = 0;
	    while(x1<w&&screen[x1][y]==oldColor){
	        screen[x1][y]=newColor;
	        if(!spanAbove&&y>0&&screen[x1][y-1]==oldColor){ //这里判断出了上行的元素可以被染色,可能为了修改screen的访存连续性,所以这里没修改。而且你改了上行的值,却没考虑其四周,会有完备性的问题。
	            if(!push(x1,y-1)) return;
	            spanAbove=1;
	        }
	        else if(spanAbove&&y>0&&screen[x1][y-1]!=oldColor){
	            spanAbove=0; //不压入重复过多的元素
	        }
	        if(!spanBelow&&y<h-1&&screen[x1][y+1]==oldColor){
	            if(!push(x1,y+1)) return;
	            spanBelow=1;
	        }
	        else if(spanBelow&&y<h-1&&screen[x1][y+1]!=oldColor){
	            spanBelow=0;
	        }
	        x1++;
	    }
	}
}

4. The floodFill function of OpenCV

In OpenCV, the flood filling algorithm is implemented by the floodFill function, which can fill a connected domain from the specified seed point. Connectivity is measured by how close the pixel values ​​are to each other.

OpenCV2.X has two C++ overloaded floodFill functions:

/* fills the semi-uniform image region starting from the specified seed point*/
CV_EXPORTS int floodFill( InputOutputArray image,
                          Point seedPoint, 
                          Scalar newVal, 
                          CV_OUT Rect* rect=0,
                          Scalar loDiff=Scalar(), 
                          Scalar upDiff=Scalar(),
                          int flags=4 );

/* fills the semi-uniform image region and/or the mask starting from the
   specified seed point*/
CV_EXPORTS int floodFill( InputOutputArray image,
                          InputOutputArray mask,
                          Point seedPoint, 
                          Scalar newVal, 
                          CV_OUT Rect* rect=0,
                          Scalar loDiff=Scalar(), 
                          Scalar upDiff=Scalar(),
                          int flags=4 );

• image The image to be processed is both an input parameter and an output parameter, accepting single-channel or 3-channel, 8-bit or floating-point type images. If Mask is provided and the flag of FLOODFILL_MASK_ONLY is set, the input image will not be modified, otherwise the filling result of calling this method will be modified to the input image.

• The mask mask image is both an input parameter and an output parameter. It accepts a single-channel 8-bit image and requires two pixels larger in width and height than the image to be processed. The mask must be initialized first, and the filling algorithm cannot overflow the non-zero area in the mask. For example, the result of edge detection can be used as a mask to prevent the edge from being filled. As an output parameter, the filled pixels in the mask corresponding to the picture will be set to 1 or the value specified by the following parameters. Therefore, when the floodFill method is called multiple times and the same mask is used, the overlapping and repeated calculation of the filling area can be avoided. Because the mask is larger than the image, the point p(x,y) in the image corresponds to the point p(x+1, y+1) in the mask

• seedPoint is the seed point of the filling algorithm, that is, the starting point

• newVal fill color

• loDiff minimum brightness or color difference

• upDiff maximum brightness or color difference

• The rect optional output parameter returns the smallest rectangle that can just enclose the filled connected domain.

• flags  

 - The lower eight bits [0-7] indicate connectivity, the default value 4 indicates four-field filling, and 8 indicates eight-field filling.    

- [8-15] bits represent the color value used to fill the mask [1-255] default is 1    

- For example, the flag value (4|(255«8)) means to use four-field filling, and the mask filling color value is 255.

- The remaining bits have two values ​​that can be set individually or simultaneously with (|):     

FLOODFILL_MASK_ONLY means that the original input image is not modified, only the result is output to the mask map, and the filled area is marked with the value specified in the previous flag in the mask. The parameter value of newVal will be ignored.

FLOODFILL_FIXED_RANGE indicates that the pixels to be filled are only compared with the seed point. If this flag is not set, it means that the pixel to be filled is compared with the adjacent pixels (equivalent to the difference range is floating), in this case, the difference between the pixel in the filled area and the seed point may become larger and larger.

reference link

The following is the reference content of this blog:

  1. Flood fill algorithm for image processing - Tencent Cloud Developer Community - Tencent Cloud (tencent.com)
  2. Flood fill Algorithm // Tan Shaojie's Amazing Computer Journey (ustc.edu.cn)
  3. Classic Image Segmentation Algorithm - "Flood Fill" (Flood Fill)

Guess you like

Origin blog.csdn.net/Eason_Y/article/details/127782837
Recommended