漫水填充算法原理及其实现

一 漫水填充算法描述

    1..1 、种子填充算法

         种子填充算法是从多边形区域内部的一点开始,由此出发找到区域内的所有像素。

         种子填充算法采用的边界定义是区域边界上所有像素具有某个特定的颜色值,区域内部所有像素均不取这一特定颜色,而边界外的像素则可具有与边界相同的颜色值。

         具体算法步骤:(1)标记种子(x,y)的像素点 (2)检测该点的颜色,若他与边界色和填充色均不同,就用填充色填   充该点,否则不填充 (3)检测相邻位置,继续(2)。这个过程延续到已经检测区域边界范围内的所有像素为止。

         当然在搜索的时候有两种检测相邻像素:四向连通和八向连通。四向连通即从区域上一点出发,通过四个方向上、下、左、右来检索。而八向连通加上了左上、左下、右上、右下四个方向。

       这种算法的有点是算法简单,易于实现,也可以填充带有内孔的平面区域。但是此算法需要更大的存储空间以实现栈结构,同一个像素多次入栈和出栈,效率低,运算量大。

     1.2、扫描线种子填充算法

       该算法属于种子填充算法,它是以扫描线上的区段为单位操作。所谓区段,就是一条扫描线上相连着的若干内部象素的集合。 
     扫描线种子填充算法思想:首先填 充当前扫描线上的位于给定区域的一区段,然后确定于这一区段相邻的上下两条线上位于该区域内是否存在需要填充的新区段,如果存在,则依次把他们保存起来,反复这个过程,直到所保存的各区段都填充完毕。

        1.2.1、扫描线种子填充算法实现(有演示动画见 如流,新一代智能工作平台 ) 
    借助于堆栈,上述算法实现步骤如下: 
1、初始化堆栈。 
2、种子压入堆栈。 
3、while(堆栈非空) 

(1)从堆栈弹出种子象素。 
(2)如果种子象素尚未填充,则: 
a.求出种子区段:xleft、xright; 
b.填充整个区段。 
c.检查相邻的上扫描线的xleft <= x <= xright区间内,是否存在需要填充的新区段,如果存在的话,则把每个新区段在xleft <= x <= xright范围内的最右边的象素,作为新的种子象素依次压入堆栈。 
d.检查相邻的下扫描线的xleft <= x <= xright区间内,是否存在需要填充的新区段,如果存在的话,则把每个新区段在xleft <= x <= xright范围内的最右边的象素,作为新的种子象素 依次压入堆栈。 
}

   1.2.2改进算法

     原算法中, 种子虽然代表一个区段, 但种子实质上仍是一个象素, 我们必须在种子出栈的时候计算种子区段, 而这里有很大一部分计算是重复的. 而且, 原算法的扫描过程如果不用mask的话, 每次都会重复扫描父种子区段所在的扫描线, 而用mask的话又会额外增加开销 

所以, 对原算法的一个改进就是让种子携带更多的信息, 让种子不再是一个象素, 而是一个结构体. 该结构体包含以下信息: 种子区段的y坐标值, 区段的x开始与结束坐标, 父种子区段的方向(上或者下), 父种子区段的x开始与结束坐标. 
struct seed{ 
    int y, 
    int xleft, 
    int xright, 
    int parent_xleft, 
    int parent_xright, 
    bool is_parent_up 
}; 

这样算法的具体实现变动如下 

1、初始化堆栈. 
2、将种子象素扩充成为种子区段(y, xleft, xright, xright+1, xrihgt, true), 填充种子区段, 并压入堆栈. (这里有一个构造父种子区段的技巧) 
3、while(堆栈非空) 

(1)从堆栈弹出种子区段。 
(2)检查父种子区段所在的扫描线的xleft <= x <= parent_xleft和parent_xright <= x <= xright两个区间, 如果存在需要填充的新区段, 则将其填充并压入堆栈. 
(3)检查非父种子区段所在的扫描线的xleft <= x <= xright区间, 如果存在需要填充的新区段, 则将其填充并压入堆栈. 


