wake-up-neo.com

Skalieren Sie ein BufferedImage auf die schnellste und einfachste Weise

Die Aufgabe: Ich habe einige Bilder, ich verkleinere sie und füge sie zu einem Bild zusammen. Ich habe aber ein kleines Problem mit der Implementierung:

Das konkrete Problem: Ich möchte ein BufferedImage skalieren. Die getScaledInstance-Methode gibt ein Image-Objekt zurück, kann jedoch nicht in BufferedImage umgewandelt werden:

Exception in thread "main" Java.lang.ClassCastException: Sun.awt.image.ToolkitImage cannot be cast to Java.awt.image.BufferedImage

(Ich weiß nicht, warum es ein ToolkitImage anstelle eines Bildes ist ...)

Ich habe eine Lösung gefunden:

Image tmp = bi.getScaledInstance(SMALL_SIZE, SMALL_SIZE, BufferedImage.SCALE_FAST);
BufferedImage buffered = new BufferedImage(SMALL_SIZE,SMALL_SIZE,BufferedImage.TYPE_INT_RGB);
buffered.getGraphics().drawImage(tmp, 0, 0, null);

Aber es ist langsam und ich denke, es sollte einen besseren Weg geben, es zu tun.  

Ich brauche das BufferedImage, weil ich die Pixel bekommen muss, um die kleinen Bilder zu verbinden.

Gibt es eine bessere (schönere/schnellere) Möglichkeit, dies zu tun?

EDIT: Wenn ich das Image zuerst in ToolkitImage umwandle, gibt es eine getBufferedImage () - Methode. Aber es gibt immer null zurück. Weißt du, warum?

22
tamas.pflanzner

Das Graphics-Objekt verfügt über eine Methode zum Zeichnen einer Image, während gleichzeitig eine Größenänderungsoperation ausgeführt wird:

Mit der Methode Graphics.drawImage(Image, int, int, int, int, ImageObserver) kann beim Zeichnen die Position zusammen mit der Größe des Bildes angegeben werden.

So könnten wir einen Code wie diesen verwenden:

BufferedImage otherImage = // .. created somehow
BufferedImage newImage = new BufferedImage(SMALL_SIZE, SMALL_SIZE, BufferedImage.TYPE_INT_RGB);

Graphics g = newImage.createGraphics();
g.drawImage(otherImage, 0, 0, SMALL_SIZE, SMALL_SIZE, null);
g.dispose();

Dies nimmt otherImage und zeichnet es auf die newImage mit der Breite und Höhe von SMALL_SIZE.


Oder, wenn es Ihnen nichts ausmacht, eine Bibliothek zu verwenden, könnte Thumbnailator dasselbe mit diesem Ziel erreichen:

BufferedImage newImage = Thumbnails.of(otherImage)
                             .size(SMALL_SIZE, SMALL_SIZE)
                             .asBufferedImage();

Thumbnailator führt auch die Größenänderung schneller als mit Image.getScaledInstance durch, während auch Größenänderungsoperationen mit höherer Qualität als nur mit Graphics.drawImage ausgeführt werden.

Disclaimer: Ich bin der Verwalter der Thumbnailator-Bibliothek.

30
coobird

Ich bekomme es mit dieser Methode, es ändert die Größe des Bildes und versucht, die Proportionen zu erhalten:

/**
* Resizes an image using a Graphics2D object backed by a BufferedImage.
* @param srcImg - source image to scale
* @param w - desired width
* @param h - desired height
* @return - the new resized image
*/
private BufferedImage getScaledImage(BufferedImage src, int w, int h){
    int finalw = w;
    int finalh = h;
    double factor = 1.0d;
    if(src.getWidth() > src.getHeight()){
        factor = ((double)src.getHeight()/(double)src.getWidth());
        finalh = (int)(finalw * factor);                
    }else{
        factor = ((double)src.getWidth()/(double)src.getHeight());
        finalw = (int)(finalh * factor);
    }   

    BufferedImage resizedImg = new BufferedImage(finalw, finalh, BufferedImage.TRANSLUCENT);
    Graphics2D g2 = resizedImg.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g2.drawImage(src, 0, 0, finalw, finalh, null);
    g2.dispose();
    return resizedImg;
}
8
digolloco

Sie können auch die OpenCV Java-Bibliothek verwenden. Die Größenänderung ist schneller als bei Imgscalr:

Prüfung

