iPad绘画应用程序上的递归洪水填充

Posted

技术标签:

【中文标题】iPad绘画应用程序上的递归洪水填充【英文标题】:Recursive flood fill on an iPad paint app 【发布时间】:2012-09-10 09:31:32 【问题描述】:

我在我的 ipad 绘画应用程序上使用递归洪水填充算法,我认为它会因堆栈溢出而崩溃。有人可以通过示例代码或好的建议帮助我解决这个问题,因为我是菜鸟吗?

-(void)paintingBucket:(int)point point2:(int)point2 width:(int)width colorAtPoint:(UIColor *)color 

int offset = 0;
int x = point;
int y = point2;
offset = 4*((width*round(y))+round(x));

if (((x<1025 && y<705)||(x<1025) ||(y<705)) && (x>0 && y>0) && (offset<2887648)) 

    int alpha = data[offset];
    int red = data[offset + 1];
    int green = data[offset + 2];
    int blue = data[offset + 3];
    color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];

        if (![color1 isEqual: color] ) 
            return;
         else 
            color3 = self.currentColor ;
            CGFloat r,g,b,a;
            [color3 getRed:&r green:&g blue: &b alpha: &a];
            int reda = (int)(255.0 * r);
            int greena = (int)(255.0 * g);
            int bluea = (int)(255.0 * b);
            int alphaa = (int)(255.0 * a);
            // NSLog(@" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);

            data[offset + 3] = alphaa;
            data[offset + 2] = reda;
            data[offset + 1] = greena;
            data[offset] = bluea;
        
    

    [self paintingBucket:x+1 point2:y    width:width colorAtPoint:color];
    [self paintingBucket:x-1 point2:y    width:width colorAtPoint:color];
    [self paintingBucket:x   point2:y+1  width:width colorAtPoint:color];
    [self paintingBucket:x   point2:y-1  width:width colorAtPoint:color];

【问题讨论】:

你从哪里得到算法的? 来自***和网络上的许多网站,它们都是一样的。 在***上发布该算法的链接。 en.wikipedia.org/wiki/Flood_fill 给你。 它不是无限的,因为它适用于小物体,但它在较大的物体上会崩溃。 【参考方案1】:

这里有一个简单的例子,让它成为一个动态算法,而不是递归的。

NSMutableArray *pointsToRender = [NSMutableArray new];
[pointsToRender addObject:startingPoint];

while (pointsToRender.length>0) 

    // Get a point from the array and fill it
    MyPoint *point = [pointsToRender lastObject];
    [pointsToRender removeLastObject];
    [self drawColor:color atPoint:point];

    // Are any surrounding points needing to be filled?
    if (point 1px above) needs to be filled
        [pointsToRender addObject : point1Above];
    .. repeat for the other three points


是的,这是半客观的 C 半伪代码,抱歉。但你明白了。英文是这样的:

创建一个需要填充的点数组,其中包含起点。 对于每个点 填满 从数组中删除它 看看它的邻居。如果还需要填充每个邻居,请将其添加到数组中 重复直到要填充的点数组为空

这将消耗堆,而不是堆栈。

【讨论】:

好的,我理解这背后的基本思想,但我不确定我是否会知道如何实现它。如果我不成功,我会尝试,你能帮我更多吗?包含位图信息的数据是 usigned char 类型,它不是一个对象,所以我不能把它放在 NSMutableArray 有什么办法吗?我将尝试实现这一点,并将得到我的结果。 您可以查看NSValue 以将任意 C 数据存储在对象中。而且我在我的可变数组中存储了一个点 (x, y) 而不是一个字符。你也可以自己创建一个类来保存 x 和 y 值(我叫我的 MyPoint :) 比原来的解决方案更好,但在这种情况下,仍然有很多点也会被绘制两次。效率不高。 :) 无论如何,我不想无情,但最终算法应该由提问者使用给定的指导方针来实现,因为这不是“给我完整的工作代码,因为我做 - not-know-how-I-can-implement-it”网站。 :( 我在回答的第一句话中确实使用了“天真”这个词;)我的回答在很大程度上是指向正确方向的指针,无论如何都不是一个完整的解决方案!首先,在紧密循环中多次修改 NSMutableArray 可能不是最有效的做法。 . .而且您当然不想为要填充的每个点创建一个新对象放入可变数组中!这可能不是有史以来性能最高的算法!【参考方案2】:

将函数分解为fillRight和fillLeft方法并不是长久之计,因为如果图像变大,可能会再次发生溢出。

我建议使用更快的洪水填充算法。 Wikipedia 上描述的 4-Way Flood 填充很容易实现,但每个点都要经过四次。

