我正在研究一种算法来拍摄图像并分离黑白像素块,不幸的是,它似乎总是溢出堆栈。这是可疑课程:
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行抛出了异常。
答案 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。