Сжатие PNG с использованием JAI (Java Advanced Imaging)

Я использую javax.imageio API и JAI для сжатия различных типов изображений. Он отлично работает для JPEG с использованием JPEGImageWriter и GIF с использованием GIFImageWriter. Но он не поддерживает сжатие PNG с использованием PNGImageWriter, которое выдает исключение, например, тип сжатия не установлен или «Нет допустимого сжатия» и т. д. Поэтому я использовал это ниже ImageWriter для PNG. Работает, но качество изображения очень плохое.

Кто-нибудь может подсказать, как использовать PNGImageWriter для сжатия PNG и какой JAR его содержит?

File input = new File("test.png");

InputStream is = new FileInputStream(input);
BufferedImage image = ImageIO.read(is);

File compressedImageFile = new File(input.getName());

OutputStream os =new FileOutputStream(compressedImageFile);

Iterator<ImageWriter>writers = 
        ImageIO.getImageWritersByFormatName("jpg"); // here "png" does not work
ImageWriter writer = (ImageWriter) writers.next();

ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);

writer.write(null, new IIOImage(image, null, null), param);

person Gnaniyar Zubair    schedule 02.07.2014    source источник


Ответы (2)


Похоже, что com.sun.imageio.plugins.png.PNGImageWriter по умолчанию, поставляемый с JRE, не поддерживает настройку сжатия. Это немного удивительно, так как формат явно поддерживает сжатие. Однако PNGImageWriter всегда пишет сжато.

Вы можете видеть из исходного кода, что он использует:

Deflater def = new Deflater(Deflater.BEST_COMPRESSION);

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

Чтобы исправить свой код, чтобы он работал с любым именем формата, измените строки:

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);

to:

ImageWriteParam param = writer.getDefaultWriteParam();

if (param.canWriteCompressed()) { 
    // NOTE: Any method named [set|get]Compression.* throws UnsupportedOperationException if false
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(0.5f);
}

Он по-прежнему будет записывать сжатые PNG.

Если вам нужен больший контроль над сжатием PNG, например, настройка сжатия или используемого фильтра, вам нужно найти ImageWriter, который его поддерживает. Поскольку вы упомянули JAI, я думаю, что CLibPNGImageWriter, являющийся частью jai-imageio.jar или jai-imageio-tools.jar, поддерживает сжатие настроек. Вам просто нужно просмотреть итератор ImageWriters, чтобы узнать, установлен ли он у вас:

Iterator<ImageWriter>writers = ImageIO.getImageWritersByFormatName("png");
ImageWriter writer = null;

while (writers.hasNext()) {
    ImageWriter candidate = writers.next();

    if (candidate.getClass().getSimpleName().equals("CLibPNGImageWriter")) {
        writer = candidate; // This is the one we want
        break;
    }
    else if (writer == null) {
        writer = candidate; // Any writer is better than no writer ;-)
    }
}

С правильным ImageWriter ваш код должен работать как положено.

person Harald K    schedule 04.07.2014
comment
Спасибо за ваше подробное объяснение. Но когда я попробовал этот код, он выдает исключение, например, сжатие не поддерживается .... Есть идеи? - person Gnaniyar Zubair; 06.07.2014
comment
да. Вы не применили строку if (param.canWriteCompressed()). И что еще более важно, у вас неправильно установлен JAI, поэтому вы все еще не используете CLibPNGImageWriter. Попробуйте распечатать класс писателя или используйте отладчик для проверки. - person Harald K; 06.07.2014
comment
Это работает нормально. Но после сжатия (32 бита) выходное изображение PNG увеличивается в размере по сравнению с исходным изображением. - person Gnaniyar Zubair; 08.07.2014
comment
это увеличивает размер вдвое или втрое вместо уменьшения. - person Gnaniyar Zubair; 08.07.2014
comment
в 2-3 раза больше чего? Другой PNG? Или JPEG? - person Harald K; 09.07.2014

Пожалуйста, убедитесь, что этот код может работать для вас.

ImageWriteParam writeParam = writer.getDefaultWriteParam();

writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

writeParam.setCompressionType("PackBits");

writeParam.setCompressionQuality(0.5f);

Спасибо.

person byreddy    schedule 02.08.2014
comment
Я попробовал решение, но оно говорит: IllegalArgumentException: Unknown Compression Type! с Явой 11 - person Ankur Raiyani; 03.10.2020