另外, opencv里的种子填充算法跟以上方法大致相同, 不同的地方是opencv用了队列不是堆栈, 而且是由固定长度的数组来实现的循环队列, 其固定长度为 max(img_width, img_height)*2. 并且push与pop均使用宏来实现而没有使用函数. 用固定长度的数组来实现队列(或堆栈)意义是显然的, 能大大减少构造结构, 复制结构等操作, 可以大大提高效率. 

二 漫水填充算法c语言实现

/* 漫水法填充标定实现
   copy from: http://blog.csdn.net/zhjx2314/article/details/1629702 


*/
  //像素值
 unsigned char pixel;
 //种子堆栈及指针
 Seed *Seeds;
 int StackPoint;
 //当前像素位置
 int iCurrentPixelx,iCurrentPixely;
  //分配种子空间 
 Seeds = new Seed[iWidth*iLength];
 
 //计算每个标定值的像素个数
 int count[251];
 for(i=1;i<252;i++)
 {  
	 count[i]=0; //初始化为0
 } 
 
    //滤波的阈值
 int yuzhi = 700;
 

 for (i=0;i<iWidth;i++)
 {
   for (j=0;j<iLength;j++)
   {  
		 if (grey_liantong.GetPixel(i,j)==0)  //当前像素为黑,对它进行漫水法标定
		 {
				//初始化种子
				 Seeds[1].x = i;
				 Seeds[1].y = j;
				 StackPoint = 1;
			 
				while( StackPoint != 0)
				{
				  //取出种子
				  iCurrentPixelx = Seeds[StackPoint].x;
				  iCurrentPixely = Seeds[StackPoint].y;
				  StackPoint--;
				        
				 
				  //取得当前指针处的像素值,注意要转换为unsigned char型
				  pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx,iCurrentPixely);
				 
				  //将当前点标定
				  grey_liantong.SetPixel(iCurrentPixelx,iCurrentPixely,flag);
						count[flag]++; //标定像素计数
				 
				  //判断左面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixelx > 1)
				  {
				   
				       //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx-1,iCurrentPixely);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely;
							Seeds[StackPoint].x = iCurrentPixelx - 1;
					   }
				  }
				 
				  //判断上面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixely < iLength - 1)
				  {
				   
					   //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx,iCurrentPixely+1);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely + 1;
							Seeds[StackPoint].x = iCurrentPixelx;
					   }
				  }
				 
				  //判断右面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixelx < iWidth - 1)
				  {
				   
					   //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx+1,iCurrentPixely);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely;
							Seeds[StackPoint].x = iCurrentPixelx + 1;
					   }
				  }
				 
				  //判断下面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixely > 1)
				  {
				   
					   //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx,iCurrentPixely-1);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely - 1;
							Seeds[StackPoint].x = iCurrentPixelx;
					   }
				  }
			  }//end while( StackPoint != 0)
			  flag = (flag + 7)%251+1;  //当前点连通区域标定后,改变标定值
		 }//end if  
   }//end for(i
 }//end for(j
 

 //释放堆栈
 delete Seeds;
    grey_res.Clone(grey_liantong); 


