Загрузка и сохранение изображений при помощи библиотеки ввода/вывода изображений (Image I/O Library)

Содержание

1 Введение
2 Программа ReadSharp
3 Запись изображений
4 Определение метаданных посредством класса ImageWriteParam
5 Программа Compress

Введение

Представленный в J2SE 1.4 пакет javax.imageio является основным для Java Image I/O API. Уже само его название подразумевает, что этот пакет помогает читать и писать файлы изображений. Возникает вопрос: что же является настолько важным в этом пакете? Несомненным является тот факт, что, начиная уже с первого выпуска платформы Java, изображения можно читать при помощи метода getImage различных классов, таких как Toolkit и Applet. Однако пакет javax.imageio - это больше, чем просто чтение изображений. Первый важный момент: до Image I/O Library не было ни writeImage, ни putImage. Теперь существует способ писать изображения. Кроме того, появилась возможность устанавливать свойства, например, степень сжатия, при сохранении изображений.

Первый вопрос, который возникает при работе с библиотеками ввода/вывода изображений: какие форматы поддерживаются? В базовой реализации Sun предоставляется определенный набор поддерживаемых форматов. Однако API достаточно гибок, так что возможно установить ваши собственные форматы, расширяя необходимые классы в библиотеке javax.imageio.spi. Но давайте пока отложим в сторону этот аспект библиотеки. Чтобы узнать, какой набор установлен для чтения и записи, просто спросите класс ImageIO через его методы getReaderFormatNames() и getWriterFormatNames() (или getReaderMIMETypes() и getWriterMIMETypes(), если хотите работать непосредственно с MIME типами). Например:

import javax.imageio.*;

public class GetList {
 
public static void main(String args[]) {
   
String readerNames[] = ImageIO.getReaderFormatNames();
    printlist
(readerNames, "Reader names:");
    String readerMimes
[] = ImageIO.getReaderMIMETypes();
    printlist
(readerMimes, "Reader MIME types:");
    String writerNames
[] = ImageIO.getWriterFormatNames();
    printlist
(writerNames, "Writer names:");
    String writerMimes
[] = ImageIO.getWriterMIMETypes();
    printlist
(writerMimes, "Writer MIME types:");
 
}

 
private static void printlist(String names[], String title) {
   
System.out.println(title);
   
for (int i = 0, n = names.length; i < n; i++) {
     
System.out.println("\t" + names[i]);
   
}
  }
}

Выполнив программу GetList в базовой реализации Sun (при условии, что не установлены никакие другие провайдеры), вы должны увидеть следующее:

  Reader names:
           jpeg
           gif
           JPG
           png
           jpg
           JPEG
   Reader MIME types:
           image/png
           image/jpeg
           image/x-png
           image/gif
   Writer names:
           jpeg
           JPG
           png
           jpg
           PNG
           JPEG
   Writer MIME types:
           image/png
           image/jpeg

Таким образом поддерживается чтение изображений в форматах GIF, JPEG и PNG, а запись - в форматах JPEG и PNG. Запись изображений в формате GIF не предусмотрена.

Работая с библиотеками ввода/вывода изображений, можно заметить, что почти вся работа запрашивается через статические методы класса ImageIO. Эти статические методы - все, что нужно для базового использования. Например, чтобы прочитать изображение, его местоположение передается одному из следующих методов read класса ImageIO:

  • read(File input)
  • read(ImageInputStream stream)
  • read(InputStream input)
  • read(URL input)

ImageInputStream, интерфейс пакета javax.imageio.stream, позволяет кеширование данных, а потому является потенциально более быстрым, чем обычный InputStream. Вместо того, чтобы получать InputStream из FileInputStream и передавать InputStream в метод read, эффективнее передать объект File для чтения, или создать FileImageInputStream из File. А именно, вместо:

InputStream is = new InputStream("myimage.jpg");
ImageIO.read
(is);

используйте либо

File file = new File("myimage.jpg");
ImageIO.read
(file);

либо

File file = new File("myimage.jpg");
FileImageInputStream fiis =
new FileImageInputStream(file);
ImageIO.read
(fiis);

Метод read в любом случае возвращает объект BufferedImage. Затем можно нарисовать изображение при помощи метода drawImage класса Graphics или отфильтровать его при помощи чего-либо, подобного ConvolveOp из пакета java.awt.image.

Программа ReadSharp

Следующая программа, ReadSharp, демонстрирует чтение и отображение изображения. В программе также применяется фильтр резкости.

import java.awt.image.*;
import javax.imageio.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;

public class ReadSharp {
 
private static class FrameShower implements Runnable {
   
final Frame frame;

   
public FrameShower(Frame frame) {
     
this.frame = frame;
   
}

   
public void run() {
     
frame.show();
   
}
  }

 
public static void main(String args[]) throws IOException {
   
if (args.length != 1) {
     
System.err.println("Please include image filename on command line");
      System.exit
(-1);
   
}
   
// Read
   
File file = new File(args[0]);
    BufferedImage input = ImageIO.read
(file);
   
// Convert
   
Kernel sharpKernel = new Kernel(3, 3, new float[] { 0.0f, -1.0f, 0.0f,
        -
1.0f, 5.0f, -1.0f, 0.0f, -1.0f, 0.0f });
    ConvolveOp convolveOp =
new ConvolveOp(sharpKernel,
        ConvolveOp.EDGE_NO_OP,
null);
   
int width = input.getWidth();
   
int height = input.getHeight();
    BufferedImage output =
new BufferedImage(width, height,
        BufferedImage.TYPE_INT_ARGB
);
    convolveOp.filter
(input, output);
   
// Make screen
   
Icon icon = new ImageIcon(output);
    JLabel label =
new JLabel(icon);
    JFrame frame =
new JFrame("Sharp Image");
    frame.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane
().add(label, BorderLayout.CENTER);
    frame.pack
();
   
// Show
   
Runnable runner = new FrameShower(frame);
    EventQueue.invokeLater
(runner);
 
}
}

