web-dev-qa-db-fra.com

Redimensionnement d'une image sans perte de qualité

J'ai fait ce code pour redimensionner les images avec deux facteurs. Cela fonctionne, mais la qualité de l'image est très mauvaise après le redimensionnement! Pouvez-vous m'aider?

C'est le code

public class ImageTest {

private static final int factor1 = 3;
private static final int factor2 = 4;
public static void main(String [] args){

    JFileChooser cs = new JFileChooser();
    cs.setFileSelectionMode(cs.DIRECTORIES_ONLY);
    int i = cs.showOpenDialog(null);
    if(i==cs.APPROVE_OPTION){
        File f = cs.getSelectedFile();
        File[] ff = f.listFiles();
        for(int j=0;j<ff.length;j++){
            String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1);
            System.out.println(end);
            try{
                BufferedImage originalImage = ImageIO.read(ff[j]);
                int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
                BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type);
                ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName()));
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }


}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){
    int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2;
    int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2;
    BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type);
    Graphics2D g = resizedImage.createGraphics();
    g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null);
    g.dispose();    
    g.setComposite(AlphaComposite.Src);

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    return resizedImage;
}   
   }

J'ai vu sur le web que resizeImageWithHint se fait dans le champ d'application afin de ne pas perdre en qualité .. mais c'est le cas! Pourquoi? Peux-tu m'aider avec ceci?

12
JackTurky

Le meilleur article que j'ai jamais lu sur ce sujet est Les dangers d'Image.getScaledInstance () (archive Web).

En bref: vous devez utiliser plusieurs étapes de redimensionnement pour obtenir une bonne image. Méthode d'assistance de l'article:

public BufferedImage getScaledInstance(BufferedImage img,
                                       int targetWidth,
                                       int targetHeight,
                                       Object hint,
                                       boolean higherQuality)
{
    int type = (img.getTransparency() == Transparency.OPAQUE) ?
        BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = (BufferedImage)img;
    int w, h;
    if (higherQuality) {
        // Use multi-step technique: start with original size, then
        // scale down in multiple passes with drawImage()
        // until the target size is reached
        w = img.getWidth();
        h = img.getHeight();
    } else {
        // Use one-step technique: scale directly from original
        // size to target size with a single drawImage() call
        w = targetWidth;
        h = targetHeight;
    }

    do {
        if (higherQuality && w > targetWidth) {
            w /= 2;
            if (w < targetWidth) {
                w = targetWidth;
            }
        }

        if (higherQuality && h > targetHeight) {
            h /= 2;
            if (h < targetHeight) {
                h = targetHeight;
            }
        }

        BufferedImage tmp = new BufferedImage(w, h, type);
        Graphics2D g2 = tmp.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
        g2.drawImage(ret, 0, 0, w, h, null);
        g2.dispose();

        ret = tmp;
    } while (w != targetWidth || h != targetHeight);

    return ret;
}
29
nfechner

Le code suivant m'a produit un redimensionnement de la plus haute qualité avec un rapport hauteur/largeur préservé. Essayé peu de choses et lu plusieurs entrées présentées ici dans d'autres réponses. J'ai perdu deux jours et au final, j'ai obtenu le meilleur résultat avec la méthode Java simple (essayé aussi avec les bibliothèques ImageMagick et Java-image-scaling):

public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
  BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
  double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
  if (width < 1) {
    width = (int) (height * ratio + 0.4);
  } else if (height < 1) {
    height = (int) (width /ratio + 0.4);
  }

  Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
  BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
  Graphics2D g2d = bufferedScaled.createGraphics();
  g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  g2d.drawImage(scaled, 0, 0, width, height, null);
  dest.createNewFile();
  writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
  return true;
}


