使用堆栈填充洪水

时间:2010-05-06 17:43:44

标签: java image-manipulation flood-fill

我在Java中使用递归Flood填充算法来填充图像的某些区域。 使用非常小的图像可以正常工作,但是当de image变大时,JVM会给出Stack Over Flow Error。

这就是我必须使用我自己的堆栈使用Flood Fill重新实现该方法的原因。 (我读到这是在这种情况下做到这一点的最好方法)

任何人都可以解释一下如何编码吗? (如果你手边没有代码,算法的伪代码就可以了)

我在互联网上看了很多,但我对此并不了解。

编辑:我添加了我的递归代码

public void floodFill(int x, int y, Color targetColor,Color replacementColor) {

    if (img.getRGB(x, y) != targetColor.getRGB()) return;

    img.setRGB(x, y, replacementColor.getRGB());
    floodFill(x - 1, y, targetColor, replacementColor);
    floodFill(x + 1, y, targetColor, replacementColor);
    floodFill(x, y - 1, targetColor, replacementColor);
    floodFill(x, y + 1, targetColor, replacementColor);

    return;

}

谢谢!

4 个答案:

答案 0 :(得分:17)

您可以使用Queue从floodfill算法中删除递归。以下是一些基本想法:

  1. 有办法标记访问点
  2. 在开始时,将起点排队。
  3. 队列不为空时,继续将其元素出列。并与每个元素
    1. 填充颜色并标记刚出列的点
    2. 将具有相同颜色的未访问的相邻点排队
  4. 以下是我的Java代码,用于解决类似但不同的 blob detection 问题。我希望你能从中得到一些想法,并能使问题适应它。但是代码并没有很好的考虑。

    package blobdetector;
    
    import java.awt.Point;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.LinkedList;
    import java.util.Queue;
    import javax.imageio.ImageIO;
    import javax.management.Query;
    
    public class Main {
    
        public Main() {
        }
    
        public static boolean isBlack(BufferedImage image, int posX, int posY) {
    
            int color = image.getRGB(posX, posY);
    
            int brightness = (color & 0xFF) + ((color >> 2) & 0xFF)
            + ((color >> 4) & 0xFF);
            brightness /= 3;
    
            return brightness < 128;
        }
    
        public static void main(String[] args) {
            if (args.length != 1) {
                System.err.println("ERROR: Pass filename as argument.");
                return;
            }
    
            String filename = args[0];
            // String filename =
            // "C:\\Users\\Natthawut\\Desktop\\blob.jpg";
            try {
                BufferedImage bimg = ImageIO.read(new File(filename));
    
                boolean[][] painted = new boolean[bimg.getHeight()][bimg.getWidth()];
    
                for (int i = 0; i < bimg.getHeight(); i++) {
                    for (int j = 0; j < bimg.getWidth(); j++) {
    
                        if (isBlack(bimg, j, i) && !painted[i][j]) {
    
                            Queue<Point> queue = new LinkedList<Point>();
                            queue.add(new Point(j, i));
    
                            int pixelCount = 0;
                            while (!queue.isEmpty()) {
                                Point p = queue.remove();
    
                                if ((p.x >= 0)
                                        && (p.x < bimg.getWidth() && (p.y >= 0) && (p.y < bimg
                                                .getHeight()))) {
                                    if (!painted[p.y][p.x]
                                                      && isBlack(bimg, p.x, p.y)) {
                                        painted[p.y][p.x] = true;
                                        pixelCount++;
    
                                        queue.add(new Point(p.x + 1, p.y));
                                        queue.add(new Point(p.x - 1, p.y));
                                        queue.add(new Point(p.x, p.y + 1));
                                        queue.add(new Point(p.x, p.y - 1));
                                    }
                                }
                            }
                            System.out.println("Blob detected : " + pixelCount
                                    + " pixels");
                        }
    
                    }
                }
    
            } catch (IOException ex) {
                ex.printStackTrace();
            }
    
        }
    
    }
    

    测试输入:

    alt text

答案 1 :(得分:5)

这是我在此页面上的信息实施基础以及其他在网络上收集的信息(经过测试和工作)

玩得开心; - )

public static void floodFillImage(BufferedImage image,int x, int y, Color color) 
{
    int srcColor = image.getRGB(x, y);
    boolean[][] hits = new boolean[image.getHeight()][image.getWidth()];

    Queue<Point> queue = new LinkedList<Point>();
    queue.add(new Point(x, y));

    while (!queue.isEmpty()) 
    {
        Point p = queue.remove();

        if(floodFillImageDo(image,hits,p.x,p.y, srcColor, color.getRGB()))
        {     
            queue.add(new Point(p.x,p.y - 1)); 
            queue.add(new Point(p.x,p.y + 1)); 
            queue.add(new Point(p.x - 1,p.y)); 
            queue.add(new Point(p.x + 1,p.y)); 
        }
    }
}

private static boolean floodFillImageDo(BufferedImage image, boolean[][] hits,int x, int y, int srcColor, int tgtColor) 
{
    if (y < 0) return false;
    if (x < 0) return false;
    if (y > image.getHeight()-1) return false;
    if (x > image.getWidth()-1) return false;

    if (hits[y][x]) return false;

    if (image.getRGB(x, y)!=srcColor)
        return false;

    // valid, paint it

    image.setRGB(x, y, tgtColor);
    hits[y][x] = true;
    return true;
}

答案 2 :(得分:1)

您应该返回最后一个floodFill语句,将其转换为尾调用。这样可以节省堆栈空间。

答案 3 :(得分:1)

洪水填充的一个重点是,如果您首先处理深度优先或宽度。深度优先是您使用堆栈查看的原始解决方案,广度优先是下面使用队列存储点的算法。填充大凸空间时差异很大。广度优先方法大致存储在圆形边缘(或填充边缘)的完美凸起区域上。如果你使用深度优先方法,你可能在最坏的情况下存储conxex区域中的每个像素,这意味着在最坏的情况下填充1000x1000图像洪水可能需要1000000个堆栈帧。