2009-10-26 17 views
14

Ho un'app open source che carica foto su Facebook. Per risparmiare larghezza di banda, le foto vengono automaticamente ridimensionate prima del caricamento (Facebook impone un limite massimo di dimensioni). Alcune persone si sono lamentate della qualità della foto e in effetti puoi vedere la differenza (vedi this issue per alcune immagini dimostrative).Qualità ridimensionamento immagine (Java)

Quindi la mia domanda è: qual è il modo "migliore" di ridimensionare le immagini (ad esempio foto) in Java senza perdere qualità o, almeno, con perdita di qualità/artefatti minimi?

È possibile visualizzare il codice corrente che ho here (ridimensionare codice tramite this page).

risposta

12

Ho provato tutto, compresi i trucchi here, e tutto quello che posso dire che è meglio usare ImageMagick con qualsiasi interfaccia, le librerie di imaging Javas non sono all'altezza di snuff quando si tratta di questo. È necessario supportare così tanti formati e algoritmi per farlo bene.

+0

Speravo di evitare una chiamata esterna ma penso che potresti avere ragione. Sembra che Java non abbia ancora ottenuto l'elaborazione delle immagini! –

+1

Questa è la mia conclusione pure! Le immagini apis di Java fanno schifo! Posso aggirare la lentezza, ma la scarsa qualità dell'immagine non è accettabile! Spero che gli apis pertinenti ottengano qualche tanto bisogno di TLC insieme a JavaFX 2.0. – Kimble

+1

Ovviamente non hai trovato [java-image-scaling] (http://code.google.com/p/java-image-scaling/). –

2

Che suggerimento di rendering stai usando? Di solito il ricampionamento bicubico sarà il migliore. Nelle foto che stai collegando sono molto jaggy, il che mi fa pensare che stai usando il vicino più vicino come suggerimento.

Nella classe PictureScaler a cui ci si collega, nel metodo paintComponent, utilizza sei diversi modi per ridimensionare l'immagine. Hai provato tutti e sei a vedere quale dà il miglior risultato?

+0

Vorrei aggiungere che nel codice sembra che l'interrogante abbia una modalità "di alta qualità" che comporta il ridimensionamento dell'immagine in più passaggi. Questa sembra una cattiva idea per lo stesso motivo per cui non si vorrebbe comprimere e decomprimere ripetutamente un campione sonoro; la qualità dell'immagine sarà in realtà peggiore e non migliore. – jprete

+0

Ciao, in realtà non ho provato tutti e sei che probabilmente dovrei. Anche se sto usando il ricampionamento bicubico che, come dici tu, "dovrebbe" essere il migliore. Anche io non uso la modalità "alta qualità" perché non funziona in quella situazione, l'API di imaging Java sembra essere soggetta a deadlock! Probabilmente qualcosa non va nel mio codice, non sono sicuro di cosa sia. –

0

Dopo alcuni esperimenti frustranti ho trovato il seguente resize evaluation e ho utilizzato l'approccio multi-pass nel mio progetto.

per farlo ho copiato il metodo getScaledInstance() nella mia classe generatore di miniature, cambiato la mia immagine leggere approccio da utilizzare ImageIO (che si restituisce un BufferedImage) e ora sono molto felice !

Ho confrontato il risultato con un ridimensionamento eseguito in Photoshop CS3 e il risultato è praticamente lo stesso.

16

Phil, non so quale soluzione alla fine si è andato con, ma le immagini di scala in Java può guardare piuttosto bene se:

  • tipi Evitare BufferedImage che non sono ben supportate dal JDK.
  • Usa incrementale ridimensionamento
  • Stick per Bicubica quando si utilizza incrementale ridimensionamento

ho fatto una congrua parte di test con questi metodi e la scala incrementale lungo con attenersi a tipi di immagine ben supportati è la chiave - - Vedo che Alexander ha detto che non ha ancora avuto fortuna con quello che è un peccato.

Ho rilasciato lo imgscalr library (Apache 2) circa 6 mesi fa per risolvere il problema di "Voglio copie di questa immagine in bella scala, FARLO ORA!" dopo aver letto qualcosa come 10 domande come questa su SO.

utilizzo standard assomiglia:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, 640); 

