StackOverflowError во время рекурсивного прохождения BufferedImage - PullRequest
0 голосов
/ 06 июня 2018

У меня есть изображение политической карты, и я хотел бы раскрасить страны на нем.Я загружаю изображение в BufferedImage, просматриваю пиксели и окрашиваю белые.Я использую рекурсивный метод для заполнения пробелов, и хотя он не бесконечен (по крайней мере, я так думаю), я получаю StackOverflowError.Также изображение, которое я использую, невелико, оно имеет только 150x160 пикселей.

Вот мой код.Я делаю что-то неправильно?Это даже хороший подход, может быть, я должен попробовать что-то еще?

    private final int[] COLORS = {-65536,-15073025,-16726785,-16711757,-16711919,-256,-417268,-455455,-5741663,-14194369,-14730152,-9885900};

    private int colorCounter;


    private BufferedImage image;

    public ImageColoring(BufferedImage image) {
        this.image = image;
    }

    public BufferedImage colorImage(){
        for(int i = 0; i<image.getWidth();i++){
            for(int j =0;j<image.getHeight();j++){
                if(image.getRGB(i,j) == -1){
                    fill(i,j);
                    incrementCounter();
                }

            }
        }
        return image;
    }


    private void fill(int x, int y){
        if(x<0 || y<0 || x>=image.getWidth() || y>=image.getHeight()) return;
        if(image.getRGB(x,y)!=-1) return;
        image.setRGB(x,y,COLORS[colorCounter]);
        fill(x+1,y);
        fill(x-1,y);
        fill(x,y+1);
        fill(x,y-1);

    }

    private void incrementCounter(){
        if(++colorCounter == COLORS.length) colorCounter = 0;
    }
}

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Вы можете избежать переполнения стека, ограничивая рекурсию обработкой небольшой части изображения.Следующий код ограничивает размер обрабатываемой области SIZE.Обратите внимание, что это также уменьшает количество времени, которое рекурсивный метод вызывается (см. Комментарии).

import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ImageColoring {

    private static final int COLOR_TO_REPLACE = 0;
    private static final int NEW_COLOR = -15073025;
    private BufferedImage image;
    private static final int SIZE = 10;

    public ImageColoring(BufferedImage image) {
        this.image = image;
    }

    public BufferedImage colorImage(){

        for(int i = 0; i<image.getWidth();i++){
            for(int j =0;j<image.getHeight();j++){
                if(image.getRGB(i,j) == COLOR_TO_REPLACE){
                    fill(i,j);
                }
            }
        }
        return image;
    }

    private void fill(int x, int y){

        if((x<0) || (y<0) || (x>=image.getWidth()) || (y>=image.getHeight())) {
            return;
        }
        if(image.getRGB(x,y)!= COLOR_TO_REPLACE) {
            return;
        }

        int xMax = ((x + SIZE) > image.getWidth())  ?  image.getWidth() : x + SIZE ;
        int yMax = ((y + SIZE) > image.getHeight()) ?  image.getHeight() : y + SIZE;

        while(fill(x, xMax, y, yMax)) {

            x = xMax; y = yMax;
            xMax = ((x + SIZE) > image.getWidth())  ?  image.getWidth() : x + SIZE ;
            yMax = ((y + SIZE) > image.getHeight()) ?  image.getHeight() : y + SIZE;
        }
    }

    private boolean fill(int x, int xMax, int y, int yMax){

        if( (x>=xMax) || (y>=yMax)) {
            return false;
        }
        if(image.getRGB(x,y)!= COLOR_TO_REPLACE) {
            return false;
        }

        image.setRGB(x,y,NEW_COLOR);

        if(fill(x+1,y, xMax, yMax) ||
                //fill(x-1,y, xMax, yMax)||     not needed. it enough to progress 
                //fill(x,y-1, xMax, yMax) ||    in one direction to cover all 
                fill(x,y+1, xMax, yMax) )   { return true; };

        return false;
    }

    public static void main(String[] args) throws IOException {

        String imagePath = "https://upload.wikimedia.org/wikipedia/commons/3/3f/Crystal_Project_bug.png";
        URL url = new URL(imagePath);
        ImageColoring ic = new ImageColoring(ImageIO.read(url));
        JFrame window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel grid = new JPanel(new GridLayout(1, 2, 15, 15));
        grid.add(new JLabel(new ImageIcon(ImageIO.read(url))));
        grid.add(new JLabel(new ImageIcon(ic.colorImage())));
        window.add(grid);
        window.pack();
        window.setVisible(true);
    }
}

(Логика изменения цвета должна быть изменена)

enter image description here

0 голосов
/ 06 июня 2018

Я думаю, что ваше решение звучит красиво и коротко, дело в том, что это изображение с ay = 160, может дать вашему коду возможность создать стек размером не менее 160 y-1, даже не учитывая другие случаи.

Я бы предложил перейти к менее интенсивному использованию стека итерации.Вы можете сделать 4 разных цикла друг под другом (не вложенные!).Делает его менее читабельным, но по крайней мере менее трудоемким.

...