Java / Image: Как сделать прозрачными смежные пиксели фона? - PullRequest
1 голос
/ 08 июня 2011

Существует много вопросов о том, как сделать фоновый цвет изображения прозрачным, но все anwers, кажется, используют RgbImageFilter, чтобы сделать каждое вхождение определенного цвета прозрачным.

MyВопрос в том, как бы реализовать это «удаление фона» в Java, чтобы оно заливало прозрачность из фиксированной точки (согласно операции «ведро» в Paint или функции RMagick Image # matte_floodfill )?

Ответы [ 3 ]

1 голос
/ 12 декабря 2012

Как и в случае с Интернетом, я попал на эту страницу после небольшого поиска, пытаясь найти какой-то код, который сделал что-то подобное.

Вот мое выбранное решение. Это не идеально, но, возможно, это отправная точка для того, кто пытается это сделать.

Это работает путем выбора четырех углов изображения, их усреднения и использования этого в качестве цвета привязки. Я использовал класс Pixel для того, что сначала казалось мне удобным, и в итоге я терял время! Хах. Как это.

public class Pixel implements Comparable{

    int x,y;

    public Pixel(int x, int y){
        this.x = x;
        this.y = y;
    }

    @Override
    public int compareTo(Object arg0) {
        Pixel p = (Pixel) arg0;
        if(p.x == x && p.y == y)
            return 0;
        return -1;
    }

}

А вот и говядина:

public class ImageGrab {

    private static int pixelSimilarityLimit = 20;

    public static void main(String[] args){
        BufferedImage image = null;
        try {
            URL url = new URL("http://animal-photography.com/thumbs/russian_blue_cat_side_view_on_~AP-0PR4DL-TH.jpg");
            image = ImageIO.read(url);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Color[] corners = new Color[]{new Color(image.getRGB(0, 0)),
                new Color(image.getRGB(image.getWidth()-1, 0)),
                new Color(image.getRGB(0, image.getHeight()-1)),
                new Color(image.getRGB(image.getWidth()-1, image.getHeight()-1))};

        int avr = 0, avb=0, avg=0, ava=0;
        for(Color c : corners){
            avr += c.getRed();
            avb += c.getBlue();
            avg += c.getGreen();
            ava += c.getAlpha();
        }
        System.out.println(avr/4+","+avg/4+","+avb/4+","+ava/4);

        for(Color c : corners){
            if(Math.abs(c.getRed() - avr/4) < pixelSimilarityLimit &&
                Math.abs(c.getBlue() - avb/4) < pixelSimilarityLimit &&
                Math.abs(c.getGreen() - avg/4) < pixelSimilarityLimit &&
                Math.abs(c.getAlpha() - ava/4) < pixelSimilarityLimit){

            }
            else{
                return;
            }
        }

        Color master = new Color(avr/4, avg/4, avb/4, ava/4);

        System.out.println("Image sufficiently bordered.");

        LinkedList<Pixel> open = new LinkedList<Pixel>();
        LinkedList<Pixel> closed = new LinkedList<Pixel>();
        open.add(new Pixel(0,0));
        open.add(new Pixel(0,image.getHeight()-1));
        open.add(new Pixel(image.getWidth()-1,0));
        open.add(new Pixel(image.getWidth()-1,image.getHeight()-1));

        while(open.size() > 0){
            Pixel p = open.removeFirst();
            closed.add(p);

            for(int i=-1; i<2; i++){
                for(int j=-1; j<2; j++){
                    if(i == 0 && j == 0)
                        continue;
                    if(p.x+i < 0 || p.x+i >= image.getWidth() || p.y+j < 0 || p.y+j >= image.getHeight())
                        continue;

                    Pixel thisPoint = new Pixel(p.x+i, p.y+j); boolean add = true;
                    for(Pixel pp : open)
                        if(thisPoint.x == pp.x && thisPoint.y == pp.y)
                            add = false;
                    for(Pixel pp : closed)
                        if(thisPoint.x == pp.x && thisPoint.y == pp.y)
                            add = false;

                    if(add && areSimilar(master,new Color(image.getRGB(p.x+i, p.y+j)))){
                        open.add(thisPoint);
                    }
                }
            }
        }

        for(Pixel p : closed){
            Color c = new Color(image.getRGB(p.x, p.y));
            Color newC = new Color(0, 0, 0, 0);
            image.setRGB(p.x, p.y, newC.getRGB());
        }

        try {
            File outputfile = new File("C:/Users/Mike/Desktop/saved.png");
            ImageIO.write(image, "png", outputfile);
        } catch (IOException e) {

        }

    }

    public static boolean areSimilar(Color c, Color d){

        if(Math.abs(c.getRed() - d.getRed()) < pixelSimilarityLimit &&
                Math.abs(c.getBlue() - d.getBlue()) < pixelSimilarityLimit &&
                Math.abs(c.getGreen() - d.getGreen()) < pixelSimilarityLimit &&
                Math.abs(c.getAlpha() - d.getAlpha()) < pixelSimilarityLimit){
                return true;
            }
            else{
                return false;
            }

    }

}

В случае, если кто-то беспокоится, рассмотрите это общественное достояние. Ура! Надеюсь, это поможет.

0 голосов
/ 08 мая 2013

Вот кое-что, что я просто собрал, чтобы удалить фон из BufferedImage. Это довольно просто, но могут быть более эффективные способы сделать это.

У меня есть три входа (исходное изображение, допустимый допуск и цвет, которым вы хотите заменить фон). Он просто возвращает буферизованное изображение с внесенными в него изменениями.

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

Для того, чтобы сделать фон прозрачным, вам нужно передать

BufferedImage RemoveBackground(BufferedImage src, float tol, int color)
{
    BufferedImage dest = src;
    int h = dest.getHeight();
    int w = dest.getWidth();
    int refCol = -(dest.getRGB(2,2)+dest.getRGB(w-2,2)+dest.getRGB(2,h-2)+dest.getRGB(w-2,h-2))/4;
    int Col = 0;
    int x = 1;
    int y = 1;
    int upperBound = (int)(refCol*(1+tol));
    int lowerBound = (int)(refCol*(1-tol));

    while (x < w)
    {
        y = 1;
        while (y < h)
        {
            Col = -dest.getRGB(x,y);
            if (Col > lowerBound && Col < upperBound)
            {
                dest.setRGB(x,y,color);
            }
           y++; 
        }
        x++;
    }

    return dest;
}   

Я знаю, что это старая ветка, но, надеюсь, это кому-нибудь пригодится.

Редактировать: я только что понял, что это не работает для прозрачных пленок, просто для замены значения RGB другим значением RGB. Для значений ARGB потребуется небольшая адаптация.

0 голосов
/ 11 июня 2011

Неудовлетворительное решение, которое я сейчас использую, - это просто предвосхищение цвета фона, на котором вы собираетесь разместить прозрачное изображение (как вы обычно это делаете), и использование решения с RgbImageFilter, как описано здесь .

Если кто-то хочет опубликовать удовлетворительное решение, пожалуйста, сделайте - до тех пор, я собираюсь принять это, как оно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...