图像填充算法

news/2024/9/28 13:15:09 标签: photoshop, 操作系统

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

    这类问题最基本的算法是种子填充。即先给出封闭区域内的一点,从这点出发搜索邻域,只要不到边界,就把相邻点纳入连通域,赋予填充色。边界的判断比较灵活,可以使用固定颜色,也可以用一定阈值的色彩容差,类似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


http://www.niftyadmin.cn/n/1447446.html

相关文章

hdu 5037 Frog(高效)

题目链接&#xff1a;hdu 5037 Frog 题目大意&#xff1a;给定N&#xff0c;M&#xff0c;L&#xff0c;表示有一只条宽为M的河&#xff0c;青蛙要从0跳到M&#xff0c;青蛙的最大跳跃能力为L&#xff0c;现在已经存在了N块石头&#xff0c;现在要放任意数量的石头&#xff0c;…

HDU 2036 改革春风吹满地

改革春风吹满地 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 28332 Accepted Submission(s): 14528 Problem Description“ 改革春风吹满地,不会AC没关系;实在不行回老家&#xff0c;还有一亩三分地。谢谢!&…

警惕网吧输入法漏洞(转)

大家都知道&#xff0c;在网吧上网的时候必须输入会员卡号和密码&#xff0c;才能登录客户端的桌面&#xff0c;客户端的登录和注销是受服务器端所控制的。 笔者最近听说出现了智能ABC输入法漏洞&#xff0c;可以结束大多数网吧管理软件。笔者就做了一次实验。 做实验的网吧采用…

PAT甲级-排序类型-1025 PAT Ranking解题思路

1025 PAT Ranking (25 分) 思路 排序后&#xff0c;判断rank&#xff0c;可以借助循环&#xff0c;但是这题有个地方&#xff0c;就是内部需要进行多次排序&#xff0c;在读数据就排序可以大大简化代码量。 代码 #include <bits/stdc.h> using namespace std;struct s…

数据库查询原来长这样!| Greenplum新功能—可视化执行计划

获得技术资料内容&#xff0c;请访问Greenplum中文社区网站 Greenplum Command Center 4.2中首次推出了全新的beta功能——可视化执行计划。 这一业界领先的最新功能提供了全新的查询计划的展示方式&#xff0c;目标是通过图形化的手段展示Query查询计划的每个步骤和执行进度&a…

Java 和Php 接口

2019独角兽企业重金招聘Python工程师标准>>> http://app.huliandaojia.cn/api/printOrder&orderCodexxxx&typexxxx http://sxapi.ceol8.com/api/index.php? mlogin&clogin&aquit&orderCodexxxx&typexxxx 转载于:https://my.oschina.net/u/…

hdu 5038 Grade(水题)

题目链接&#xff1a;hdu 5038 Grade 题目大意&#xff1a;给出n个蘑菇的重量&#xff0c;根据重量计算出蘑菇的分数&#xff0c;按照大小输出分数最多的分数。如果分数包含了所有的蘑菇&#xff0c;除非只有一种分数&#xff0c;否则输出Bad Mushroom. 解题思路&#xff1a;水…

PAT甲级-排序类型-1028 List Sorting解题思路

1028 List Sorting (25 分) 思路 三个排序&#xff0c;直接秒杀&#xff01;&#xff01;&#xff01; 代码 #include <bits/stdc.h> using namespace std;struct stu{char id[10];char name[12];int score; }stud[100005];bool cmp1(stu a,stu b) {if(strcmp(a.id,b.…