2011-10-31 16 views
11

Ho creato questo codice per ridimensionare le immagini con due fattori. Funziona, ma la qualità dell'immagine è pessima dopo che è stata ridimensionata! Mi potete aiutare?Ridimensionamento di un'immagine senza perdita di qualità

Questo è il codice

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

ho visto sul web che resizeImageWithHint è fatto nell'ambito di applicazione in modo da non perdere la qualità .. ma lo fa! perché? Puoi aiutarmi con questo?

risposta

27

Il miglior articolo che abbia mai letto su questo argomento è The Perils of Image.getScaledInstance() (archivio web).

In breve: per ottenere una buona immagine è necessario utilizzare diversi passaggi di ridimensionamento. Metodo di supporto dall'articolo:

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; 
} 
+0

sì, quell'articolo è buono, sia imgscalr che java-image-scaling lo citano, ma anche in questo caso il risultato è diverso, penso che i filtri che il ridimensionamento di immagini java può applicare fanno la differenza. – stivlo

+0

le mie immagini sono png ... puoi postare il mio codice di multistep? pls? – JackTurky

+1

Un buon articolo, ha davvero aiutato a migliorare la qualità delle immagini ridimensionate. Cambiare 'w/= 2' a 'w/= 1.2' mi dà una qualità migliore (verranno eseguiti più loop, quindi la qualità è migliore) –

1

Sì, ho avuto gli stessi problemi e li ho risolti, per favore read my question (la risposta è inclusa nella domanda). Ho provato imgscalr e librerie java-image-scaling e ho trovato la seconda qualità molto migliore. Avvicinati al monitor per apprezzare la differenza tra gli esempi di miniature.

Nonostante i miei pensieri iniziali, ridimensionare un'immagine sembra una cosa molto complicata, non si vuole farlo da soli. Ad esempio, dico a java-image-scaling per utilizzare ResampleFilters.getLanczos3Filter() per ottenere risultati migliori.

Si occupa inoltre di come salvare un JPG con una qualità superiore rispetto allo standard 75, che produce un risultato negativo soprattutto per una miniatura.

Ho anche scritto una piccola classe, chiamata MyImage per aiutare con attività comuni, come la lettura di un'immagine da un array di byte, da un file, ridimensionamento specificando solo larghezza o solo altezza, ridimensionamento specificando un rettangolo di selezione, ridimensionamento specificando larghezza e altezza e aggiungendo una banda bianca per rendere l'immagine non distorta e la scrittura in file JPG.

8

Il seguente codice mi ha prodotto il ridimensionamento di alta qualità con proporzioni conservate. Ho provato alcune cose e ho letto diverse voci presentate qui in altre risposte. Perso due giorni e alla fine ho ottenuto il miglior risultato con metodo Java pianura (provato anche ImageMagick e java-image-scaling biblioteche):

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(); 
    } 
    } 
} 
+0

Il codice postato da @nfechner è un miglioramento per il tuo. Se un'immagine è grande, la qualità non sarà buona come ti aspetteresti. Come scritto da un determinato articolo: "La ragione di questa disparità di qualità è dovuta ai diversi algoritmi di filtraggio in uso. Se si esegue il downscaling di più di due volte, gli algoritmi BILINEAR e BICUBIC tendono a perdere informazioni a causa del modo in cui i pixel vengono campionati dall'immagine sorgente '. Utilizzando un modo di rendering multi-step, l'immagine ridimensionata sarà migliore. –

+0

Ho usato le mie immagini con gli occhi e le immagini ridotte a circa 320 px e questo ha prodotto una qualità migliore a mio parere. Capisco quello che stai dicendo, tuttavia, non ero soddisfatto del risultato ottenuto dall'algoritmo @nfechner, l'ho provato anche io ... –

2

Se la sorgente immagine è un png quindi utilizzare in questo modo:

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

Se si desidera ridimensionare jpeg o gif, senza perdere troppa qualità, ho fatto una libreria nel 2010 per questo: beautylib on github che utilizza internamente questa altra libreria: java-image-scaling. Puoi vedere direttamente il codice sorgente per trovare qualcosa di utile: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java

+0

È consigliato solo per "png"? – Tiny

+0

Non ricordo più, mi dispiace. –

2

Nessuna delle risposte ti aiuterà a ottenere la qualità reale che desideri. Includi thumbailator.jar nel progetto (scaricare dalla qui):

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

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

quindi caricare la prima immagine (in formato senza Thumbnailator - il suo uso è quello di creare i pollici, ma è possibile creare immagini di grandi dimensioni con esso ovviamente) e ridimensionalo a tutte le dimensioni che desideri (con Thumbnailator 800x600 ad esempio). La qualità sarà molto buona Stavo cercando da tanto tempo, questo .jar mi ha aiutato a ottenere quello che volevo.

+0

Ciao, grazie per aver condiviso! Questo è un vecchio post, ma proverò e ricorderò per il futuro. Grazie! – JackTurky

5

La domanda è vecchia ... Ho provato diverse soluzioni navigando poi sul Web, ho ottenuto il miglior risultato utilizzando getScaledInstance(), fornendo Image.SCALE_SMOOTH come argomento. In effetti la qualità dell'immagine risultante era davvero migliore. mio codice qui sotto:

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(); 
} 
+0

È consigliato solo per "png"? – Tiny

+0

@Tiny Per quanto posso ricordare, l'ho provato solo con png ... – user3564042

-1

Risposta: Rimuovere i suggerimenti VALUE_INTERPOLATION_BILINEAR, e VALUE_RENDERING_QUALITY.

Esempio:

public static BufferedImage resizeImage(BufferedImage image, int width, int height) { 

    // Temporary image 

    BufferedImage tmp = image; 

    // Result image 

    BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 

    // Graphics object 

    Graphics2D graphics = (Graphics2D)result.createGraphics(); 

    // Add rendering hints 

    graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 
    graphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 

    // Draw tmp 

    graphics.drawImage(tmp, 0, 0, width, height, null); 

    // Dispose of graphics object 

    graphics.dispose(); 

    // Return image result 

    return result; 
} 

Nota: Per qualche ragione, i suggerimenti VALUE_INTERPOLATION_BILINEAR e VALUE_RENDERING_QUALITY sfocatura dell'immagine ridimensionata.

0

Tutti i metodi pubblicati non funzionano per me Devo ridurre la dimensione di QrCode, ma con i metodi precedenti la qualità è scarsa e lo scanner non funziona, se scatto foto originale e lo ridimensiono in vernice lo scanner sta funzionando .

Problemi correlati