1

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

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 行抛出异常。

4

2 回答 2

2

640x480 太大了。在最坏的情况下,您最终会达到 640*480 = 307200 层深。

你有几个选择。选项 1 是不递归执行,而是维护要处理的像素队列。用第一轮待检查的Cell初始化Queue,然后在队列不为空的情况下,取出前面的item,进行处理,在队列中加入新的待处理Cell。

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

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

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

于 2013-08-13T02:53:52.960 回答
0

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

这样做的方式是依赖于 JVM 的。如果您使用的是热点,

java -Xss50m <rest of your command line>

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

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

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

于 2013-08-13T03:20:02.893 回答