递归填充溢出堆栈

时间:2013-08-13 02:46:02

标签: java image-processing stack-overflow flood-fill

我正在研究一种算法来拍摄图像并分离黑白像素块,不幸的是,它似乎总是溢出堆栈。这是可疑课程:

package me.dylan.eat;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class Cell {
    public Point location = new Point(0, 0);

    public Cell(int x, int y) {
        location.x = x;
        location.y = y;
    }

    public void recurseNeighbors(ArrayList<Cell> universe, BufferedImage img) {

        if (!universe.contains(this)) {
            universe.add(this);
            ArrayList<Cell> neighbors = CellUtil.assimilateNeighbors(location, img, new Rectangle(0,0,0,0));
                        //get all neighbors of the same color
            for (Cell c : neighbors) {
                if (!universe.contains(c)) {
                    c.recurseNeighbors(universe, img);
                }
            }
        }
    }
}
编辑:图像是640x480,那太大了? 第23行抛出了异常。

2 个答案:

答案 0 :(得分:2)

640x480太大了。最坏的情况是你最终会得到640 * 480 = 307200的深度。

您有几个选择。选项1不是递归地执行,而是维护要处理的像素队列。使用第一轮要检查的单元格初始化队列,然后在队列不为空时,删除前面的项目,处理它,并将要处理的新单元格添加到队列中。

选项2是一种迭代方法,例如描述here的方法(在那里也描述了基于队列的方法)。

虽然递归似乎是实现泛洪填充的一种自然方式,但实际上它通常会遇到堆栈限制,并且迭代或基于队列的算法运行效率更高。

根据您的目标,您可能还需要考虑一种完全不同的方法,例如union-find(如果它们是相同的颜色,则两个单元格是等效的),这将为您提供所有黑色和白色的列表在O(log n)时间(其中n是像素数)中的图像中的分组,一次通过。

答案 1 :(得分:0)

您还可以增加堆栈大小。就个人而言,如果这是自然而然的事情,我更喜欢保持递归算法。这显然只要应用程序要求/限制允许。

这样做的方式取决于JVM。如果您使用的是Hotspot,

java -Xss50m <rest of your command line>

应该这样做。 50m代表50 MB。如果需要,尝试更大的值甚至是64位JVM(请注意,我不是100%确定64位JVM会产生很大的不同,如果有的话)。

堆栈大小的默认值也取决于JVM,但通常从300 KB到1 MB。你会将它增加50倍到165倍。

请注意,这些指令会增加JVM中每个线程的堆栈大小。如果你有太多它们,你可能更喜欢为你的进程创建一个单独的线程,为它指定一个单独的堆栈大小。另请注意,尊重或忽略此参数也取决于JVM。