Bild 5184 x 3456, skaliert auf 150 x 100 (dies ist die kleinere Version, da die Originaldatei größer als 2 MB ist):  enter image description here

Imgscalr

Abhängigkeit:

<dependency>
    <groupId>org.imgscalr</groupId>
    <artifactId>imgscalr-lib</artifactId>
    <version>4.2</version>
</dependency>

Code:

BufferedImage thumbnail = Scalr.resize(img,
            Scalr.Method.SPEED,
            Scalr.Mode.AUTOMATIC,
            150,
            100);

Ergebnisbild:

 enter image description here

Durchschnittliche Zeit: 80 Millis

OpenCV

Abhängigkeit:

<dependency>
    <groupId>nu.pattern</groupId>
    <artifactId>opencv</artifactId>
    <version>2.4.9-4</version>
</dependency>

BufferedImage in Mat-Objekt konvertieren (müssen):

BufferedImage img = ImageIO.read(image); // load image
byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer())
            .getData();
Mat matImg = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3);
matImg.put(0, 0, pixels);

Code:

Imgproc.resize(matImg, resizeimage, sz);

Zusätzliche Konfiguration (für Windows): 

Fügen Sie opencv_Java249.dll zum bin-Verzeichnis Ihres JDK hinzu.

Ergebnisbild:

 enter image description here

Durchschnittliche Zeit: 13 Millis

Gesamtergebnis

Im Test werden einfach "Größenänderung" -Funktionen berechnet. Imgscalr hat das angegebene Bild in 80 Millis geändert, wobei OpenCV in 13 Millis dieselbe Aufgabe ausgeführt hat. Sie können das gesamte Projekt hier unten finden, um etwas davon zu spielen. 

Wie Sie auch auf einfache Weise gefragt haben, ob die Leistung der Imgscalr-Bibliothek für Sie gut ist, ist tödlich. Damit Sie OpenCV so verwenden können, wie Sie es sehen, muss sich eine Bibliotheksdatei in allen Entwicklungsumgebungen und Servern befinden. Sie müssen auch Mat-Objekte verwenden.

Ganzes Projekt

Pom.xml:

<project xmlns="http://maven.Apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.Apache.org/POM/4.0.0 http://maven.Apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.btasdemir</groupId>
    <artifactId>testapp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>testapp</name>
    <url>http://maven.Apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.imgscalr</groupId>
            <artifactId>imgscalr-lib</artifactId>
            <version>4.2</version>
        </dependency>

        <dependency>
            <groupId>nu.pattern</groupId>
            <artifactId>opencv</artifactId>
            <version>2.4.9-4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin> 
                <groupId>org.bytedeco</groupId> 
                <artifactId>javacpp</artifactId> 
                <version>0.9</version> 
            </plugin>
        </plugins>
    </build>

</project>

App.Java:

package com.btasdemir.testapp;

import Java.awt.image.BufferedImage;
import Java.awt.image.DataBufferByte;
import Java.io.File;
import Java.io.IOException;

import javax.imageio.ImageIO;

import org.imgscalr.Scalr;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;


/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws IOException
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

        File image = new File("C:\\your_dir\\test.jpg");
        BufferedImage img = ImageIO.read(image); // load image
        long startTime = System.currentTimeMillis();//imgscalr------------------------------------------------------
        //resize to 150 pixels max
        BufferedImage thumbnail = Scalr.resize(img,
                Scalr.Method.SPEED,
                Scalr.Mode.AUTOMATIC,
                150,
                100);
//      BufferedImage thumbnail = Scalr.resize(img,
//                Scalr.Method.SPEED,
//                Scalr.Mode.AUTOMATIC,
//                150,
//                100,
//                Scalr.OP_ANTIALIAS);
        System.out.println(calculateElapsedTime(startTime));//END-imgscalr------------------------------------------------------
        File outputfile = new File("C:\\your_dir\\imgscalr_result.jpg");
        ImageIO.write(thumbnail, "jpg", outputfile);


        img = ImageIO.read(image); // load image
        byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer())
                .getData();
        Mat matImg = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3);
        matImg.put(0, 0, pixels);

        Mat resizeimage = new Mat();
        Size sz = new Size(150, 100);
        startTime = System.currentTimeMillis();//opencv------------------------------------------------------

        Imgproc.resize(matImg, resizeimage, sz);
