web-dev-qa-db-fra.com

Définition du niveau de compression jpg avec ImageIO dans Java

J'utilise javax.imageio.ImageIO pour enregistrer un BufferedImage en tant que fichier jpeg. En particulier, j'ai créé la fonction Java 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);
        }
}

Comme pour tout logiciel de manipulation d'images, je souhaite modifier le niveau de compression du fichier jpeg. Cependant, je recherche cette option qui semble manquer dans ImageIO.

Puis-je définir le niveau de compression et comment?

42
mat_boy

Vous devez utiliser JPEGImageWriteParam puis enregistrer l'image avec ImageWriter.write() . Avant d'écrire, définissez la sortie via ImageWriter.setOutput .

Définissez le niveau de compression comme suit:

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

1f est un nombre flottant qui signifie 100% de qualité. La valeur par défaut est environ 70% si je ne me souviens pas mal.

[~ # ~] modifier [~ # ~]

Ensuite, vous devez procéder comme suit pour obtenir une instance d'un ImageWriter. Il y a deux façons, une courte et une longue (je garde les deux, juste au cas où).

Le court chemin (suggéré par lapo dans un commentaire) est:

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);

ou plus long chemin

// 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);
46
JeanValjean

Une manière plus succincte consiste à obtenir le ImageWriter directement à partir de 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();

L'appel à ImageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) est nécessaire afin de définir explicitement le niveau de compression (qualité).

Dans ImageWriteParam.setCompressionQuality()1.0f Est la qualité maximale, compression minimale, tandis que 0.0f Est la qualité minimale, compression maximale.

ImageWriter.setOutput Doit être passé un ImageOutputStream . Bien que la méthode accepte Object, selon la documentation, elle n'est généralement pas prise en charge:

L'utilisation d'un Object général autre qu'un ImageOutputStream est destiné aux rédacteurs qui interagissent directement avec un périphérique de sortie ou un protocole d'imagerie. L'ensemble des classes juridiques est annoncé par la méthode getOutputTypes du fournisseur de services de l'auteur; la plupart des rédacteurs renverront un tableau à un seul élément contenant uniquement ImageOutputStream.class pour indiquer qu'ils acceptent uniquement un ImageOutputStream.

La plupart des cas doivent être traités par ces deux classes:

  • FileImageOutputStream - une implémentation de ImageOutputStream qui écrit sa sortie directement dans un File ou RandomAccessFile.
  • MemoryCacheImageOutputStream - une implémentation de ImageOutputStream qui écrit sa sortie dans un OutputStream normal. Habituellement utilisé avec ByteArrayOutputStream (merci pour l'astuce, @ lmiguelmh !).
55
Igor Klimer

Une méthode plus générale sera (de réponse d'Igor ):

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();

    }
0
Naeem