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绘画应用程序上的递归洪水填充的主要内容,如果未能解决你的问题,请参考以下文章