FloodFill算法实现及填充改进

来源:互联网 发布:js trim函数在哪里 编辑:程序博客网 时间:2024/06/06 02:17

+ From wikipedia

Flood fill, also called seed fill, is an algorithm that determines the area connected to a given node in a multi-dimensional array. It is used in the "bucket" fill tool of paint programs to determine which parts of a bitmap to fill with color

+ the algorithms

The flood fill algorithm takes three parameters: a start node, a target color, and a replacement color. The algorithm looks for all nodes in the array which are connected to the start node by a path of the target color, and changes them to the replacement color. There are many ways in which the flood-fill algorithm can be structured, but they all make use of a queue or stack data structure, explicitly or implicitly.

从上面的算法介绍可知,凡是会搜索的同学就能很轻易地掌握floodfill。因为floodfill算法从大体而言可以细分为两种算法思想,一种是DFS,一种是BFS。以下讲介绍两大种常用的算法,并简单分析其中用到的dfs和bfs。

1. per-pixel fill (点点填充)

                                                           

recursive flood-fill with 4 directions                      recursive flood-fill with 8 directions

这两个有一个小区别,就是8方向的算法是在4方向的算法的基础上添加了四个方向(左上、左下、右上、右下),因此造成的结果是8方向的算法有可能会“leak through sloped edges of 1 pixel thick”。至于其它方面则是完全一样,下面所有的算法都是基于4方向的。

dfs的搜索思想(递归)写的代码:

Flood-fill (node, target-color, replacement-color):
1. If the color of node is not equal to target-color, return.
2. Set the color of node to replacement-color.
3. Perform Flood-fill (one step to the west of nodetarget-colorreplacement-color).
    Perform Flood-fill (one step to the east of nodetarget-colorreplacement-color).
    Perform Flood-fill (one step to the north of nodetarget-colorreplacement-color).
    Perform Flood-fill (one step to the south of nodetarget-colorreplacement-color).
4. Return.

bfs的搜索思想(队列)写的代码:

Flood-fill (node, target-color, replacement-color):
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to the end of Q.
4. While "Q" is not empty: 
5.     Set "n" equal to the first element of "Q"
6.     If the color of n is equal to target-color, set the color of n to replacement-color.
7.     Remove first element from "Q"
8.     If the color of the node to the west of n is target-color, set the color of that node to replacement-color, add that node to the end of Q.
9.     If the color of the node to the east of n is target-color, set the color of that node to replacement-color, add that node to the end of Q.
10.    If the color of the node to the north of n is target-color, set the color of that node to replacement-color, add that node to the end of Q.
11.    If the color of the node to the south of n is target-color, set the color of that node to replacement-color, add that node to the end of Q.
12. Return.

以上两种小算法的问题在于,如果填充的面积较大的话,程序很容易爆掉。究其原因,就是搜索的深度过大或队列的长度不够造成的。因此为了减少搜索深度或进队列的元素个数,可以用线方式代替点方式。而这样做还有一个好处就是填充速度的提高。

2. scanline fill (扫描线填充)

//stack friendly and fast floodfill algorithm(递归深搜的写法)

void floodFillScanline(int x, int y, int newColor, int oldColor)
{
    if(oldColor == newColor) return;
    if(screenBuffer[x][y] != oldColor) return;
      
    int y1;
    
    //draw current scanline from start position to the top
    y1 = y;
    while(y1 < h && screenBuffer[x][y1] == oldColor)
    {
        screenBuffer[x][y1] = newColor;
        y1++;
    }    
    
    //draw current scanline from start position to the bottom
    y1 = y - 1;
    while(y1 >= 0 && screenBuffer[x][y1] == oldColor)
    {
        screenBuffer[x][y1] = newColor;
        y1--;
    }
    
    //test for new scanlines to the left
    y1 = y;
    while(y1 < h && screenBuffer[x][y1] == newColor)
    {
        if(x > 0 && screenBuffer[x - 1][y1] == oldColor) 
        {
            floodFillScanline(x - 1, y1, newColor, oldColor);
        } 
        y1++;
    }
    y1 = y - 1;
    while(y1 >= 0 && screenBuffer[x][y1] == newColor)
    {
        if(x > 0 && screenBuffer[x - 1][y1] == oldColor) 
        {
            floodFillScanline(x - 1, y1, newColor, oldColor);
        }
        y1--;
    } 
    
    //test for new scanlines to the right 
    y1 = y;
    while(y1 < h && screenBuffer[x][y1] == newColor)
    {
        if(x < w - 1 && screenBuffer[x + 1][y1] == oldColor) 
        {           
            floodFillScanline(x + 1, y1, newColor, oldColor);
        } 
        y1++;
    }
    y1 = y - 1;
    while(y1 >= 0 && screenBuffer[x][y1] == newColor)
    {
        if(x < w - 1 && screenBuffer[x + 1][y1] == oldColor) 
        {
            floodFillScanline(x + 1, y1, newColor, oldColor);
        }
        y1--;
    }
}

//The scanline floodfill algorithm using our own stack routines, faster(广搜队列的写法)

void floodFillScanlineStack(int x, int y, int newColor, int oldColor)
{
    if(oldColor == newColor) return;
    emptyStack();
    
    int y1; 
    bool spanLeft, spanRight;
    
    if(!push(x, y)) return;
    
    while(pop(x, y))
    {    
        y1 = y;
        while(y1 >= 0 && screenBuffer[x][y1] == oldColor) y1--;
        y1++;
        spanLeft = spanRight = 0;
        while(y1 < h && screenBuffer[x][y1] == oldColor )
        {
            screenBuffer[x][y1] = newColor;
            if(!spanLeft && x > 0 && screenBuffer[x - 1][y1] == oldColor) 
            {
                if(!push(x - 1, y1)) return;
                spanLeft = 1;
            }
            else if(spanLeft && x > 0 && screenBuffer[x - 1][y1] != oldColor)
            {
                spanLeft = 0;
            }//写这一部分是防止因为同一列上有断开的段而造成的可能的“没填充”
            if(!spanRight && x < w - 1 && screenBuffer[x + 1][y1] == oldColor) 
            {
                if(!push(x + 1, y1)) return;
                spanRight = 1;
            }
            else if(spanRight && x < w - 1 && screenBuffer[x + 1][y1] != oldColor)
            {
                spanRight = 0;
            } //写这一部分是防止因为同一列上有断开的段而造成的可能的“没填充”
            y1++;
        }
    }
}

以上两个小算法填充的方向都是纵向填充,你也可以修改成横向填充。

原创粉丝点击