Заливка стеком

Я использую рекурсивный алгоритм заливки потока в Java для заполнения некоторых областей изображения. С очень маленькими изображениями он отлично работает, но когда изображение de становится больше, JVM дает мне ошибку стека над потоком.

вот почему я должен переопределить метод, используя заливку потока с моим собственным стеком. (Я читал, что это лучший способ сделать это в таких случаях)

может кто-нибудь объяснить мне, как его закодировать? (если у вас нет кода под рукой, с псевдо-кодом алгоритм будет в порядке)

Я много читала в интернете, но я не очень хорошо понял.

EDIT: я добавил свой рекурсивный код

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 ответов


вы можете использовать Queue для удаления рекурсии из алгоритма заполнения. Вот некоторые основные идеи:

  1. есть способ отметить посещенные точки
  2. в начале, очередь начальной точки.
  3. пока очередь не пуста, продолжайте удаление ее элемента. И с каждым элементом
    1. заполните его цвет и отметьте просто-dequeued точку, как посетил
    2. Enqueue непрошеные смежные точки, которые имеют тот же цвет

ниже приведен мой код Java для решения подобной, но другой проблемы обнаружение blob. Я надеюсь, что вы можете получить некоторые идеи из этого и можете адаптировать его в проблему. Однако код не очень хорошо учитывается.

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


вот моя база реализации на infos с этой страницы и других, собранных в интернете (протестировано и работает)

получать удовольствие ;-)

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;
}

вы должны вернуть последний оператор floodFill, превратив его в хвостовой вызов. Это сэкономит вам место в стеке.


важным моментом в заливке наводнения является то, что вы обрабатываете сначала глубину точек или ширину. Depth first-это оригинальное решение, которое вы искали с помощью стека, width first-алгоритмы, показанные ниже, используя очередь для хранения точки. Разница существенна при заполнении больших выпуклых пространств. Первый метод ширины сохраняет на идеально выпуклой области примерно край круга (или край заливки). Если вы используете метод глубины, вы можете в худшем случае сохранить каждый пиксель в conxex область, это означает, что в худшем случае заполненный поток изображений 1000x1000 может потребовать 1000000 кадров стека.