Почему ImageIO не читает BMP-файл, пока он не будет повторно сохранен в MS Paint? - PullRequest
8 голосов
/ 22 ноября 2011

У меня есть файл растрового изображения test3.bmp, который я могу просматривать и редактировать с помощью любого средства просмотра изображений, с которым я тестировал.

Тем не менее, я не могу прочитать это в своем Java-приложении. Если я редактирую BMP в MS Paint, сохраняю, отменяю изменения и сохраняю (test3_resaved.bmp), у меня то же изображение, но с другим размером файла. Меня не интересуют файлы разных размеров ... что значит, что мое приложение может прочитать повторно сохраненный файл.

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

Файлы изображений:

Вот минимальное тестовое приложение:

package Test;

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

import javax.swing.ImageIcon;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class Test extends JFrame {
    private ImageIcon imageIcon;

    public Test(String filename) throws IOException {
        super();
        BufferedImage image = javax.imageio.ImageIO.read(new File(filename));
        imageIcon = new ImageIcon(image);
        setVisible(true);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        repaint();
    }

    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        setSize(imageIcon.getIconWidth(), imageIcon.getIconHeight());
        if (imageIcon != null)
            g2d.drawImage(imageIcon.getImage(), 0, 0, this);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            if (args.length > 0)
                new Test(args[0]);
            else
                System.out.println("usage - specify image filename on command line");
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

Ответы [ 3 ]

10 голосов
/ 22 ноября 2011

(расширяя мои комментарии)

Проблема сводится к следующему: люди обычно считают, что «формат» задается следующей командой:

ImageIO.getReaderFileSuffixes();

поддерживаются Java.

Но это не так, как его следует читать / понимать, потому что это просто не то, как оно работает.

Неправильно: "ImageIO может читать любой файл, закодированный в одном из этих форматов"

Правильно: "ImageIO не может прочитать изображение, закодированное в формате, который не относится к этим форматам"

Но что теперь это говорит о форматах, фигурирующих в этом списке? Ну ... Становится сложно.

Например, этот список обычно возвращает как «PNG», так и «BMP» (и другие форматы). Но нет ни «одного» PNG, ни «одного» BMP. Я могу прийти завтра с «действительным» форматом PNG (под), который был бы прекрасно, но ни один декодер PNG там не декодировал бы (это нужно было бы проверить и принять: но как только он будет принят, он «сломается» "все существующие декодеры PNG там). К счастью, для изображений PNG проблема не так уж и плоха.

Формат BMP очень сложный. Вы можете иметь сжатие или нет (что может объяснить различный размер файла, который вы видели). Вы можете иметь различные заголовки (разной длины, что также может объяснить различный размер файла, который вы видели). Черт, BMP на самом деле настолько сложен, что я думаю, что вы можете встроить пиксели в формате PNG в «оболочку» BMP.

Существует два основных типа файлов BMP:

  • Варианты BMP, появившиеся после создания Java-декодера
  • Варианты BMP, которые достаточно неясны, чтобы разработчики Java ImageIO не считали его достойным поддержки

«Ошибка» заключается в том, что существует один формат PNG или один BMP. Оба формата (и другие форматы изображений тоже) на самом деле «расширяемые». Каждый раз, когда выходит новый вариант, он может сломать любой декодер.

Итак, что происходит в вашем случае, это:

  1. вы читаете свой оригинальный файл BMP из MS Paint, и MS Paint может прочитать этот файл, потому что это формат BMP, который понимает MS Paint.

  2. тот же формат BMP не соответствует используемой вами версии Java (есть надежда, что он будет поддерживаться в другой версии Java, но я бы на это не рассчитывал).

  3. когда вы повторно сохраняете этот файл из MS Paint, вы сохраняете в формате BMP, который определенно не совпадает с исходным форматом (изменяющийся размер файла довольно очевиден) )

  4. этот другой формат поддерживается вашей версией Java.

Теперь, чтобы действительно решить вашу проблему: по моему опыту библиотеки изображений, такие как ImageMagick , могут читать намного больше изображений, чем API Java ImageIO по умолчанию, поэтому я бы посмотрел либо на другие библиотеки изображений, либо обертки вокруг ImageMagick .

Эти библиотеки также обычно обновляются, чтобы поддерживать новые варианты и новые форматы намного быстрее, чем Java. Например, удивительный формат WebP от Google (на 28–34% лучше, чем PNG для изображений без потерь + полупрозрачные изображения) уже поддерживается некоторыми библиотеками для работы с изображениями, но я не затаив дыхание, когда он приходит сделать ImageIO.read (someWebPpicture) ...

Другой вариант - использовать PNG: даже если теоретически PNG может быть расширен, вы вряд ли найдете «не поддерживаемые» PNG в дикой природе. С BMP это слишком часто.

0 голосов
/ 15 марта 2012

Я протестировал два изображения, используя свой собственный Java BMP декодер. Это также сбрасывает некоторую информацию об изображении. Я обнаружил, что оригинальный 32-битный BMP, а восстановленный 24-битный. Поэтому я предполагаю, что imageio bmp reader не может правильно обрабатывать 32-битные BMP.

Обновление: чтобы подтвердить свое давнее предположение, я снова протестировал изображение и все еще проблематично. Проблема в том, что изображение не отображается, и причина в том, что Java ImageIO считает, что изображение полностью прозрачно. Ниже приведен дамп из Java ImageIO, созданного BufferedImage:

 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000
 IntegerInterleavedRaster: width = 494 height = 516 #Bands = 4 xOff = 0 yOff = 0
 dataOffset[0] 0
 java.awt.image.SinglePixelPackedSampleModel@80809ee

Мы можем видеть здесь, что есть 4 полосы, представляющие RGBA, как это интерпретировал Java ImageIO. Правда в том, что четвертая полоса или четвертый байт не предназначены для альфа-канала для 32-битного образа Windows BMP. Это просто мусор или чтобы выровнять двойное слово.

BMP-декодер Windows 3.x и многое другое здесь https://github.com/dragon66/icafe

0 голосов
/ 22 ноября 2011

Здесь приведен пример кода http://www.java2s.com/Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm, который будет перечислять поддерживаемые форматы изображений вашим JDK.

BMP поддерживается расширенным инструментарием изображения http://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html, но я знаю, что он такжев нем есть вещи, которые теперь поддерживаются и базовым JDK.Так что, если его поддерживают оба, то, возможно, поддержка JAI является более полной.Это кажется маловероятным, поскольку это не имеет особого смысла.OTOH, это было Sun.

Если вы используете JDK 6, вы определенно можете сделать PNG (который является более переносимым), вы можете конвертировать ваши изображения?IIRC MS Paint сохранит png.

...