/**
* Write a JPEG file setting the compression quality.
*
* @param image a BufferedImage to be saved
* @param destFile destination file (absolute or relative path)
* @param quality a float between 0 and 1, where 1 means uncompressed.
* @throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
      throws IOException {
  ImageWriter writer = null;
  FileImageOutputStream output = null;
  try {
    writer = ImageIO.getImageWritersByFormatName("jpeg").next();
    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(quality);
    output = new FileImageOutputStream(new File(destFile));
    writer.setOutput(output);
    IIOImage iioImage = new IIOImage(image, null, null);
    writer.write(null, iioImage, param);
  } catch (IOException ex) {
    throw ex;
  } finally {
    if (writer != null) {
      writer.dispose();
    }
    if (output != null) {
      output.close();
    }
  }
}
9
Mladen Adamovic

Savoir que la question est ancienne ... J'ai essayé différentes solutions en surfant sur le Web, j'ai obtenu le meilleur résultat en utilisant getScaledInstance(), en fournissant Image.SCALE_SMOOTH en argument. En fait, la qualité d'image obtenue était vraiment meilleure. Mon code ci-dessous:

final int THUMB_SIDE = 140;
try {
    BufferedImage masterImage = ImageIO.read(startingImage);
    BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = thumbImage.createGraphics();
    g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null);
    g2d.dispose();
    String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png";
    ImageIO.write(thumbImage, "png", new File(thumb_path));
} catch (IOException e) {
    e.printStackTrace();
}
5
user3564042

Aucune des réponses ne vous aidera à obtenir la vraie qualité que vous désirez. Incluez thumbailator.jar dans votre projet (téléchargez le formulaire ici):

https://code.google.com/p/thumbnailator/

https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2

Ensuite, téléchargez d'abord l'image (sous forme de fichier, sans Thumbnailator - son utilisation est de créer des vignettes, mais vous pouvez créer de grandes images avec elle bien sûr), puis redimensionnez-la à chaque dimension de votre choix (avec Thumbnailator 800x600 par exemple). La qualité sera très bonne. Je cherchais depuis longtemps, ce .jar m'a aidé à réaliser ce que je voulais.

2
DarioBB

Si votre source d'image est une png, utilisez comme ceci:

Image imgSmall = imgBig.getScaledInstance(
        targetWidth, targetHeight, Image.SCALE_SMOOTH);

Si vous voulez redimensionner jpeg ou gif sans perdre trop de qualité, j'ai créé une bibliothèque en 2010 pour cela: beautylib sur github qui utilise en interne cette autre bibliothèque: Java-image-scaling . Vous pouvez voir directement le code source pour trouver quelque chose d’utile: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.Java

2
Felipe

Oui, j'ai eu les mêmes problèmes et les ai résolus, s'il vous plaît lisez ma question (la réponse est intégrée à la question). J'ai essayé imgscalr et Java-image-scaling bibliothèques et j'ai trouvé la deuxième beaucoup meilleure qualité. Approchez-vous du moniteur pour apprécier la différence entre les exemples miniatures.

Malgré mes réflexions initiales, redimensionner une image semble être une tâche très compliquée, vous ne voulez pas le faire vous-même. Par exemple, je dis à Java-image-scaling d'utiliser ResampleFilters.getLanczos3Filter() pour obtenir de meilleurs résultats.

Il explique également comment enregistrer un fichier JPG avec une qualité supérieure à la norme 75, ce qui produit un mauvais résultat, en particulier pour une vignette.

J'ai également écrit une petite classe, appelée MyImage pour aider aux tâches courantes, telles que la lecture d'une image dans un tableau d'octets, dans un fichier, la mise à l'échelle en spécifiant uniquement la largeur ou la hauteur, la mise à l'échelle en spécifiant un cadre de sélection, la mise à l'échelle en spécifiant la largeur et la hauteur et en ajoutant une bande blanche pour que l'image ne soit pas déformée et en écrivant dans un fichier JPG.

1
stivlo

Toutes les méthodes publiées ne fonctionnent pas pour moi, je dois réduire la taille de QrCode, mais avec les méthodes ci-dessus, la qualité est médiocre et le scanner ne fonctionne pas. Si je prends une photo d'origine et la redimensionne dans Paint, le scanner fonctionne.

0
cyril