Подтвердить что ты не робот

Установка уровня сжатия jpg с помощью ImageIO в Java

Я использую javax.imageio.ImageIO, чтобы сохранить BufferedImage как файл jpeg. В частности, я создал следующую функцию Java:

public static void getScreenShot(BufferedImage capture, Path folder, String filename) {
        try {
            ImageIO.write(capture, "jpeg", new File(folder.toString()+"/"+filename+".jpg"));
        } catch (AWTException | IOException ex) {
            Logger.getLogger(ScreenShotMaker.class.getName()).log(Level.SEVERE, null, ex);
        }
}

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

Можно ли установить уровень сжатия и как?

4b9b3361

Ответ 1

Вы должны использовать JPEGImageWriteParam а затем сохранить изображение с помощью ImageWriter.write(). Прежде чем писать, установите вывод через ImageWriter.setOutput.

Установите уровень сжатия следующим образом:

JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(null);
jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(1f);

Где 1f - число с плавающей точкой, которое соответствует 100% качеству. Значение по умолчанию составляет около 70%, если я не помню неправильно.

РЕДАКТИРОВАТЬ

Затем вы должны сделать следующее, чтобы получить экземпляр ImageWriter. Есть два способа, короткий и длинный (я оставляю оба на всякий случай).

Короткий путь (предложенный Лапо в одном комментарии):

final ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
// specifies where the jpg image has to be written
writer.setOutput(new FileImageOutputStream(
  new File(folder.toString() + "/" + filename + ".jpg")));

// writes the file with given compression level 
// from your JPEGImageWriteParam instance
writer.write(null, new IIOImage(capture, null, null), jpegParams);

или более длинный путь

// use IIORegistry to get the available services
IIORegistry registry = IIORegistry.getDefaultInstance();
// return an iterator for the available ImageWriterSpi for jpeg images
Iterator<ImageWriterSpi> services = registry.getServiceProviders(ImageWriterSpi.class,
                                                 new ServiceRegistry.Filter() {   
        @Override
        public boolean filter(Object provider) {
            if (!(provider instanceof ImageWriterSpi)) return false;

            ImageWriterSpi writerSPI = (ImageWriterSpi) provider;
            String[] formatNames = writerSPI.getFormatNames();
            for (int i = 0; i < formatNames.length; i++) {
                if (formatNames[i].equalsIgnoreCase("JPEG")) {
                    return true;
                }
            }

            return false;
        }
    },
   true);
//...assuming that servies.hasNext() == true, I get the first available service.
ImageWriterSpi writerSpi = services.next();
ImageWriter writer = writerSpi.createWriterInstance();

// specifies where the jpg image has to be written
writer.setOutput(new FileImageOutputStream(
  new File(folder.toString() + "/" + filename + ".jpg")));

// writes the file with given compression level 
// from your JPEGImageWriteParam instance
writer.write(null, new IIOImage(capture, null, null), jpegParams);

Ответ 2

Более сжатым способом является получение ImageWriter непосредственно из ImageIO:

ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(0.7f);

ImageOutputStream outputStream = createOutputStream(); // For example implementations see below
jpgWriter.setOutput(outputStream);
IIOImage outputImage = new IIOImage(image, null, null);
jpgWriter.write(null, outputImage, jpgWriteParam);
jpgWriter.dispose();

Вызов ImageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) необходим для явного задания уровня сжатия (качества).

In ImageWriteParam.setCompressionQuality() 1.0f - максимальное качество, минимальное сжатие, а 0.0f - минимальное качество, максимальное сжатие.

ImageWriter.setOutput следует передать ImageOutputStream. Хотя метод принимает Object, в соответствии с документацией он обычно не поддерживается:

Использование общего Object, отличного от ImageOutputStream, предназначено для писателей, которые взаимодействуют непосредственно с устройством вывода или протоколом визуализации. Набор юридических классов рекламируется поставщиком услуг getOutputTypes; большинство авторов вернут одноэлементный массив, содержащий только ImageOutputStream.class, чтобы указать, что они принимают только ImageOutputStream.

В большинстве случаев эти два класса должны обрабатываться:

  • FileImageOutputStream - реализация ImageOutputStream, которая записывает свой вывод непосредственно в File или RandomAccessFile.
  • MemoryCacheImageOutputStream - реализация ImageOutputStream, которая записывает свой вывод в обычный OutputStream. Обычно используется с ByteArrayOutputStream (спасибо за подсказку, @lmiguelmh!).

Ответ 3

Более общий метод будет (от Игорь ответ):

static void saveImage(BufferedImage image,File jpegFiletoSave,float quality) throws IOException{
    // save jpeg image with specific quality. "1f" corresponds to 100% , "0.7f" corresponds to 70%

        ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
        ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
        jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        jpgWriteParam.setCompressionQuality(quality);

        jpgWriter.setOutput(ImageIO.createImageOutputStream(jpegFiletoSave));
        IIOImage outputImage = new IIOImage(image, null, null);
        jpgWriter.write(null, outputImage, jpgWriteParam);
        jpgWriter.dispose();

    }