Domanda

A seguito delle javadocs, ho cercato di scalare un BufferedImage senza successo qui è il mio codice:

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?

È stato utile?

Soluzione

AffineTransformOp offre la aggiuntivo la 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);

Il frammento mostrato illustra ricampionamento , non ritaglio ; risponde a questa correlata risposta problema ; alcuni esempi relativi sono esaminati qui .

Altri suggerimenti

Purtroppo le prestazioni di getScaledInstance () è molto scarsa, se non problematico.

L'approccio alternativo è quello di creare un nuovo BufferedImage ed e disegnare una versione in scala dell'originale sul nuovo.

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 correttamente calcolato. In caso di ridimensionamento fattore:

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

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

Come @Bozho dice, probabilmente avrete bisogno di 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"));
    }
}

Data duke.png :
entrare descrizione dell'immagine qui

produce duke_double_size.png :
entrare descrizione dell'immagine qui

imgscalr - Java Immagine Scaling Biblioteca :

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

Questo è abbastanza veloce per me.

Se non ti dispiace utilizzando una libreria esterna, Thumbnailator in grado di eseguire il ridimensionamento di BufferedImage s.

Thumbnailator si occuperà di gestire l'elaborazione del Java 2D (come l'utilizzo di < a href = "https://docs.oracle.com/javase/8/docs/api/java/awt/Graphics2D.html" rel = "noreferrer"> Graphics2D e l'impostazione appropriata suggerimenti di rendering ) in modo che una semplice chiamata API fluente può essere utilizzato per ridimensionare le immagini:

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

Anche se Thumbnailator, come suggerisce il nome, è orientata verso immagini contrazione, si farà un lavoro decente l'ingrandimento delle immagini, nonché, mediante interpolazione bilineare nella sua implementazione predefinita Resizer.


. Disclaimer: Io sono il manutentore della biblioteca Thumbnailator

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

Per ridimensionare l'immagine, è necessario creare una nuova immagine e disegnare in essa. Un modo è quello di utilizzare il metodo filter() di un AffineTransferOp, come suggerito qui . Ciò consente 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 è quello di disegnare semplicemente l'immagine originale nella nuova immagine, con un'operazione di ridimensionamento per fare la scala. Questo metodo è molto simile, ma illustra anche come si può disegnare tutto quello che vuoi 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;
}

Addendum: Risultati

Per illustrare le differenze, ho confrontato i risultati dei cinque metodi indicati. Ecco ciò che i risultati assomigliano, in scala sia su e giù, insieme ai dati relativi alle prestazioni. (Le prestazioni variano da un'esecuzione a quella successiva, in modo da prendere questi numeri solo come indicazioni di massima.) L'immagine in alto è l'originale. Mi scala che doppio-size e half-size.

Come si può vedere, AffineTransformOp.filter(), utilizzato in scaleBilinear(), è più veloce rispetto al metodo di disegno standard Graphics2D.drawImage() in scale2(). Anche interpolazione bicubico è il più lento, ma dà i migliori risultati quando l'espansione dell'immagine. (Per le prestazioni, dovrebbe essere paragonato solo con scaleBilinear() e scaleNearest().) bilineare sembra essere migliore per il restringimento dell'immagine, anche se è una chiamata dura. E NearestNeighbor è il più veloce, con i risultati peggiori. Bilineare sembra essere il miglior compromesso tra velocità e qualità. Il Image.getScaledInstance(), chiamato nel metodo questionable(), eseguita molto poco, e restituito la stessa qualità partire da NearestNeighbor. (I numeri delle prestazioni vengono dati solo per espandere l'immagine.)

entrare descrizione dell'immagine qui

public static BufferedImage scaleBilinear(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_BILINEAR;
    return scale(before, scale, interpolation);
}

public static BufferedImage scaleBicubic(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_BICUBIC;
    return scale(before, scale, interpolation);
}

public static BufferedImage scaleNearest(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
    return scale(before, scale, interpolation);
}

@NotNull
private static 
BufferedImage scale(final BufferedImage before, final double scale, final int type) {
    int w = before.getWidth();
    int h = before.getHeight();
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, before.getType());
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type);
    scaleOp.filter(before, after);
    return after;
}

/**
 * This is a more generic solution. It produces the same result, but it shows how you 
 * can draw anything you want into the newly created image. It's slower 
 * than scaleBilinear().
 * @param before The original image
 * @param scale The scale factor
 * @return A scaled version of the original image
 */
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, before.getType());
    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 just drawing
    // a scaled version of the original image. This is slower than 
    // calling scaleOp.filter().
    g2.drawImage(before, scaleOp, 0, 0);
    g2.dispose();
    return after;
}

/**
 * I call this one "questionable" because it uses the questionable getScaledImage() 
 * method. This method is no longer favored because it's slow, as my tests confirm.
 * @param before The original image
 * @param scale The scale factor
 * @return The scaled image.
 */
private static Image questionable(final BufferedImage before, double scale) {
    int w2 = (int) (before.getWidth() * scale);
    int h2 = (int) (before.getHeight() * scale);
    return before.getScaledInstance(w2, h2, Image.SCALE_FAST);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top