При выполнении программы ReadSharp введите название изображения в командной строке. Используем, например, изображение из Digital Images with ConvolveOp от 10 февраля 2004 года.

java ReadSharp BrightnessChanger.jpg

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

Запись изображений

Запись изображений может быть столь же проста, как и их чтение, хотя можно также определить метаданные, если необходим больший контроль. В базовом варианте есть три формы метода write:

  • write(RenderedImage im, String formatName, File output)
  • write(RenderedImage im, String formatName, ImageOutputStream output)
  • write(RenderedImage im, String formatName, OutputStream output)

Все три формы метода write возвращают логическое значение (boolean). Оно указывает, доступен ли соответствующий объект в системе. Например, при запросе GIF-писателя возвратится false, потому что такого объекта нет в системе.

BufferedImage является типом RenderedImage, поэтому нужно всего лишь передать BufferedImage, возвращенный методом read, методу write. Это облегчит выполнение простых преобразований, например, из GIF в PNG:

File inputFile = new File("image.gif");
BufferedImage input = ImageIO.read
(inputFile);
File outputFile =
new File("image.png");
ImageIO.write
(input, "PNG", outputFile);

Заметьте, что имя формата, переданное методу write, возвращается методом getWriterFormatNames().

Определение метаданных посредством класса ImageWriteParam

При записи изображения можно конфигурировать различные метаданные, такие, например, как степень сжатия. Однако, это нельзя делать непосредственно при помощи метода write класса ImageIO. Вместо этого необходимо использовать некоторые другие классы ввода/вывода изображений, а также пакеты. Далее - коротко о том, как определить эти метаданные посредством класса ImageWriteParam.

Возможно существование более одного провайдера Reader или Writer для определенного формата. Поэтому, методы типа getImageWritersByFormatName класса ImageIO возвращают Iterator. Чтобы настроить степени сжатия вывода, можно провести исследование всех установленных провайдеров и найти поддерживающего максимальную степень сжатия. Можно, также, использовать первого попавшегося и работать с ним. Применим более простой подход:

  Iterator iter =
     ImageIO.getImageWritersByFormatName
("JPG");
  
if (iter.hasNext()) {
    
ImageWriter writer = (ImageWriter)iter.next();
     ...
  
}

Можно получить параметры записи по умолчанию для определенного ImageWriter посредством его метода getDefaultWriteParam. Метод возвращает объект ImageWriteParam. Для JPEG возвращается javax.imageio.plugins.jpeg.JPEGImageWriteParam (хотя знать это не обязательно). Чтобы изменить степень сжатия, необходимо сообщить объекту ImageWriteParam, что вы хотите установить способ сжатия явно:

iwp.setCompressionMode(
    
ImageWriteParam.MODE_EXPLICIT);

Теперь установите качество сжатия при помощи:

iwp.setCompressionQuality(floatLevel);

Вместо того, чтобы выбирать значение наугад, можно спросить, какие значения качества сжатия поддерживаются (или насколько будет выполнено сжатие):

float values[] = iwp.getCompressionQualityValues();

Программа Compress

Ниже приведена программа Compress, преобразующая любое изображение, которое читается посредством Image I/O Library. Программа читает и пишет изображение для каждого поддерживаемого формата сжатия. Поставляемый Sun JPEG кодировщик возвращает из getCompressionQualityValues значения 5%, 75%, и 95%. Заметьте, что программа пишет непосредственно при помощи объекта ImageWriter (а не ImageIO).

Поскольку параметры вывода являются настраиваемыми, для представления записываемого изображения необходимо использовать объект IIOImage (IIO позиции для Image IO). Вы не можете использовать обыкновенный BufferedImage. BufferedImage может быть преобразован в IIOIMAGE дополнительно.

import java.awt.image.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import java.io.*;
import java.util.Iterator;

public class Compress {
 
public static void main(String args[]) throws IOException {
   
if (args.length != 1) {
     
System.err.println("Please include image filename on command line");
      System.exit
(-1);
   
}
   
// Read
   
String name = args[0];
    File file =
new File(name);
    BufferedImage input = ImageIO.read
(file);
   
// Get Writer and set compression
   
Iterator iter = ImageIO.getImageWritersByFormatName("JPG");
   
if (iter.hasNext()) {
     
ImageWriter writer = (ImageWriter) iter.next();
      ImageWriteParam iwp = writer.getDefaultWriteParam
();
      iwp.setCompressionMode
(ImageWriteParam.MODE_EXPLICIT);
     
float values[] = iwp.getCompressionQualityValues();
     
// Write one for each compression values
     
for (int i = 0, n = values.length; i < n; i++) {
       
iwp.setCompressionQuality(values[i]);
        String newName = i + name;
       
if (!newName.endsWith(".jpg")) {
         
newName += ".jpg";
       
}
       
File outFile = new File(newName);
        FileImageOutputStream output =
new FileImageOutputStream(
           
outFile);
        writer.setOutput
(output);
        IIOImage image =
new IIOImage(input, null, null);
        System.out.println
("Writing " + values[i] + "%");
        writer.write
(null, image, iwp);
     
}
    }
  }
}

У Image I/O API значительно больше возможностей, чем здесь продемонстрировано. Эта статья должна помочь вам получить начальные знания по чтению и записи изображений, а так же по настройке вывода. Также интересен пакет javax.imageio.event, позволяющий прослушивать операции чтения и записи изображения.

Подробная информация по Image I/O Library приведена в Java Image I/O API Guide.

Теги: images