it-swarm.com.ru

Масштабирование BufferedImage самый быстрый и простой способ

Задание: У меня есть несколько изображений, я уменьшаю их и соединяю в одно изображение. Но у меня есть небольшая проблема с реализацией:

Конкретная проблема: Я хочу изменить размер/масштабировать BufferedImage. Метод getScaledInstance возвращает объект Image, но я не могу привести его к BufferedImage:

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

(Я не знаю, почему это ToolkitImage вместо изображения ...)

Я нашел решение:

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

Но это медленно, и я думаю, что должен быть лучший способ сделать это.  

Мне нужен BufferedImage, потому что я должен получить пиксели, чтобы соединить маленькие изображения.

Есть ли лучший (лучше/быстрее) способ сделать это?

Правка: Если я сначала приведу изображение к ToolkitImage, у него есть метод getBufferedImage (). Но это всегда возвращает ноль. Ты знаешь почему?

22
tamas.pflanzner

У объекта Graphics есть метод для рисования Image при выполнении операции изменения размера:

Graphics.drawImage(Image, int, int, int, int, ImageObserver) метод может использоваться для указания местоположения вместе с размером изображения при рисовании.

Итак, мы могли бы использовать кусок кода, подобный этому:

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

Это возьмет otherImage и нарисует его на newImage с шириной и высотой SMALL_SIZE.


Или, если вы не возражаете против использования библиотеки, Thumbnailator может сделать то же самое с этим:

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

Thumbnailator также выполнит операцию изменения размера быстрее, чем с помощью Image.getScaledInstance, одновременно выполняя операции изменения размера с более высоким качеством, чем при использовании только Graphics.drawImage.

Отказ от ответственности: я поддерживаю библиотеку Thumbnailator.

30
coobird

Я получаю это с помощью этого метода, он изменяет размер изображения и пытается сохранить пропорции:

/**
* 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

Вы также можете использовать библиотеку Java OpenCV. Это операция изменения размера быстрее, чем Imgscalr:

Тестовое задание

Изображение 5184 x 3456, масштабированное до 150 x 100 (это уменьшенная версия, поскольку исходный файл больше 2 МБ):  enter image description here

Imgscalr

Зависимость:

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

Код:

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

Результат изображения:

 enter image description here

Среднее время: 80 миллис

OpenCV

Зависимость:

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

Преобразовать BufferedImage в объект Mat (должен):

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

Код:

Imgproc.resize(matImg, resizeimage, sz);

Дополнительная конфигурация (для окон): 

Добавить opencv_Java249.dll в каталог bin вашего JDK.

Результат изображения:

 enter image description here

Среднее время: 13 миллис

Общие результаты

В тесте просто "изменяются размеры" функций, рассчитываются времена. Imgscalr изменил размер данного изображения в 80 миллис, где OpenCV выполнил ту же задачу за 13 миллис. Вы можете найти весь проект ниже, чтобы немного поиграть с ним. 

Как вы и просили простым способом, если производительность библиотеки Imgscalr вам подходит, то она смертельно проста. Потому что для использования OpenCV, как вы видите, файл библиотеки должен находиться во всех ваших средах разработки и на серверах. Также вы должны использовать объекты Mat.

Весь проект

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
Bahadir Tasdemir

Ни один из этих ответов не был достаточно быстрым для меня. Итак, я наконец запрограммировал свою собственную процедуру.

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 :

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

Это достаточно быстро для меня.

2
ceklock

Может быть, этот метод поможет:

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

Не забудьте эти строки импорта:

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

А по поводу кастинга:

Абстрактный класс Image является суперклассом всех классов, представляющих графические изображения . Мы не можем привести Image к BufferedImage, потому что каждый BufferedImage является Image, но, наоборот, неверен.

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

Это взято прямо из imgscalr в https://github.com/rkalla/imgscalr/blob/master/src/main/Java/org/imgscalr/Scalr.Java

Мое среднее время снижения качества изображения [5152x3864] составляло ~ 800 мс.

Нет зависимостей. Я ненавижу их. Иногда.

Это будет работать только с изображениями JPG. Насколько я понимаю.

Пример: 

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

Результат: 

Process took: 783ms
0
VocoJax