FloodFill(泛洪算法)

泛洪算法图形处理中的一个填充算法,我们可以设想这样一个场景,windows的画图软件中的油漆桶为一个形状着色,该形状的范围内都将被着色,我们所使用的算法就是从一个像素点出发,以此向周边的像素点扩充着色,直到图形的边界。这个场景我们使用的算法就是flood fill(泛洪算法)。

泛洪算法有3中不同的方式,每种算法有两种形式一种是递归的一种是非递归的。一般来说对于递归的算法我们比较容易实现,但是若所需处理的对象非常大,递归算法非常消耗内存。下面我们将介绍这几种算法:

(1)四邻域泛洪算法
四邻域泛洪算法的思想是对于像素点(x,y),将其着色之后将其周围的上下左右四个点分别进行着色。其递归方式为:

void floodFill4(int x, int y, int newColor, int oldColor)
{
  if(x >= 0 && x < w && y >= 0 && y < h && screenBuffer[y][x][y] == oldColor && screenBuffer[x] != newColor)
  {
    screenBuffer[y][x] = newColor; 
    floodFill4(x + 1, y    , newColor, oldColor);
    floodFill4(x - 1, y    , newColor, oldColor);
    floodFill4(x    , y + 1, newColor, oldColor);
    floodFill4(x    , y - 1, newColor, oldColor);
  }
}
递归方式非常消耗内存,若所需着色的面积非常大,会导致溢出现象。因此,下面我们将介绍四邻域泛洪算法的非递归方式。这里我们使用一个栈来存储未被着色的点,然后依次将存在于着色空间内的点的上下左右的点加入栈,依次着色直到栈为空。

void floodFill4Stack(int x, int y, int newColor, int oldColor)
{
  if(newColor == oldColor) return; //avoid infinite loop
  emptyStack();

  static const int dx[4] = {0, 1, 0, -1}; 
  static const int dy[4] = {-1, 0, 1, 0}; 

  if(!push(x, y)) return;
  while(pop(x, y))
  {
    screenBuffer[y][x] = newColor;
    for(int i = 0; i < 4; i++) {
      int nx = x + dx[i];
      int ny = y + dy[i];
      if(nx > 0 && nx < w && ny > 0 && ny < h && screenBuffer[ny][nx] == oldColor) {
        if(!push(nx, ny)) return;
      }
    }
  }
}
(2)八邻域泛洪算法
八邻域算法是将一个像素点的上下左右,左上,左下,右上,右下都进行着色。该算法的递归方式为:

void floodFill8(int x, int y, int newColor, int oldColor)
{
  if(x >= 0 && x < w && y >= 0 && y < h && screenBuffer[y][x][y] == oldColor && screenBuffer[x] != newColor)
  {
    screenBuffer[y][x] = newColor;

    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);
  }
}
非递归方式为:

void floodFill8Stack(int x, int y, int newColor, int oldColor)
{
  if(newColor == oldColor) return; //avoid infinite loop
  emptyStack();

  static const int dx[8] = {0, 1, 1, 1, 0, -1, -1, -1};
  static const int dy[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; 

  if(!push(x, y)) return;
  while(pop(x, y))
  {
    screenBuffer[y][x] = newColor;
    for(int i = 0; i < 8; i++) {
      int nx = x + dx[i];
      int ny = y + dy[i];
      if(nx > 0 && nx < w && ny > 0 && ny < h && screenBuffer[ny][nx] == oldColor) {
        if(!push(nx, ny)) return;
      }
    }
  }
}
(3)扫描线算法
该算法的过程是:先将一条线上的像素点进行着色,然后依次向上下扩张,直到着色完成。该算法的递归方式为:

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=0&&screen[x1][y]==oldColor){
    screen[x1][y]=newColor;
    x1--;
}
x1=x;
while(x10&&screen[x1][y]==newColor){
    if(y>0&&screen[x1][y+1]==oldColor) floodFillScanline(x1,y+1,newColor,oldColor);
    x1--;
}
x1=x;
while(x10&&screen[x1][y]==newColor){
    if(y>0&&screen[x1][y-1]==oldColor) floodFillScanline(x1,y+1,newColor,oldColor);
    x1--;
}
}
该算法的非递归方式为:

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(x10&&screen[x1][y-1]==oldColor){
            if(!push(x1,y-1)) return;
            spanAbove=1;
        }
        else if(spanAbove&&y>0&&screen[x1][y-1]!=oldColor){
            spanAbove=1;
        }
        if(!spanBelow&&y


你可能感兴趣的:(数据结构和算法)