2010-11-18 14 views
38

Seguendo le javadocs, ho cercato di scalare un BufferedImage senza successo qui è il mio codice:Come scalare un BufferedImage

BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded); 
Graphics2D grph = image.createGraphics(); 
grph.scale(2.0, 2.0); 
grph.dispose(); 

non riesco a capire il motivo per cui non funziona, qualsiasi aiuto?

+1

Un'esercitazione eccellente: http://www.glyphic.com/transform/applet/1intro.html –

+0

partire da questa scrittura, la la risposta più popolare è la risposta sbagliata. Ridimensiona l'immagine, ma restituisce un'immagine della stessa dimensione, con 3/4 dell'immagine mancante. È la risposta data da trashgod. È vicino, ma ha un piccolo bug. – MiguelMunoz

risposta

55

AffineTransformOp offre l'ulteriore flessibilità di scegliere il tipo di interpolazione.

BufferedImage before = getBufferedImage(encoded); 
int w = before.getWidth(); 
int h = before.getHeight(); 
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 
AffineTransform at = new AffineTransform(); 
at.scale(2.0, 2.0); 
AffineTransformOp scaleOp = 
    new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); 
after = scaleOp.filter(before, after); 

Alcuni esempi correlate sono esaminate here e here.

+1

È davvero necessario allocare tutto la memoria di 'after', quando hai una frase come:' after = ... '? –

+3

@Martijn: Dipende da quale 'ColorModel' si desidera in [' filter() '] (http://download.oracle.com/javase/6/docs/api/java/awt/image/AffineTransformOp.html# filtro% 28java.awt.image.BufferedImage,% 20java.awt.image.BufferedImage% 29). Restituisce un riferimento, quindi non c'è memoria extra. – trashgod

+0

C'è un modo per farlo con la larghezza e l'altezza desiderate al posto del fattore di scala? –

3

scale(..) funziona un po 'diverso. È possibile utilizzare bufferedImage.getScaledInstance(..)

+0

Ho provato in questo modo, ma getScaledInstance restituisce ToolkitImage, e per il mio scopo non mi va bene. grazie a –

+2

puoi convertirlo in una BufferedImage copiando il suo raster in una nuova BufferedImage. Serach per 'convertire l'immagine in bufferedimage' – Bozho

+0

'Converti immagine in BufferedImage'> http://stackoverflow.com/a/13605411/439171 –

10

Come dice @Bozho, probabilmente si desidera utilizzare getScaledInstance.

Per capire come funziona grph.scale(2.0, 2.0) tuttavia, si potrebbe avere uno sguardo a questo codice:

import java.awt.*; 
import java.awt.image.BufferedImage; 
import java.io.*; 

import javax.imageio.ImageIO; 
import javax.swing.ImageIcon; 


class Main { 
    public static void main(String[] args) throws IOException { 

     final int SCALE = 2; 

     Image img = new ImageIcon("duke.png").getImage(); 

     BufferedImage bi = new BufferedImage(SCALE * img.getWidth(null), 
              SCALE * img.getHeight(null), 
              BufferedImage.TYPE_INT_ARGB); 

     Graphics2D grph = (Graphics2D) bi.getGraphics(); 
     grph.scale(SCALE, SCALE); 

     // everything drawn with grph from now on will get scaled. 

     grph.drawImage(img, 0, 0, null); 
     grph.dispose(); 

     ImageIO.write(bi, "png", new File("duke_double_size.png")); 
    } 
} 

Dato duke.png:
enter image description here

produce duke_double_size.png:
enter image description here

+0

Ho provato questo codice, ma non ho ottenuto il risultato mostrato. Il risultato che ho ottenuto è stato molto più fortemente alias. Se esegui lo zoom sulla prima immagine del tuo browser, fino a quando ne ottieni uno della stessa dimensione del secondo, avrai un'idea migliore di cosa produce questo codice. (Ho provato a inserire l'immagine risultante in questo commento, ma non ha funzionato. Immagino che le immagini non siano consentite nei commenti.) – MiguelMunoz

+0

Puoi provare 'grph.setRenderingHint (RenderingHints.KEY_INTERPOLATION, interpolazione);' dove 'interpolazione ' è 'RenderingHints.VALUE_INTERPOLATION _...' ad esempio 'VALUE_INTERPOLATION_BICUBIC'? – aioobe

32

Sfortunatamente la prestazione di getScaled Instance() è molto povero se non problematico.

