Jak skalować Bufor

Po javadocach próbowałem skalować BufferedImage bez powodzenia Oto Mój kod:

BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();
Nie mogę zrozumieć, dlaczego nie działa, jakaś pomoc?
Author: Grant Miller, 2010-11-18

7 answers

AffineTransformOp oferuje dodatkową elastyczność wyboru typu interpolacji.

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

Pokazany fragment ilustruje resampling, Nie przycinanie; Ta powiązana odpowiedź dotyczy zagadnienia ; niektóre powiązane przykłady są analizowanetutaj .

 58
Author: trashgod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-15 19:02:59

Niestety wydajność getScaledInstance() jest bardzo słaba, jeśli nie problematyczna.

Alternatywnym podejściem jest utworzenie nowego obrazu BufferedImage i narysowanie skalowanej wersji oryginału na nowym.

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 wskazują nowy Rozmiar BufferedImage i muszą być prawidłowo obliczone. W przypadku skalowania czynnika:

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

EDIT : znaleziono artykuł ilustrujący problem wydajności: niebezpieczeństwa Obraz.getScaledInstance()

 32
Author: charisis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-26 19:02:40

Jak mówi @ Bozho, prawdopodobnie chcesz użyć getScaledInstance.

Aby jednak zrozumieć, jak działa grph.scale(2.0, 2.0), możesz spojrzeć na ten kod:

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

Given duke.png:
Tutaj wpisz opis obrazka

Tworzy duke_double_size.png:
Tutaj wpisz opis obrazka

 10
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 09:40:13

Using imgscalr – Java Image Scaling Library :

BufferedImage image =
     Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight);
To jest dla mnie wystarczająco szybkie.
 9
Author: ceklock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-05 20:52:32

Jeśli nie masz nic przeciwko użyciu zewnętrznej biblioteki, Thumbnailator może wykonać skalowanie BufferedImages.

Thumbnailator zajmie się obsługą przetwarzania Java 2D (np. za pomocą Graphics2D i ustawienie odpowiednich renderowania podpowiedzi) tak, aby można było użyć prostego, płynnego wywołania API do zmiany rozmiaru obrazów:

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

Chociaż Thumbnailator, jak sama nazwa wskazuje, jest nastawiony na zmniejszanie obrazów, zrobi również przyzwoitą robotę powiększając obrazy, użycie interpolacji dwuliniowej w domyślnej implementacji zmiany rozmiaru.


Uwaga: jestem opiekunem biblioteki Thumbnailator.

 4
Author: coobird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-16 10:35:05

scale(..) działa trochę inaczej. Możesz użyć bufferedImage.getScaledInstance(..)

 3
Author: Bozho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-18 15:02:45

Aby przeskalować obraz, musisz utworzyć nowy obraz i narysować go. Jednym ze sposobów jest użycie metody filter() AffineTransferOp, jak sugerowano tutaj . Pozwala to na wybór techniki interpolacji.

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

Innym sposobem jest po prostu narysowanie oryginalnego obrazu do nowego obrazu, używając operacji skalowania do skalowania. Ta metoda jest bardzo podobna, ale ilustruje również, w jaki sposób możesz narysować cokolwiek chcesz na ostatecznym obrazie. (Wstawiam pustą linię, w której dwie metody zacznij się różnić.)

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

Dodatek: Wyniki

Aby zilustrować różnice, porównałem wyniki pięciu metod poniżej. Oto, jak wyglądają wyniki, skalowane zarówno w górę, jak i w dół, wraz z danymi dotyczącymi wydajności. (Wydajność różni się w zależności od biegu, więc te liczby należy traktować tylko jako ogólne wytyczne.) Górny obraz jest oryginalny. Przeskalowuję go na dwa i pół rozmiaru.

Jak widać, AffineTransformOp.filter(), używany w scaleBilinear(), jest szybszy niż standardowy metoda rysowania Graphics2D.drawImage() w scale2(). Również interpolacja dwubiegunowa jest najwolniejsza, ale daje najlepsze rezultaty przy rozszerzaniu obrazu. (Jeśli chodzi o wydajność, należy ją porównać tylko z scaleBilinear() i scaleNearest().) Bilinear wydaje się być lepszy do zmniejszania obrazu, chociaż to trudna decyzja. A NearestNeighbor jest najszybszy, z najgorszymi wynikami. Bilinear wydaje się być najlepszym kompromisem między szybkością a jakością. Metoda Image.getScaledInstance(), wywołana metodą questionable(), wykonała się bardzo słabo i zwróciła ten sam niski jakość jak NearestNeighbor. (Numery wydajności są podane tylko dla rozszerzenia obrazu.)

Tutaj wpisz opis obrazka

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);
}
 1
Author: MiguelMunoz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-15 00:25:29