我建议使用扫描线填充:请参阅http://lodev.org/cgtutor/floodfill.html - 带有堆栈的扫描线洪水填充算法。我在我的绘图应用程序中替换了我的 4 向洪水填充,现在它更快并且在不到一秒的时间内填充了 1024x768 像素的区域。当然,速度可能会因您的实施而异。

最后,一些注意事项:

1.您可以使用CGPoint将点存储在数组中。

CGPoint point=CGPointMake(100, 50); //Declare a point and put a value in it

然后您可以使用point.x和point.y获取和设置x和y值

2. 按照 deanWombourne 的建议,使用数组存储要检查的点。

NSMutableArray * pointsToCheck=[[NSMutableArray alloc]init];//Initialise the array
[pointsToCheck addObject:[NSValue valueWithCGPoint:start]];//Assuming you have a CGPoint start, you need to convert it to a NSValue before you put it into the array like [NSValue valueWithCGPoint:point]

while ([pointsToCheck count]>0)//While there are points to be checked:
    NSValue * pointValue=[pointsToCheck objectAtIndex:0];//Get the point: It doesn't matter if you get the first or last item
    [pointsToCheck removeObjectAtIndex:0];//Remove the point from the queue so we won't go through it again

    CGPoint point=[pointValue CGPointValue];//Get the CGPoint from the NSValue object

    //Implement your algorithm here: check for neighbour points, paint the current point or whatever. I'd recommend  using scanline fill - see above
    //When you need to add a point to the queue (If it was a recursive function, then you'd call the function from itself with different parameters) use:
    [pointsToCheck addObject:[NSValue valueWithCGPoint:newPoint];

【讨论】:

【参考方案3】:

我用这两种方法解决了我的问题。它有点慢,但我正在优化这个解决方案。

-(void)fillRight:(int)x point2:(int)y witdh:(int)width 
int x1 = x;
int y1 = y;

int offset = 4*((width*round(y1))+round(x1));
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
// NSLog(@"%d %d %@ %@", x,y,color,color1);

if([color1 isEqual: color])

    color3 = self.currentColor ;
    CGFloat r,g,b,a;
    [color3 getRed:&r green:&g blue: &b alpha: &a];
    int reda = (int)(255.0 * r);
    int greena = (int)(255.0 * g);
    int bluea = (int)(255.0 * b);
    int alphaa = (int)(255.0 * a);
    // NSLog(@" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);

    data[offset + 3] = alphaa;
    data[offset + 2] = reda;
    data[offset + 1] = greena;
    data[offset] = bluea;


    [self fillRight:++x1 point2:y1 witdh:width];
    x1 = x1 - 1 ;
    [self fillRight:x1 point2:y1-1 witdh:width];
    [self fillRight:x1 point2:y1+1 witdh:width];



-(void)fillLeft:(int)x point2:(int)y width:(int)width 
int x1 = x;
int y1 = y;

int offset = 4*((width*round(y1))+round(x1));
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
// NSLog(@"%d %d %@ %@", x,y,color,color1);

if([color1 isEqual: color])

    color3 = self.currentColor ;
    CGFloat r,g,b,a;
    [color3 getRed:&r green:&g blue: &b alpha: &a];
    int reda = (int)(255.0 * r);
    int greena = (int)(255.0 * g);
    int bluea = (int)(255.0 * b);
    int alphaa = (int)(255.0 * a);
    // NSLog(@" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);

    data[offset + 3] = alphaa;
    data[offset + 2] = reda;
    data[offset + 1] = greena;
    data[offset] = bluea;

    [self fillLeft:--x1 point2:y1 width:width];
    x1 = x1 + 1 ;
    [self fillLeft:x1 point2:y1-1 width:width];
    [self fillLeft:x1 point2:y1+1 width:width];


【讨论】:

你有解决方案吗,我想了很多天这个表格,但我总是内存溢出,解决方案很慢 我一直没有时间优化解决方案,因为项目被放弃了,抱歉。此代码有效,因此您可以尝试自己优化它。【参考方案4】:

我个人更喜欢使用 A* 算法进行泛洪填充。基本上,您为访问的节点着色。在搜索已知位于填充区域之外的点的路径时。 (-1,-1 可以解决问题)

【讨论】:

以上是关于iPad绘画应用程序上的递归洪水填充的主要内容,如果未能解决你的问题,请参考以下文章

android上的洪水填充着色

使用matlab进行洪水填充

在 iOS / iPad 上编写绘画应用程序的最佳方法

当使用洪水填充算法填充颜色后绘制线条时,填充颜色消失

如何最佳解决洪水填充难题?

想通过 Quartz 在 ipad 绘画应用程序中添加手动擦除选项