Il secondo argomento è la larghezza e l'altezza di delimitazione imgscalr utilizzerà per ingrandire l'immagine - mantenendo le proporzioni corrette, anche se hai superato in dimensioni non valide - ci sono molti more detailed methods, ma questo è l'utilizzo più semplice.

Il caso d'uso si vorrebbe, per esempio se Facebook immagini limitate a 800x600 pixel, sarebbe simile a questa:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600); 

che garantirà l'immagine rimane nel tipo di immagine migliore supportato e scalato con la metodo di alta qualità che Java può radunare.

Nel mio test ad alta risoluzione non ho notato alcuna discrepanza con le immagini in scala utilizzando questa libreria/questi metodi ECCETTO quando l'immagine viene inserita in un tipo di immagine supportato male dal caricatore ImageIO - ad esempio, questo accade molto con le GIF. Se li si lascia così e non li uscire da quei tipi poco supportati, si finisce per guardare davvero retinato e terribile.

La ragione di ciò è che il team Java2D ha effettivamente pipeline accelerate hardware differenti per tutti i diversi tipi di BufferedImages che il JDK può elaborare - un sottoinsieme di quei tipi di immagine che sono meno comuni ricadono tutti nello stesso software rendering della pipeline sotto le copertine in Java2D, con conseguente immagini scadenti e talvolta totalmente errate. Questo è stato un PIA da spiegare e cercare di capire che ho appena scritto quella logica direttamente nella libreria.

I due tipi più supportati sono BufferedImage.TYPE_INT_RGB e _ARGB se siete curiosi.

+0

Ciao Riyad, Grazie per aver messo il tuo impegno nel costruire questo. È fantastico e ho appena iniziato a usarlo. Ho un problema con la qualità quando provo solo a RIDIMENSIONARE un'immagine ad una larghezza e altezza specifica. Ho un'immagine che 240X320 e devo ridimensionare a 50X75 e 120X180. Ho provato entrambi separatamente usando il seguente codice (sto usando imgscalr 4.2). – WowBow

+1

image = ridimensiona (immagine, metodo.ULTRA_QUALITY, 50,75, OP_ANTIALIAS, OP_BRIGHTER); saveImage (image, ImageFormatTypes.JPEG, DESTINATION + RESIZED_IMAGE + "." + ImageFormatTypes.JPEG); Ho due problemi. # 1 è di qualità molto bassa rispetto ad altri strumenti interni che ho usato per ridimensionare l'immagine manualmente. # 2. La larghezza aumenta da 50 a 56 e da 120 a 135, il che è strano. Hai idea del motivo per cui stanno accadendo? Apprezzo il vostro aiuto. – WowBow

+2

@WowBow Ah! Sì, penso di sapere cosa sta succedendo ... Odio che imgscalr non lo sottragga ancora al dev, ma funziona su valori di pixel grezzi - quando si riprende l'immagine con ImageIO, sta usando il DEFAULT Le impostazioni dell'encoder JPG, che sono impostate al 75% di qualità, credo - devi google "qualità JPEG java ImageIO" per esempio codice su come impostare quello per dire il 95%. Il modo "economico e facile" per testare se questo è il caso, scrivere l'immagine come un PNG (formato losssles) - se sembra 'molto meglio', allora è tutto. –

5

Per ridimensionare l'immagine con qualità personalizzata, utilizzare thumbnailator.jar.

Esempio Codice http://code.google.com/p/thumbnailator/wiki/Examples

+1

Testato su org.imgscalr.Scalr e com.mortennobel.imagescaling in combinazione con la modifica del livello di compressione (http://blog.carsoncheng.ca/2011/02/how-to-change-jpeg-compression-in-java.html) e nessuno ha funzionato. thumbnailator è stato fantastico, almeno per quanto riguarda la qualità. Grazie! – daker

0

volevo più alta qualità ridimensionamento con proporzioni conservato. provato alcune cose e letto diverse voci. Perso due giorni e alla fine ho ottenuto il miglior risultato con metodo Java pianura (provato anche ImageMagick e librerie 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(); 
    } 
    } 
} 
5

