图像填充算法

封闭连通域的图像填充是个常见的算法,最近有机会接触到大图像的例子,做一下总结。

这类问题最基本的算法是种子填充。即先给出封闭区域内的一点,从这点出发搜索邻域,只要不到边界,就把相邻点纳入连通域,赋予填充色。边界的判断比较灵活,可以使用固定颜色,也可以用一定阈值的色彩容差,类似photoshop中的魔棒。其他更复杂的计算自然也可以。

邻域的搜索是填充的重点。最简单的算法就是递归,写出来也就几行代码,像下面的样子:

FillShape(int   x,   int   y)
{
    if( !IsBrim(x,y) )
    {
        SetPixel(x,y);
        FillShape(x+1,   y);
        FillShape(x-1,   y);
        FillShape(x,   y+1);
        FillShape(x,   y-1);
     }
}

只要当前点不是边缘,就填充,然后递归相邻的点。这个算法是4连域,改成8连域也是很简单的事。

上述算法简单直观,但存在一些问题。首先它需要逐点搜索,效率不高。其次,在实际的编程中,递归运算是很消耗资源的事情。函数的递归需要压栈,即把当前函数的地址和状态量放到系统的堆栈中,进入下一个状态。实际操作系统的资源总是有限的,如果递归的层数太多,系统的堆栈会溢出,这个是用户控制不了的。这样当我们处理稍大一些的图像时,往往会出现堆栈溢出的错误,使程序无法运行。

针对这些问题,我们先做修改,把填充目标由点改为线。也即使用线扫描的方式搜索连通域。进入一个种子点后,在X方向从左向右逐点搜索连通点,填充,直到遇到中断点停止,然后从这些连通点开始,取上下点递归,伪代码见下:

FillShape(int   x,   int   y)
{
    if( !IsBrim(x,y) )
    {
// 向左填充,FillToLeft(x,y);
        nleft = x;
        while(!IsBrim(nleft,y))
       {
            SetPixel(nleft,y);
            nleft--;
        }
// 向右填充,FillToRight(x,y);
        nright = x;
        while(!IsBrim(nright,y))
       {
            SetPixel(nright,y);
            nright++;
        }
// 上下层的点递归
        for(i=nleft+1;i<right;i++)
        {
            FillShape(x,y+1);
            FillShape(x,y-1);
        }
     }
}

这样做减少了递归次数,提高了效率。但是还没有解决递归的根本缺陷,如果遇到大区域填充,仍然可能出现堆栈溢出。根本的解决之道是放弃递归。研究表明,任何递归算法都是可以修改为使用循环的非递归算法。修改的关键是两步:一、设计并实现一个自己的栈,保存原来递归出现的中间状态量,这样资源的利用率大大提高。二、在循环处理代码中,至少实现起始层和下一层递归的功能,这样才能把中间状态压入自己的栈中以备处理。
    经过修改的非递归算法如下:(为了简洁起见,我把具体的代码用函数名代替):

// 构建堆栈代码
PushStack();// 压栈
PopStack();// 出栈
SetStackEmpty();// 清空栈
int IsStackEmpty();// 判断栈是否为空

FillShape(int   x,   int   y)
{
    FillToLeft(x,y);
    FillToRight(x,y);
    SetStackEmpty();
    PushStack();
    while(!IsStackEmpty())
    {
        PopStack();
        xLeft = getstack_left();
        xRight = getstack_right();
        // 处理上边
        y=y-1;
        FillToLeft(xLeft,y);
        i=xLeft;
        while( i <= xRight)
        {
            FillToRight(i,y);
            PushStack();
        }
        // 处理下边
        y=y+2;
        FillToLeft(xLeft,y);
        i=xLeft;
        while( i <= xRight)
        {
            FillToRight(i,y);
            PushStack();
        }
    }
}

这段代码的思路是,仍然使用扫描线进行邻域填充,使用自己的堆栈来记录中间状态。完成一条扫描线的填充后,把前一个扫描线状态压入栈,弹出时,向上下搜索相邻的扫描线段,填充,压栈。直到栈中状态都被弹出处理为止。

至此,填充算法提高了效率,也避免了系统堆栈溢出。而递归算法,更适合用于原理说明和较少层次的运算,对图像的处理应当慎用并修改之。

http://blog.sina.com.cn/s/blog_6fa6b8fe0100r4gn.html

时间: 2024-10-12 15:34:33

图像填充算法的相关文章

图像放大算法

