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

Тема Зацепин
268

Java-разработчик 🧩
43
2 минуты

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

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

Добавлено : 18 Mar 2009, 17:08

Содержание

Введение

Представленный в 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

Еще от автора

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Совмещение изображений

1 Введение 2 Правила визуализации и пример 3 Совмещение изображений в оперативной памяти 4 Постепенное исчезновение изображения 5 Ссылки и дополнительная информация

Еще по теме

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Использование потоков

1 Введение 2 Работа с выражениями типа Boolean 3 Класс JoptionPane 4 Приложение-счетчик 5 Ссылки

Перехват необрабатываемых исключений

В статье от 16 марта 2004 года Best Practices in Exception Handling были описаны приемы обработки исключений. В данной статье вы изучите новый способ обработки исключений при помощи класса UncaughtExceptionHandler добавленного в J2SE 5.0.

Использование класса LinkedHashMap

1 Введение 2 Сортировка хэш-таблицы 3 Копирование таблицы 4 Сохранение порядка доступа к элементам 5 Ссылки

Сказ про кодировки и java

С кодировками в java плохо. Т.е., наоборот, все идеально хорошо: внутреннее представление строк – Utf16-BE (и поддержка Unicode была с самых первых дней). Все возможные функции умеют преобразовывать строку из маленького регистра в большой, проверять является ли данный символ буквой или цифрой, выполнять поиск в строке (в том числе с регулярными выражениями) и прочее и прочее. Для этих операций не нужно использовать какие-то посторонние библиотеки вроде привычных для php mbstring или iconv. Как говорится, поддержка многоязычных тестов “есть в коробке”. Так откуда берутся проблемы? Проблемы возникают, как только строки текста пытаются “выбраться” из jvm (операции вывода текста различным потребителям) или наоборот пытаются в эту самую jvm “залезть” (операция чтения данных от некоторого поставщика).