/*
   摘自:http://blog.sina.com.cn/s/blog_611a555e0100fcrq.html


	floodfill算法
2008-07-28 11:22
 

+ 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 node, target-color, replacement-color).
    Perform Flood-fill (one step to the east of node, target-color, replacement-color).
    Perform Flood-fill (one step to the north of node, target-color, replacement-color).
    Perform Flood-fill (one step to the south of node, target-color, replacement-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++;
        }
    }
}


//以上两个小算法填充的方向都是纵向填充,你也可以修改成横向填充。
  //像素值
 unsigned char pixel;
 //种子堆栈及指针
 Seed *Seeds;
 int StackPoint;
 //当前像素位置
 int iCurrentPixelx,iCurrentPixely;
  //分配种子空间 
 Seeds = new Seed[iWidth*iLength];
 
 //计算每个标定值的像素个数
 int count[251];
 for(i=1;i<252;i++)
 {  
	 count[i]=0; //初始化为0
 } 
 
    //滤波的阈值
 int yuzhi = 700;
 

 for (i=0;i<iWidth;i++)
 {
   for (j=0;j<iLength;j++)
   {  
		 if (grey_liantong.GetPixel(i,j)==0)  //当前像素为黑,对它进行漫水法标定
		 {
				//初始化种子
				 Seeds[1].x = i;
				 Seeds[1].y = j;
				 StackPoint = 1;
			 
				while( StackPoint != 0)
				{
				  //取出种子
				  iCurrentPixelx = Seeds[StackPoint].x;
				  iCurrentPixely = Seeds[StackPoint].y;
				  StackPoint--;
				        
				 
				  //取得当前指针处的像素值,注意要转换为unsigned char型
				  pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx,iCurrentPixely);
				 
				  //将当前点标定
				  grey_liantong.SetPixel(iCurrentPixelx,iCurrentPixely,flag);
						count[flag]++; //标定像素计数
				 
				  //判断左面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixelx > 1)
				  {
				   
				       //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx-1,iCurrentPixely);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely;
							Seeds[StackPoint].x = iCurrentPixelx - 1;
					   }
				  }
				 
				  //判断上面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixely < iLength - 1)
				  {
				   
					   //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx,iCurrentPixely+1);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely + 1;
							Seeds[StackPoint].x = iCurrentPixelx;
					   }
				  }
				 
				  //判断右面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixelx < iWidth - 1)
				  {
				   
					   //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx+1,iCurrentPixely);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely;
							Seeds[StackPoint].x = iCurrentPixelx + 1;
					   }
				  }
				 
				  //判断下面的点,如果为黑,则压入堆栈
				  //注意防止越界
				  if(iCurrentPixely > 1)
				  {
				   
					   //取得当前指针处的像素值,注意要转换为unsigned char型
					   pixel = (unsigned char)grey_liantong.GetPixel(iCurrentPixelx,iCurrentPixely-1);
					   if (pixel == 0)
					   {
							StackPoint++;
							Seeds[StackPoint].y = iCurrentPixely - 1;
							Seeds[StackPoint].x = iCurrentPixelx;
					   }
				  }
			  }//end while( StackPoint != 0)
			  flag = (flag + 7)%251+1;  //当前点连通区域标定后,改变标定值
		 }//end if  
   }//end for(i
 }//end for(j
 

 //释放堆栈
 delete Seeds;
    grey_res.Clone(grey_liantong); 


/*
   摘自:http://blog.sina.com.cn/s/blog_611a555e0100fcrq.html


	floodfill算法
2008-07-28 11:22
 

+ 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 node, target-color, replacement-color).
    Perform Flood-fill (one step to the east of node, target-color, replacement-color).
    Perform Flood-fill (one step to the north of node, target-color, replacement-color).
    Perform Flood-fill (one step to the south of node, target-color, replacement-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++;
        }
    }
}


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

以上是从网上摘录的,代码中有地址,不明白的话可以去它们的博客去看。。。

三 openCv中漫水填充算法

  

/*
 漫水填充算法:
 摘自:函数介绍 http://blog.csdn.net/superjimmy/article/details/6181528
      


FloodFill
用指定颜色填充一个连接域 

void cvFloodFill( CvArr* image, CvPoint seed_point, CvScalar new_val,CvScalar lo_diff=cvScalarAll(0), 
         CvScalar up_diff=cvScalarAll(0),CvConnectedComp* comp=NULL, int flags=4, CvArr* mask=NULL );
   #define CV_FLOODFILL_FIXED_RANGE (1 << 16)
   #define CV_FLOODFILL_MASK_ONLY (1 << 17)
	image 
	输入的 1- 或 3-通道, 8-比特或浮点数图像。输入的图像将被函数的操作所改变,除非你选择 CV_FLOODFILL_MASK_ONLY 选项 (见下面). 
	seed_point 
	开始的种子点. 
	new_val 
	新的重新绘制的象素值 
	lo_diff 
	当前观察象素值与其部件领域象素或者待加入该部件的种子象素之负差(Lower difference)的最大值。对 8-比特 彩色图像,它是一个 packed value. 
	up_diff 
	当前观察象素值与其部件领域象素或者待加入该部件的种子象素之正差(upper difference)的最大值。 对 8-比特 彩色图像,它是一个 packed value. 
	comp 
	指向部件结构体的指针,该结构体的内容由函数用重绘区域的信息填充。 
	flags 
	操作选项. 低位比特包含连通值, 4 (缺省) 或 8, 在函数执行连通过程中确定使用哪种邻域方式。高位比特可以是 0 或下面的开关选项的组合: 
	CV_FLOODFILL_FIXED_RANGE - 如果设置,则考虑当前象素与种子象素之间的差,否则考虑当前象素与其相邻象素的差。(范围是浮点数). 
	CV_FLOODFILL_MASK_ONLY - 如果设置,函数不填充原始图像 (忽略 new_val), 但填充掩模图像 (这种情况下 MASK 必须是非空的). 
	mask 
	运算掩模, 应该是单通道、8-比特图像, 长和宽上都比输入图像 image 大两个象素点。若非空,则函数使用且更新掩模, 所以使用者需对 mask 内容的初始化负责。填充不会经过 MASK 的非零象素, 例如,一个边缘检测子的输出可以用来作为 MASK 来阻止填充边缘。或者有可能在多次的函数调用中使用同一个 MASK,以保证填充的区域不会重叠。注意: 因为 MASK 比欲填充图像大,所以 mask 中与输入图像(x,y)像素点相对应的点具有(x+1,y+1)坐标。 
	函数 cvFloodFill 用指定颜色,从种子点开始填充一个连通域。连通性由象素值的接近程度来衡量。在点 (x, y) 的象素被认为是属于重新绘制的区域,如果: 

	src(x',y')-lo_diff<=src(x,y)<=src(x',y')+up_diff, 灰度图像,浮动范围 
	src(seed.x,seed.y)-lo<=src(x,y)<=src(seed.x,seed.y)+up_diff, 灰度图像,固定范围 
	src(x',y')r-lo_diffr<=src(x,y)r<=src(x',y')r+up_diffr 和 
	src(x',y')g-lo_diffg<=src(x,y)g<=src(x',y')g+up_diffg 和 
	src(x',y')b-lo_diffb<=src(x,y)b<=src(x',y')b+up_diffb, 彩色图像,浮动范围 
	src(seed.x,seed.y)r-lo_diffr<=src(x,y)r<=src(seed.x,seed.y)r+up_diffr 和 
	src(seed.x,seed.y)g-lo_diffg<=src(x,y)g<=src(seed.x,seed.y)g+up_diffg 和 
	src(seed.x,seed.y)b-lo_diffb<=src(x,y)b<=src(seed.x,seed.y)b+up_diffb, 彩色图像,固定范围 
	其中 src(x',y') 是象素邻域点的值。也就是说,为了被加入到连通域中,一个象素的彩色/亮度应该足够接近于: 

	它的邻域象素的彩色/亮度值,当该邻域点已经被认为属于浮动范围情况下的连通域。 
	固定范围情况下的种子点的彩色/亮度值 
*/
//rorger, 2010

#include "cv.h"
#include "highgui.h"

int main()
{
	cvNamedWindow("image");
	IplImage * src = cvLoadImage("D:\\openCV\\openCVProject\\openCv笔记\\openCv笔记\\test.jpg");
	
	IplImage * img=cvCreateImage(cvGetSize(src), 8, 3);
	cvCopyImage(src, img);
	
    cvFloodFill(
		img,
		cvPoint(54,82), //100,75,标记种子
		CV_RGB(255,0,0),
		cvScalar(20,30,40,0),
		cvScalar(20,30,40,0),
		NULL,
		4,
		NULL
		);

	
	cvShowImage("image", img);
	cvWaitKey(0);
	cvReleaseImage(&src);
	cvReleaseImage(&img);
	cvDestroyAllWindows();
	return 0;
}6181528
      


FloodFill
用指定颜色填充一个连接域 

void cvFloodFill( CvArr* image, CvPoint seed_point, CvScalar new_val,CvScalar lo_diff=cvScalarAll(0), 
         CvScalar up_diff=cvScalarAll(0),CvConnectedComp* comp=NULL, int flags=4, CvArr* mask=NULL );
   #define CV_FLOODFILL_FIXED_RANGE (1 &l
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值