Почему ImageReader возвращает неверное BufferedImage?

Я пытаюсь получить доступ к анимированному изображению GIF с 21 кадром, а затем прочитать 12-й (потому что он начинается с 0?) кадра.

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;

public class PictureSearch {

    public static void search(File file) {
        try {
            ImageReader reader = (ImageReader) ImageIO.getImageReadersBySuffix("gif").next();
            reader.setInput(ImageIO.createImageInputStream(file), false);
            BufferedImage caption = reader.read(12);

            System.out.println(caption.getHeight());
            System.out.println(caption.getWidth());

            caption.flush();

        } catch (IOException e) {
            System.out.println(e);
        }
    }

    public static void main(String[] args) throws IOException {
        List<String> suffixes = new ArrayList<String>();
        suffixes.add(".jpg");
        suffixes.add(".gif");
        suffixes.add(".bmp");
        suffixes.add(".png");

        Iterator<File> files = FileUtils.iterateFiles(new File(
                "F:/test/"), (IOFileFilter) new SuffixFileFilter(
                suffixes), TrueFileFilter.INSTANCE);

        while (files.hasNext()) {
            File file = (File) files.next();
            PictureSearch.search(file);
        }

    }
}

Читатель должен вернуть мне буферизованное изображение высотой 220 и шириной 200 (или высотой 205 и шириной 188, если он игнорирует белые поля вокруг изображения). Но что он делает, так это возвращает мне изображение высотой 155 и шириной 174, что абсурдно, потому что я трижды проверял, а кадр 12 имеет высоту 220 и ширину 200. Все ли я правильно делаю при чтении кадров?


person Rihards    schedule 16.04.2011    source источник
comment
Можешь скинуть ссылку на .gif? Также может помочь sscce.   -  person trashgod    schedule 16.04.2011
comment
@trashgod, вот он: i55.tinypic.com/263veb9.gif Исходный код выше в основном это весь код для чтения анимированного файла gif.   -  person Rihards    schedule 16.04.2011
comment
Я читаю файл со своего рабочего стола, когда появляются неправильные ширина и высота. Мне все еще нужен прослушиватель хода чтения?   -  person Rihards    schedule 16.04.2011
comment
@andrew, добавил sscce в первый пост.   -  person Rihards    schedule 16.04.2011
comment
@Richards, решение для правильных размеров кадров здесь - stackoverflow.com/questions/8933893/   -  person Andrey Mormysh    schedule 19.02.2013


Ответы (2)


Прямоугольник в вашем примере выглядит как рамка, представляющая измененную часть последовательности изображений, начиная с 1. Откройте файл в Gimp, чтобы увидеть.

введите здесь описание изображения

Приложение: похоже на функцию, предназначенную для оптимизации рендеринга. На первый взгляд, я бы сказал, что вы можете полагаться на границы изображения номер getMinIndex(); более поздние кадры кажутся включенными в первый.

Приложение:

есть ли способ получить полные данные пикселей с нормальным изображением и изменениями?

Предполагая, что геометрия известна, вы сможете объединить первое изображение и любое последующее в BufferedImage, как показано здесь.

Код:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;

public class GifBounds {

    /** @see https://stackoverflow.com/questions/5688104 */
    public static void main(String[] args) throws IOException {
        search(new URL("http://i55.tinypic.com/263veb9.gif"));
    }
    public static void search(URL url) throws IOException {
        try {
            ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
            reader.setInput(ImageIO.createImageInputStream(url.openStream()));
            int i = reader.getMinIndex();
            while (true) {
                BufferedImage bi = reader.read(i++);
                System.out.println(i
                    + ": " + bi.getWidth()
                    + ", " + bi.getHeight());
            }

        } catch (IndexOutOfBoundsException e) {
            // ignored
        }
    }
}

Консоль:

1: 200, 220
2: 79, 95
3: 77, 94
4: 78, 95
5: 79, 95
6: 77, 94
7: 78, 95
8: 79, 95
9: 77, 94
10: 180, 205
11: 97, 111
12: 173, 200
13: 174, 155
14: 174, 155
15: 174, 155
16: 174, 155
17: 174, 155
18: 174, 155
19: 174, 155
20: 167, 200
21: 97, 111
person trashgod    schedule 16.04.2011
comment
Я провел несколько экспериментов, подтверждающих вашу точку зрения. Кажется, что 12-е изображение в последовательности имеет меньшую ширину x высоту, чем общий GIF. (И я был неправ, что изображение загружается не полностью.) - person Andrew Thompson; 16.04.2011
comment
@trashgod, да, действительно, что вы предлагаете мне сделать, чтобы решить эту проблему и правильно получить рамку, ее высоту и ширину? - person Rihards; 16.04.2011
comment
@Richards: я подробно изложил выше, но я бы предпочел больший опыт @Andrew Thompson в этой области. Что касается вашего sscce, я исключил зависимость apache в своем примере. - person trashgod; 16.04.2011
comment
@Эндрю Томпсон: я буду рад вашим критическим мыслям о моем подходе. В частности, я не вижу способа получить количество изображений, кроме как отметить IndexOutOfBoundsException. Кроме того, мой sscce был основан на вашем каноническом примере, который я рекомендую вам восстановить. - person trashgod; 16.04.2011
comment
@trashgod, также есть ли способ получить полные данные о пикселях с обычным изображением и изменениями (чтобы я мог работать с getRGB) кадра, а не только области изменения? - person Rihards; 16.04.2011
comment
@trashgod Мой например. сейчас восстановлен. Я раньше не работал с изображениями с несколькими фреймами в Java, поэтому не уверен, что могу внести свой вклад, помимо того, что вы обнаружили. Я более внимательно рассмотрю ваш пример и JavaDocs и посмотрю, смогу ли я придумать что-нибудь стоящее. - person Andrew Thompson; 17.04.2011
comment
@trashgod Я не вижу способа подсчитать количество изображений .. См. ImageReader.getNumImages(true);. Я взломал ваш источник, чтобы увидеть, как он работает. Вы хотите, чтобы я отредактировал ваш пример, чтобы показать мои изменения в вашем коде? (Я надеюсь, что вы его измените. ;) - person Andrew Thompson; 17.04.2011
comment
@Эндрю Томпсон: Ой! Я совершенно забыл getNumImages(). API даже предлагает использовать исключение при определенных обстоятельствах. Я думаю, что я буду стоять на месте, как пример такого подхода. Могу ли я заставить вас обновить ваш ответ, показав ваш вариант? Я вижу ценность наличия обоих для дальнейшего использования. Я бы посоветовал @Richards принять любой из них. - person trashgod; 17.04.2011

Код 1

import java.net.URL;
import java.awt.Image;
import javax.imageio.ImageIO;

class GetGifSize {

    public static void main(String[] args) throws Exception {
        URL urlToImage = new URL("http://i55.tinypic.com/263veb9.gif");
        Image image = ImageIO.read(urlToImage);
        System.out.println( "Image size is " +
            image.getWidth(null) +
            "x" +
            image.getHeight(null) );
    }
}

Вывод

Image size is 200x220

Код 2

Вариант кода, размещенный на сайте TrashGod.

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;

public class GifBounds {

    /** @see http://stackoverflow.com/questions/5688104 */
    public static void main(String[] args) throws IOException {
        search(new URL("http://i55.tinypic.com/263veb9.gif"));
    }
    public static void search(URL url) throws IOException {
        ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
        reader.setInput(ImageIO.createImageInputStream(url.openStream()));
        int i = reader.getMinIndex();
        int offset = i-0;
        int count = reader.getNumImages(true);
        System.out.println("Image count: " + count);
        for (int ii=i; ii<(count-i); ii++) {
            BufferedImage bi = reader.read(ii);
            System.out.println(ii
                + offset
                + ": " + bi.getWidth()
                + ", " + bi.getHeight());
        }
    }
}

Кроме того, я думаю, вы должны отметить ответ Trashgod правильным из двух ответов.

Он был первым, кто добрался до реальной сути проблемы. И вам должен понравиться ответ со скриншотами. Это «все 9 ярдов».

person Andrew Thompson    schedule 16.04.2011
comment
Да, простой ImageIO.read() также дает мне правильную высоту и ширину, но мне нужно получить доступ к 12-му кадру анимированного gif, и это дает мне неправильную высоту и ширину. - person Rihards; 16.04.2011
comment
Хорошо понял. Я внимательно рассмотрю ваш код и посмотрю, что я могу понять для получения правильной ширины/высоты кадра «N». - person Andrew Thompson; 16.04.2011
comment
+1 Я могу проверить sscce Эндрю и вижу 21 кадр в Preview и Gimp. - person trashgod; 16.04.2011
comment
(через несколько мгновений) К сожалению, это не SSCCE, который я могу запустить, поскольку у меня нет общих ресурсов apache в моем пути к классам во время выполнения. Кроме того, для SSCCE полезно использовать горячую ссылку на изображение в сети, как это было в моем примере. Можете ли вы исправить эти два пункта? - person Andrew Thompson; 16.04.2011