L'approccio alternativo consiste nel creare una nuova immagine bufferizzata e nel disegnare una versione ridimensionata dell'originale su quella nuova.

BufferedImage resized = new BufferedImage(newWidth, newHeight, original.getType()); 
Graphics2D g = resized.createGraphics(); 
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
    RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
g.drawImage(original, 0, 0, newWidth, newHeight, 0, 0, original.getWidth(), 
    original.getHeight(), null); 
g.dispose(); 

newWidth, newHeight indicano la nuova dimensione BufferedImage e devono essere calcolati correttamente. In caso di scalatura fattore:

int newWidth = new Double(original.getWidth() * widthFactor).intValue(); 
int newHeight = new Double(original.getHeight() * heightFactor).intValue(); 

EDIT: trovato l'articolo che illustra il problema di prestazioni: The Perils of Image.getScaledInstance()

+0

Penso che getScaledInstance() sia più veloce al giorno d'oggi, almeno se si dispone di una scheda grafica decente, grazie alla pipeline di rendering Java2D ottimizzata. – ceklock

+0

FYI vedere ** [qui] (http://docs.oracle.com/javase/tutorial/2d/advanced/quality.html) ** altri possibili valori per 'RenderingHints.KEY_INTERPOLATION' – rustyx

9

Utilizzando imgscalr – Java Image Scaling Library:

BufferedImage image = 
    Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight); 

Questo è abbastanza veloce per me.

+1

Concordato, questa è la soluzione migliore ed evita tutti i problemi di trasparenza, traduzioni sbagliate, colori sbagliati, ecc. Quando si utilizza affinetransform e vari altri metodi. – zyamys

+1

Fantastico! La prima soluzione che ho visto su questo thread mi ha fatto ottenere ciò di cui avevo bisogno. – Shadoninja

+2

https://mvnrepository.com/artifact/org.imgscalr/imgscalr-lib/4.2 – kahonmlg

4

Se non ti dispiace usare una libreria esterna, Thumbnailator può eseguire il ridimensionamento di BufferedImage s.

Thumbnailator si occuperà di gestire l'elaborazione Java 2D (come l'utilizzo Graphics2D e impostazione appropriata rendering hints) in modo che una semplice chiamata API fluente può essere utilizzato per ridimensionare immagini:

BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage(); 

Sebbene Thumbnailator, come il suo nome implica, è orientato verso il restringimento delle immagini, farà un lavoro decente allargando anche le immagini, usando l'interpolazione bilineare nella sua implementazione predefinita del resettatore.


Disclaimer: io sono il manutentore della biblioteca Thumbnailator.

+0

Questa è una libreria eccellente! Le miniature sono semplicemente incredibili rispetto a Graphics2D – Artem

1

Per ridimensionare un'immagine, è necessario creare una nuova immagine e disegnarla. Un modo è utilizzare il metodo filter() di un AffineTransferOp, come suggerito here. Questo ti permette di scegliere la tecnica di interpolazione.

private static BufferedImage scale1(BufferedImage before, double scale) { 
    int w = before.getWidth(); 
    int h = before.getHeight(); 
    // Create a new image of the proper size 
    int w2 = (int) (w * scale); 
    int h2 = (int) (h * scale); 
    BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB); 
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); 
    AffineTransformOp scaleOp 
     = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); 

    scaleOp.filter(before, after); 
    return after; 
} 

Un altro modo è semplicemente di disegnare l'immagine originale nella nuova immagine, utilizzando un'operazione di ridimensionamento per eseguire il ridimensionamento. Questo metodo è molto simile, ma illustra anche come è possibile disegnare tutto ciò che si desidera nell'immagine finale. (Ho messo in una riga vuota in cui i due metodi iniziano a differire.)

private static BufferedImage scale2(BufferedImage before, double scale) { 
    int w = before.getWidth(); 
    int h = before.getHeight(); 
    // Create a new image of the proper size 
    int w2 = (int) (w * scale); 
    int h2 = (int) (h * scale); 
    BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB); 
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); 
    AffineTransformOp scaleOp 
     = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); 

    Graphics2D g2 = (Graphics2D) after.getGraphics(); 
    // Here, you may draw anything you want into the new image, but we're 
    // drawing a scaled version of the original image. 
    g2.drawImage(before, scaleOp, 0, 0); 
    g2.dispose(); 
    return after; 
}