http://www.cnblogs.com/celerychen/archive/2010/11/25/3588222.html 一. 图像放大算法 图像放大有许多算法,其关键在于对未知像素使用何种插值方式.以下我们将具体分析几种常见的算法,然后从放大后的图像是否存在色彩失真,图像的细节是否得到较好的保存,放大过程所需时间是否分配合理等多方面来比较它们的优劣. 当把一个小图像放大的时候,比如放大400%,我们可以首先依据原来的相邻4个像素点的色彩值,按照放大倍数找到新的ABCD像素点的位置并进

CGA填充算法之种子填充算法

CGA填充算法之种子填充算法 平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界 (也可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜色(也可能是图案填充).区域填充中最常用的是多边形填色,本文讨论种子填充算法(Seed Filling)   如果要填充的区域是以图像元数据方式给出的,通常使用种子填充算法(Seed Filling)进行区域填充.种子填充算法需要给出图像数据的区域,以及区域内的一个点,这种算法比较适合人机交互方式进

多边形区域填充算法--递归种子填充算法

http://blog.csdn.net/orbit/article/details/7323090 平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界(也可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜色(也可能是图案填充).区域填充中最常用的是多边形填色,本文中我们就讨论几种多边形区域填充算法. 一.种子填充算法(Seed Filling) 如果要填充的区域是以图像元数据方式给出的,通常使用种子填充算法(Seed Fillin

漫水填充算法 - cvFloodFill() 实现

前言 漫水填充算法是用来标记一片区域的:设置一个种子点,然后种子点附近的相似点都被填充同一种颜色. 该算法应用性很广,比如目标识别,photoshop 的魔术棒功能等等,是填充类算法中应用最为广泛的一个算法. 漫水填充算法函数 - cvFloodFill() 函数原型: 1 void cvFloodFill ( 2 IplImage * img, // 输入图像 3 CvPoint seedPoint, // 种子点 4 CvScalar newVal, // 像素点被染色的值 5 CvScal

漫水填充算法的一个简单实现(Qt版)

所谓漫水填充算法,是给定一个联通域内的一个点,以此为起点找到这个联通域的其余所有点并将其填充为指定颜色的一种算法. 之所以称之为漫水填充,是因为这种算法就是模拟了涨水的过程,从一点开始,水流慢慢加大,直到漫过了全部区域. 这个算法的详细介绍可以参考下面的链接. https://en.wikipedia.org/wiki/Flood_fill 这个算法在我们寻找一片指定区域时非常有用.因此,我就花了点时间写了个程序.我所实现的算法类似于下面这张图片中的方法,不过我是每次填充一行. 下面这个 flo

[opencv]空洞填充算法

在Matlab下,使用imfill可以很容易的完成孔洞填充操作,感觉这是一个极为常用的方法,然而不知道为什么Opencv里面却没有集成这个函数.在网上查了好多关于Opencv下的孔洞填充方法,大部分使用轮廓查找方法去做的,但对于这种方法,总感觉不是特别好.之前了解过冈萨雷斯那本书上的孔洞填充算法,所以想着手重新写一个.这里借鉴了冈萨雷斯书上的集合运算方法(并不完全一样) 大致思路如下: 0, 设原图像为 A. 1, 首先A向外延展一到两个像素,并将值填充为背景色(0),标记为B. 2, 使用fl

基于matlab的经典图像边缘检测算法

图像边缘检测算法 (1)Robert算子边缘检测 (2)Sobel算子边缘检测 (3)Prewitt算子边缘检测 (4)LOG算子边缘检测 (5)Canny边缘检测 Matlab的实现. 其实还只是掉包侠,一点算法没有写 争取有空用openCV写一遍 I=imread('1.jpg'); I0=rgb2gray(I); subplot(231); imshow(I); BW1=edge(I0,'Roberts',0.16); subplot(232); imshow(BW1); title('R

种子填充算法

1 /************************************************************* 2 pb-图形学题4 3 种子填充算法 4 5 *************************************************************/ 6 7 8 #include <GL/glut.h> 9 #include<cstdio> 10 #include<cmath> 11 #include<stack

图像缩放算法

图像缩放算法较多,下面仅以最邻近插值算法和双线性插值算法作介绍. 如下图1所示,表示原始图像和缩放以后的图像. 图1 图像缩放(原始图像à缩放图像) 图像缩放就是将原始图像中的点经过某一算法映射到目标图像的点的行为,即要找到目标图像中的点p1对应在原始图像中点p0,简单而言就是找点p0. 假设: 原始图像src的分辨率为(srcW * srcH): 目标图像dst的分辨率为(dstW * dstH). 那么: 原始图像宽与目标图像宽的比例 原始图像高与目标图像高的比例 由 所以,原始图像中的点p