I due più popolari librerie open source specializzate nel ridimensionamento delle immagini in java attualmente sono:

Additonal c'è il modo JDK con Java di Graphics2D (see this question on how to do it), che è noto per creare bad results especially with downscaling. C'è anche una Java interface to ImageMagick che verrà qui omessa poiché richiede uno strumento esterno.

qualità visiva

Ecco un confronto tra i risultati di ridimensionamento/downscaling un png 580x852-145x213. Come riferimento, viene utilizzato il ridimensionamento di Photoshop CS5 "Salva per Web". Nota: i risultati sono 1: 1 che cosa le librerie create semplicemente copiati insieme. Lo zoom non utilizza alcun filtro, solo un semplice algoritmo del vicino più prossimo.Here you can find the original image.

comparison

  1. Thumbnailator 0.4.8 con le impostazioni predefinite, senza aggiustamenti dimensionali
  2. Photoshop CS5 con l'algoritmo bicubic
  3. imgscalr 4.2 con impostazione ULTRA_QUALITY, nessun aggiustamento dimensione
  4. Graphics2D (Java 8) con rende suggerimenti VALUE_INTERPOLATION_BICUBIC, VALUE_RENDER_QUALITY, VALUE_ANTIALIAS_ON

lascio al lettore di selezionare il miglior risultato in quanto è soggettivo. Generalmente, tutti hanno una buona resa eccetto Graphics2D. Thumbnailator genera immagini più nitide molto simili all'output di Photoshop, mentre l'output di imgscalr è notevolmente più morbido. Per icone/testo, ecc., Vuoi un output più nitido, per le immagini che potresti desiderare di ottenere risultati più nitidi.

tempo di calcolo

Qui è punto di riferimento non scientifica di utilizzare questo tool e 114 immagini con dimensione da circa 96x96 fino a 2560x1440 trattandolo come immagini 425% che creano: 100%, 150%, 200%, 300% e Versioni ridimensionate del 400% (quindi operazioni di ridimensionamento 114 * 5). Tutte le librerie usano le stesse impostazioni del confronto di qualità (quindi la massima qualità possibile). I tempi non stanno ridimensionando solo l'intero processo. Fatto su un i5-2520M con RAM da 8 GB e 5 corse.

  • Thumbnailator: 7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700.3ms
  • imgscalr: 25218.5ms | 25786,6ms | 25095,7ms | 25790.4ms | 29296.3ms
  • Grafica2D: 7387.6ms | 7177.0ms | 7048.2ms | 7132.3ms | 7510.3ms

Here is the code used in this benchmark.

interessante Thumbnailator è anche il più veloce con un tempo medio di 6,9 sec seguita da Java2D 7.2 sec lasciando imgscalr dietro con scarsa 26,2 sec. Questo probabilmente non è corretto poiché imgscalr è impostato su ULTRA_QUALITY che sembra essere estremamente costoso; con l'impostazione QUALITY media in un 11,1 sec più competitivo.

+0

Le misurazioni delle prestazioni sono difficili (ho scritto alcune parole su questo in [questa risposta] (http://stackoverflow.com/a/32278737/3182664)). Tuttavia, 1 su questa e l'altra risposta – Marco13

+0

@ Marco13 è vero, ma penso che abbia avuto una dimensione di campione abbastanza grande e diverese per cogliere almeno le tendenze. – for3st

+0

Ho completato personalmente i test e ho osservato modelli simili a quelli che hai fatto (Thumbnailator ha fornito le prestazioni più veloci). Vi consiglio di scrivere la riga "imgscalr" due volte (una volta per ULTRA_QUALITY e una volta per QUALITÀ), in modo che i benchmark siano più facilmente confrontabili su una rapida occhiata. –

Problemi correlati