//      Imgproc.resize(matImg, resizeimage, sz, 0.5, 0.5, Imgproc.INTER_CUBIC);

        System.out.println(calculateElapsedTime(startTime));//END-opencv------------------------------------------------------
        Highgui.imwrite("C:\\your_dir\\opencv_result.jpg", resizeimage);


    }

    protected static long calculateElapsedTime(long startTime) {
        long stopTime = System.currentTimeMillis();
        long elapsedTime = stopTime - startTime;
        return elapsedTime;
    }
}
7

Keine dieser Antworten war für mich schnell genug. Also habe ich endlich meine eigene Prozedur programmiert.

static BufferedImage scale(BufferedImage src, int w, int h)
{
  BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
  int x, y;
  int ww = src.getWidth();
  int hh = src.getHeight();
  for (x = 0; x < w; x++) {
    for (y = 0; y < h; y++) {
      int col = src.getRGB(x * ww / w, y * hh / h);
      img.setRGB(x, y, col);
    }
  }
  return img;
}
5
Victor

imgscalr - Java Image Scaling Library :

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

Das ist für mich schnell genug.

2
ceklock

Vielleicht hilft diese Methode:

public  BufferedImage resizeImage(BufferedImage image, int width, int height) {
         int type=0;
        type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType();
        BufferedImage resizedImage = new BufferedImage(width, height,type);
        Graphics2D g = resizedImage.createGraphics();
        g.drawImage(image, 0, 0, width, height, null);
        g.dispose();
        return resizedImage;
     }

Vergiss nicht diese "Import" -Zeilen:

import Java.awt.Graphics2D;
import Java.awt.image.BufferedImage;

Und über das Casting:

Die abstrakte Klasse Imageist die Oberklasse aller Klassen, die grafische Bilder darstellen Wir können Image nicht in BufferedImage umwandeln, da jede BufferedImageImage ist, aber umgekehrt nicht.

Image im = new BufferedImage(width, height, imageType);//this is true

BufferedImage img = new Image(){//.....}; //this is wrong
1
Azad
public static double[] reduceQuality(int quality, int width, int height) {
    if(quality >= 1 && quality <= 100) {
        double[] dims = new double[2];
        dims[0] = width * (quality/100.0);
        dims[1] = height * (quality/100.0);
        return dims;
    } else if(quality > 100) {
        return new double[] { width, height };
    } else {
        return new double[] { 1, 1 };
    }
}

public static byte[] resizeImage(byte[] data, int width, int height) throws Exception {
    BufferedImage bi = ImageIO.read(new ByteArrayInputStream(data));
    BufferedImage bo = resizeImage(bi, width, height);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ImageIO.write(bo, "jpg", bos);
    bos.close();
    return bos.toByteArray();
}

private static BufferedImage resizeImage(BufferedImage buf, int width, int height) {
    final BufferedImage bufImage = new BufferedImage(width, height, 
            (buf.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
            : BufferedImage.TYPE_INT_ARGB));
    final Graphics2D g2 = bufImage.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    g2.drawImage(buf, 0, 0, width, height, null);
    g2.dispose();
    return bufImage;
}

Dies wird direkt von imgscalr unter https://github.com/rkalla/imgscalr/blob/master/src/main/Java/org/imgscalr/Scalr.Java übernommen

Meine durchschnittliche Zeit, die die Qualität eines Bildes [5152x3864] verringert, betrug ~ 800ms.

Keine Abhängigkeiten Ich hasse sie. Manchmal.

DAS WIRD NUR MIT JPG-BILDERN ARBEITEN. Soweit es mich betrifft.

Beispiel: 

byte[] of = Files.readAllBytes(Paths.get("/home/user/Pictures/8mbsample.jpg"));
    double[] wh = ImageUtil.reduceQuality(2, 6600, 4950);

    long start = System.currentTimeMillis();
    byte[] sof = ImageUtil.resizeImage(of, (int)wh[0], (int)wh[1]);
    long end = System.currentTimeMillis();

    if(!Files.exists(Paths.get("/home/user/Pictures/8mbsample_scaled.jpg"))) {
        Files.createFile(Paths.get("/home/user/Pictures/8mbsample_scaled.jpg"), Util.getFullPermissions());
    }

    FileOutputStream fos = new FileOutputStream("/home/user/Pictures/8mbsample_scaled.jpg");
    fos.write(sof); fos.close();

    System.out.println("Process took: " + (end-start) + "ms");

Ausgabe: 

Process took: 783ms
0
VocoJax