Как улучшить производительность метода g.drawImage () для изменения размера изображений - PullRequest
47 голосов
/ 19 октября 2010

У меня есть приложение, в котором пользователи могут загружать изображения в альбомы, но естественно размер загружаемых изображений должен быть изменен, чтобы были также доступны большие пальцы и показанные изображения также помещались на странице (например, 800x600).То, как я делаю изменение размера, выглядит следующим образом:

Image scaledImage = img.getScaledInstance((int)width, (int)height, Image.SCALE_SMOOTH);
BufferedImage imageBuff = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_RGB);
Graphics g = imageBuff.createGraphics();
g.drawImage(scaledImage, 0, 0, new Color(0,0,0), null);
g.dispose();

И все работает нормально.Моя единственная проблема заключается в том, что метод g.drawImage() кажется очень медленным, и я просто не могу себе представить, что пользователь будет достаточно терпеливым, чтобы дождаться загрузки 20 снимков 20 * 10 секунд ~ 3 минуты.На самом деле, на моем компьютере требуется почти 40 секунд для создания трех разных размеров для одного изображения.

Этого недостаточно, и я ищу более быстрое решение.Мне интересно, может ли кто-нибудь рассказать мне о лучшем в Java ИЛИ, вызвав сценарий оболочки, команду, что бы вы ни взломали, это должно быть быстрее, на этот раз все остальное не имеет значения.

Ответы [ 12 ]

26 голосов
/ 19 октября 2010

Я использую код, подобный следующему, для масштабирования изображений, я удалил часть, которая касается сохранения соотношения сторон. Производительность была определенно лучше, чем 10 секунд на изображение, но я не помню точных цифр. Чтобы сохранить лучшее качество при уменьшении масштаба, вы должны масштабировать в несколько этапов, если исходное изображение более чем в два раза превышает размер требуемого эскиза, каждый шаг должен масштабировать предыдущее изображение примерно до половины его размера.

public static BufferedImage getScaledImage(BufferedImage image, int width, int height) throws IOException {
    int imageWidth  = image.getWidth();
    int imageHeight = image.getHeight();

    double scaleX = (double)width/imageWidth;
    double scaleY = (double)height/imageHeight;
    AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY);
    AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR);

    return bilinearScaleOp.filter(
        image,
        new BufferedImage(width, height, image.getType()));
}
14 голосов
/ 19 октября 2010

Вам действительно нужно качество, которое обеспечивается с помощью Image.SCALE_SMOOTH? Если вы этого не сделаете, вы можете попробовать использовать Image.SCALE_FAST . статья может оказаться полезной, если вы хотите использовать что-то, предоставленное Java.

13 голосов
/ 07 июля 2012

Ну, мы с Джейкобом хотели изменить размер изображения, а не BufferedImage. Таким образом, мы получили этот код:

/**
 * we want the x and o to be resized when the JFrame is resized
 *
 * @param originalImage an x or an o. Use cross or oh fields.
 *
 * @param biggerWidth
 * @param biggerHeight
 */
private Image resizeToBig(Image originalImage, int biggerWidth, int biggerHeight) {
    int type = BufferedImage.TYPE_INT_ARGB;


    BufferedImage resizedImage = new BufferedImage(biggerWidth, biggerHeight, type);
    Graphics2D g = resizedImage.createGraphics();

    g.setComposite(AlphaComposite.Src);
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    g.drawImage(originalImage, 0, 0, biggerWidth, biggerHeight, this);
    g.dispose();


    return resizedImage;
}
11 голосов
/ 28 августа 2015

Суть вопроса касалась производительности масштабирования изображений в Java .Другие ответы показали разные подходы, не оценивая их дальше.Мне тоже было любопытно, поэтому я попытался написать небольшой тест производительности.Однако тестирование производительности масштабирования изображения надежно , разумно и объективно затруднительно.Слишком много факторов влияния, которые необходимо учитывать:

  • Размер входного изображения
  • Размер выходного изображения
  • Интерполяция (т. Е.«качество»: ближайший сосед, билинейный, бикубический)
  • BufferedImage.TYPE_* входного изображения
  • BufferedImage.TYPE_* выходного изображения
  • Версия JVM и работающаяsystem
  • Наконец: метод , который фактически используется для выполнения операции.

Я попытался охватить те из них, которые считал наиболее важными.Установки были:

  • На входе простая, "средняя" фотография (в частности, это "Изображение дня" из Википедии, размером 2560x1706пикселей)

  • Тестируются основные типы интерполяции, а именно с использованием RenderingHints, где для клавиши INTERPOLATION установлены значения NEAREST_NEIGHBOR, BILINEAR и BICUBIC

  • Входное изображение было преобразовано в различные типы:

    • BufferedImage.TYPE_INT_RGB: тип, который обычно используется, как это «обычно» показываетнаилучшие характеристики производительности

    • BufferedImage.TYPE_3BTE_BGR: это тип, с которым он считывается по умолчанию, когда просто читается с помощью ImageIO

  • Размер целевого изображения варьировался от ширины 10000 (таким образом, масштабируя изображение up ) до 100 (таким образом, масштабируя изображение до размера миниатюры)

Тесты проводились на Win64 / AMD K10 с 3,7 ГГц и JDK 1.8u31 с -Xmx4000m -server.

. Испытанные методы:

Код тестов показан здесь:

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;

import javax.imageio.ImageIO;
import javax.swing.JLabel;

public class ImageScalingPerformance
{
    private static int blackHole = 0;

    public static void main(String[] args) throws IOException
    {
        // Image with size 2560 x 1706, from https://upload.wikimedia.org/
        //   wikipedia/commons/4/41/Pitta_moluccensis_-_Kaeng_Krachan.jpg
        BufferedImage image = ImageIO.read(
            new File("Pitta_moluccensis_-_Kaeng_Krachan.jpg"));

        int types[] =
        {
            BufferedImage.TYPE_3BYTE_BGR,
            BufferedImage.TYPE_INT_RGB,
        };
        Object interpolationValues[] =
        {
            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR,
            RenderingHints.VALUE_INTERPOLATION_BICUBIC,
        };
        int widths[] =
        {
            10000, 5000, 2500, 1000, 500, 100
        };


        System.out.printf("%10s%22s%6s%18s%10s\n",
            "Image type", "Interpolation", "Size", "Method", "Duration (ms)");

        for (int type : types)
        {
            BufferedImage currentImage = convert(image, type);
            for (Object interpolationValue : interpolationValues)
            {
                for (int width : widths)
                {
                    List<Supplier<Image>> tests = 
                        createTests(currentImage, interpolationValue, width);

                    for (Supplier<Image> test : tests)
                    {
                        double durationMs = computeMs(test);

                        System.out.printf("%10s%22s%6s%18s%10s\n",
                            stringForBufferedImageType(type),
                            stringForInterpolationValue(interpolationValue),
                            String.valueOf(width), 
                            String.valueOf(test),
                            String.format(Locale.ENGLISH, "%6.3f", durationMs));
                    }
                }
            }
        }
        System.out.println(blackHole);
    }

    private static List<Supplier<Image>> createTests(
        BufferedImage image, Object interpolationValue, int width)
    {
        RenderingHints renderingHints = new RenderingHints(null);
        renderingHints.put(
            RenderingHints.KEY_INTERPOLATION, 
            interpolationValue);
        double scale = (double) width / image.getWidth();
        int height = (int)(scale * image.getHeight());

        Supplier<Image> s0 = new Supplier<Image>()
        {
            @Override
            public BufferedImage get()
            {
                return scaleWithAffineTransformOp(
                    image, width, height, renderingHints);
            }

            @Override
            public String toString()
            {
                return "AffineTransformOp";
            }
        };

        Supplier<Image> s1 = new Supplier<Image>()
        {
            @Override
            public Image get()
            {
                return scaleWithGraphics(
                    image, width, height, renderingHints);
            }

            @Override
            public String toString()
            {
                return "Graphics";
            }
        };

        Supplier<Image> s2 = new Supplier<Image>()
        {
            @Override
            public Image get()
            {
                return scaleWithGetScaledInstance(
                    image, width, height, renderingHints);
            }

            @Override
            public String toString()
            {
                return "GetScaledInstance";
            }
        };

        List<Supplier<Image>> tests = new ArrayList<Supplier<Image>>();
        tests.add(s0);
        tests.add(s1);
        tests.add(s2);
        return tests;
    }

    private static double computeMs(Supplier<Image> supplier)
    {
        int runs = 5;
        long before = System.nanoTime();
        for (int i=0; i<runs; i++)
        {
            Image image0 = supplier.get();
            blackHole += image0.hashCode();
        }
        long after = System.nanoTime();
        double durationMs = (after-before) / 1e6 / runs;
        return durationMs;
    }

    private static BufferedImage convert(BufferedImage image, int type)
    {
        BufferedImage newImage = new BufferedImage(
            image.getWidth(), image.getHeight(), type);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }        

    private static BufferedImage scaleWithAffineTransformOp(
        BufferedImage image, int w, int h,
        RenderingHints renderingHints)
    {
        BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
        double scaleX = (double) w / image.getWidth();
        double scaleY = (double) h / image.getHeight();
        AffineTransform affineTransform = 
            AffineTransform.getScaleInstance(scaleX, scaleY);
        AffineTransformOp affineTransformOp = new AffineTransformOp(
            affineTransform, renderingHints);
        return affineTransformOp.filter(
            image, scaledImage);
    }

    private static BufferedImage scaleWithGraphics(
        BufferedImage image, int w, int h,
        RenderingHints renderingHints) 
    {
        BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
        Graphics2D g = scaledImage.createGraphics();
        g.setRenderingHints(renderingHints);
        g.drawImage(image, 0, 0, w, h, null);
        g.dispose();
        return scaledImage;
    }

    private static Image scaleWithGetScaledInstance(
        BufferedImage image, int w, int h,
        RenderingHints renderingHints)
    {
        int hint = Image.SCALE_REPLICATE;
        if (renderingHints.get(RenderingHints.KEY_ALPHA_INTERPOLATION) != 
            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
        {
            hint = Image.SCALE_AREA_AVERAGING;
        }
        Image scaledImage = image.getScaledInstance(w, h, hint);
        MediaTracker mediaTracker = new MediaTracker(new JLabel());
        mediaTracker.addImage(scaledImage, 0);
        try
        {
            mediaTracker.waitForAll();
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
        }
        return scaledImage;
    }

    private static String stringForBufferedImageType(int type)
    {
        switch (type)
        {
            case BufferedImage.TYPE_INT_RGB : return "INT_RGB";
            case BufferedImage.TYPE_INT_ARGB : return "INT_ARGB";
            case BufferedImage.TYPE_INT_ARGB_PRE : return "INT_ARGB_PRE";
            case BufferedImage.TYPE_INT_BGR : return "INT_BGR";
            case BufferedImage.TYPE_3BYTE_BGR : return "3BYTE_BGR";
            case BufferedImage.TYPE_4BYTE_ABGR : return "4BYTE_ABGR";
            case BufferedImage.TYPE_4BYTE_ABGR_PRE : return "4BYTE_ABGR_PRE";
            case BufferedImage.TYPE_USHORT_565_RGB : return "USHORT_565_RGB";
            case BufferedImage.TYPE_USHORT_555_RGB : return "USHORT_555_RGB";
            case BufferedImage.TYPE_BYTE_GRAY : return "BYTE_GRAY";
            case BufferedImage.TYPE_USHORT_GRAY : return "USHORT_GRAY";
            case BufferedImage.TYPE_BYTE_BINARY : return "BYTE_BINARY";
            case BufferedImage.TYPE_BYTE_INDEXED : return "BYTE_INDEXED";
        }
        return "CUSTOM";
    }

    private static String stringForInterpolationValue(Object value)
    {
        if (value == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
        {
            return "NEAREST/REPLICATE";
        }
        if (value == RenderingHints.VALUE_INTERPOLATION_BILINEAR)
        {
            return "BILINEAR/AREA_AVG";
        }
        if (value == RenderingHints.VALUE_INTERPOLATION_BICUBIC)
        {
            return "BICUBIC/AREA_AVG";
        }
        return "(unknown)";
    }


}

Во-первых, относительно getScaledInstance: как указал Крис Кэмпбелл в своем(знаменитая) статья о Опасности Image.getScaledInstance () (о которой уже говорилось в других ответах), метод Image#getScaledInstance несколько сломан и имеет крайне плохую производительность для большинства конфигураций.Кроме того, он имеет недостаток, заключающийся в отсутствии такого тонкого контроля над типом интерполяции. Это следует учитывать при следующем сравнении производительности : качество результирующих изображений может отличаться, что здесь не рассматривается.Например, метод "усреднения по области" getScaledInstance не дает хорошего качества изображения, когда размер изображения увеличен .

(Самый серьезный недостаток Image#getScaledInstance - это ИМХО, что он дает только Image, а не BufferedImage, но если изображение предполагается только нарисовать в Graphics, это может быть не важно)

Я просто дам вывод результатов программы здесь для справки, некоторые детали будут приведены ниже:

Image type         Interpolation  Size            MethodDuration (ms)
 3BYTE_BGR     NEAREST/REPLICATE 10000 AffineTransformOp   197.287
 3BYTE_BGR     NEAREST/REPLICATE 10000          Graphics   184.427
 3BYTE_BGR     NEAREST/REPLICATE 10000 GetScaledInstance  1869.759
 3BYTE_BGR     NEAREST/REPLICATE  5000 AffineTransformOp    38.354
 3BYTE_BGR     NEAREST/REPLICATE  5000          Graphics    40.220
 3BYTE_BGR     NEAREST/REPLICATE  5000 GetScaledInstance  1088.448
 3BYTE_BGR     NEAREST/REPLICATE  2500 AffineTransformOp    10.153
 3BYTE_BGR     NEAREST/REPLICATE  2500          Graphics     9.461
 3BYTE_BGR     NEAREST/REPLICATE  2500 GetScaledInstance   613.030
 3BYTE_BGR     NEAREST/REPLICATE  1000 AffineTransformOp     2.137
 3BYTE_BGR     NEAREST/REPLICATE  1000          Graphics     1.956
 3BYTE_BGR     NEAREST/REPLICATE  1000 GetScaledInstance   464.989
 3BYTE_BGR     NEAREST/REPLICATE   500 AffineTransformOp     0.861
 3BYTE_BGR     NEAREST/REPLICATE   500          Graphics     0.750
 3BYTE_BGR     NEAREST/REPLICATE   500 GetScaledInstance   407.751
 3BYTE_BGR     NEAREST/REPLICATE   100 AffineTransformOp     0.206
 3BYTE_BGR     NEAREST/REPLICATE   100          Graphics     0.153
 3BYTE_BGR     NEAREST/REPLICATE   100 GetScaledInstance   385.863
 3BYTE_BGR     BILINEAR/AREA_AVG 10000 AffineTransformOp   830.097
 3BYTE_BGR     BILINEAR/AREA_AVG 10000          Graphics  1501.290
 3BYTE_BGR     BILINEAR/AREA_AVG 10000 GetScaledInstance  1627.934
 3BYTE_BGR     BILINEAR/AREA_AVG  5000 AffineTransformOp   207.816
 3BYTE_BGR     BILINEAR/AREA_AVG  5000          Graphics   376.789
 3BYTE_BGR     BILINEAR/AREA_AVG  5000 GetScaledInstance  1063.942
 3BYTE_BGR     BILINEAR/AREA_AVG  2500 AffineTransformOp    52.362
 3BYTE_BGR     BILINEAR/AREA_AVG  2500          Graphics    95.041
 3BYTE_BGR     BILINEAR/AREA_AVG  2500 GetScaledInstance   612.660
 3BYTE_BGR     BILINEAR/AREA_AVG  1000 AffineTransformOp     9.121
 3BYTE_BGR     BILINEAR/AREA_AVG  1000          Graphics    15.749
 3BYTE_BGR     BILINEAR/AREA_AVG  1000 GetScaledInstance   452.578
 3BYTE_BGR     BILINEAR/AREA_AVG   500 AffineTransformOp     2.593
 3BYTE_BGR     BILINEAR/AREA_AVG   500          Graphics     4.237
 3BYTE_BGR     BILINEAR/AREA_AVG   500 GetScaledInstance   407.661
 3BYTE_BGR     BILINEAR/AREA_AVG   100 AffineTransformOp     0.275
 3BYTE_BGR     BILINEAR/AREA_AVG   100          Graphics     0.297
 3BYTE_BGR     BILINEAR/AREA_AVG   100 GetScaledInstance   381.835
 3BYTE_BGR      BICUBIC/AREA_AVG 10000 AffineTransformOp  3015.943
 3BYTE_BGR      BICUBIC/AREA_AVG 10000          Graphics  5431.703
 3BYTE_BGR      BICUBIC/AREA_AVG 10000 GetScaledInstance  1654.424
 3BYTE_BGR      BICUBIC/AREA_AVG  5000 AffineTransformOp   756.136
 3BYTE_BGR      BICUBIC/AREA_AVG  5000          Graphics  1359.288
 3BYTE_BGR      BICUBIC/AREA_AVG  5000 GetScaledInstance  1063.467
 3BYTE_BGR      BICUBIC/AREA_AVG  2500 AffineTransformOp   189.953
 3BYTE_BGR      BICUBIC/AREA_AVG  2500          Graphics   341.039
 3BYTE_BGR      BICUBIC/AREA_AVG  2500 GetScaledInstance   615.807
 3BYTE_BGR      BICUBIC/AREA_AVG  1000 AffineTransformOp    31.351
 3BYTE_BGR      BICUBIC/AREA_AVG  1000          Graphics    55.914
 3BYTE_BGR      BICUBIC/AREA_AVG  1000 GetScaledInstance   451.808
 3BYTE_BGR      BICUBIC/AREA_AVG   500 AffineTransformOp     8.422
 3BYTE_BGR      BICUBIC/AREA_AVG   500          Graphics    15.028
 3BYTE_BGR      BICUBIC/AREA_AVG   500 GetScaledInstance   408.626
 3BYTE_BGR      BICUBIC/AREA_AVG   100 AffineTransformOp     0.703
 3BYTE_BGR      BICUBIC/AREA_AVG   100          Graphics     0.825
 3BYTE_BGR      BICUBIC/AREA_AVG   100 GetScaledInstance   382.610
   INT_RGB     NEAREST/REPLICATE 10000 AffineTransformOp   330.445
   INT_RGB     NEAREST/REPLICATE 10000          Graphics   114.656
   INT_RGB     NEAREST/REPLICATE 10000 GetScaledInstance  2784.542
   INT_RGB     NEAREST/REPLICATE  5000 AffineTransformOp    83.081
   INT_RGB     NEAREST/REPLICATE  5000          Graphics    29.148
   INT_RGB     NEAREST/REPLICATE  5000 GetScaledInstance  1117.136
   INT_RGB     NEAREST/REPLICATE  2500 AffineTransformOp    22.296
   INT_RGB     NEAREST/REPLICATE  2500          Graphics     7.735
   INT_RGB     NEAREST/REPLICATE  2500 GetScaledInstance   436.779
   INT_RGB     NEAREST/REPLICATE  1000 AffineTransformOp     3.859
   INT_RGB     NEAREST/REPLICATE  1000          Graphics     2.542
   INT_RGB     NEAREST/REPLICATE  1000 GetScaledInstance   205.863
   INT_RGB     NEAREST/REPLICATE   500 AffineTransformOp     1.413
   INT_RGB     NEAREST/REPLICATE   500          Graphics     0.963
   INT_RGB     NEAREST/REPLICATE   500 GetScaledInstance   156.537
   INT_RGB     NEAREST/REPLICATE   100 AffineTransformOp     0.160
   INT_RGB     NEAREST/REPLICATE   100          Graphics     0.074
   INT_RGB     NEAREST/REPLICATE   100 GetScaledInstance   126.159
   INT_RGB     BILINEAR/AREA_AVG 10000 AffineTransformOp  1019.438
   INT_RGB     BILINEAR/AREA_AVG 10000          Graphics  1230.621
   INT_RGB     BILINEAR/AREA_AVG 10000 GetScaledInstance  2721.918
   INT_RGB     BILINEAR/AREA_AVG  5000 AffineTransformOp   254.616
   INT_RGB     BILINEAR/AREA_AVG  5000          Graphics   308.374
   INT_RGB     BILINEAR/AREA_AVG  5000 GetScaledInstance  1269.898
   INT_RGB     BILINEAR/AREA_AVG  2500 AffineTransformOp    68.137
   INT_RGB     BILINEAR/AREA_AVG  2500          Graphics    80.163
   INT_RGB     BILINEAR/AREA_AVG  2500 GetScaledInstance   444.968
   INT_RGB     BILINEAR/AREA_AVG  1000 AffineTransformOp    13.093
   INT_RGB     BILINEAR/AREA_AVG  1000          Graphics    15.396
   INT_RGB     BILINEAR/AREA_AVG  1000 GetScaledInstance   211.929
   INT_RGB     BILINEAR/AREA_AVG   500 AffineTransformOp     3.238
   INT_RGB     BILINEAR/AREA_AVG   500          Graphics     3.689
   INT_RGB     BILINEAR/AREA_AVG   500 GetScaledInstance   159.688
   INT_RGB     BILINEAR/AREA_AVG   100 AffineTransformOp     0.329
   INT_RGB     BILINEAR/AREA_AVG   100          Graphics     0.277
   INT_RGB     BILINEAR/AREA_AVG   100 GetScaledInstance   127.905
   INT_RGB      BICUBIC/AREA_AVG 10000 AffineTransformOp  4211.287
   INT_RGB      BICUBIC/AREA_AVG 10000          Graphics  4712.587
   INT_RGB      BICUBIC/AREA_AVG 10000 GetScaledInstance  2830.749
   INT_RGB      BICUBIC/AREA_AVG  5000 AffineTransformOp  1069.088
   INT_RGB      BICUBIC/AREA_AVG  5000          Graphics  1182.285
   INT_RGB      BICUBIC/AREA_AVG  5000 GetScaledInstance  1155.663
   INT_RGB      BICUBIC/AREA_AVG  2500 AffineTransformOp   263.003
   INT_RGB      BICUBIC/AREA_AVG  2500          Graphics   297.663
   INT_RGB      BICUBIC/AREA_AVG  2500 GetScaledInstance   444.497
   INT_RGB      BICUBIC/AREA_AVG  1000 AffineTransformOp    42.841
   INT_RGB      BICUBIC/AREA_AVG  1000          Graphics    48.605
   INT_RGB      BICUBIC/AREA_AVG  1000 GetScaledInstance   209.261
   INT_RGB      BICUBIC/AREA_AVG   500 AffineTransformOp    11.004
   INT_RGB      BICUBIC/AREA_AVG   500          Graphics    12.407
   INT_RGB      BICUBIC/AREA_AVG   500 GetScaledInstance   156.794
   INT_RGB      BICUBIC/AREA_AVG   100 AffineTransformOp     0.817
   INT_RGB      BICUBIC/AREA_AVG   100          Graphics     0.790
   INT_RGB      BICUBIC/AREA_AVG   100 GetScaledInstance   128.700

Это можно увидетьчто почти во всех случаях getScaledInstance работает плохо по сравнению с другими подходами (и те немногие случаи, когда кажется, что он работает лучше, можно объяснить меньшим качеством при увеличении).

На основе AffineTransformOpПохоже, что этот подход работает лучше всего в среднем, с единственным заметным исключением - масштабированием NEAREST_NEIGHBOR TYPE_INT_RGB изображений, где подход на основе Graphics, по-видимому, неизменно быстрее.

Суть в следующем: метод, использующий AffineTransformOp, как в ответе Йорна Хорстманна , кажется, тот, который предлагает лучшую производительность для большинство случаи применения.

11 голосов
/ 19 октября 2010

Вы можете использовать ImageMagick до создавать миниатюры .

convert -define jpeg:size=500x180  hatching_orig.jpg  -auto-orient \
        -thumbnail 250x90   -unsharp 0x.5  thumbnail.gif

Чтобы использовать его из Java, вы можете попробовать JMagick , который предоставляет интерфейс Java (JNI) для ImageMagick.Или вы можете просто вызывать команды ImageMagick напрямую, используя Runtime.exec или ProcessBuilder.

6 голосов
/ 10 сентября 2014

это работает для меня:

private BufferedImage getScaledImage(BufferedImage src, int w, int h){
    int original_width = src.getWidth();
    int original_height = src.getHeight();
    int bound_width = w;
    int bound_height = h;
    int new_width = original_width;
    int new_height = original_height;

    // first check if we need to scale width
    if (original_width > bound_width) {
        //scale width to fit
        new_width = bound_width;
        //scale height to maintain aspect ratio
        new_height = (new_width * original_height) / original_width;
    }

    // then check if we need to scale even with the new height
    if (new_height > bound_height) {
        //scale height to fit instead
        new_height = bound_height;
        //scale width to maintain aspect ratio
        new_width = (new_height * original_width) / original_height;
    }

    BufferedImage resizedImg = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = resizedImg.createGraphics();
    g2.setBackground(Color.WHITE);
    g2.clearRect(0,0,new_width, new_height);
    g2.drawImage(src, 0, 0, new_width, new_height, null);
    g2.dispose();
    return resizedImg;
}

Также я добавил белый фон для PNG

5 голосов
/ 28 августа 2015

Самый быстрый способ масштабировать изображение в Java без потери качества изображения - это использовать билинейное масштабирование. Билинейный режим хорош только в том случае, если вы масштабируете изображение на 50% за раз из-за того, как оно работает. Следующий код взят из «Грязных богатых клиентов» Чета Хаазе. В книге он объясняет несколько приемов, но этот способ обеспечивает наилучшее соотношение цены и качества.

Он поддерживает все типы BufferedImages, поэтому не беспокойтесь о совместимости. Это также позволяет аппаратному обеспечению java2D ускорить ваш образ, потому что вычисления выполняются Java2D. Не волнуйтесь, если вы не понимаете эту последнюю часть. Самое главное, что это самый быстрый способ сделать это.

public static BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, boolean progressiveBilinear)
{
    int type = (img.getTransparency() == Transparency.OPAQUE) ? 
            BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = (BufferedImage) img;
    BufferedImage scratchImage = null;
    Graphics2D g2 = null;
    int w, h;
    int prevW = ret.getWidth();
    int prevH = ret.getHeight();
    if(progressiveBilinear) {
        w = img.getWidth();
        h = img.getHeight();
    }else{
        w = targetWidth;
        h = targetHeight;
    }
    do {
        if (progressiveBilinear && w > targetWidth) {
            w /= 2;
            if(w < targetWidth) {
                w = targetWidth;
            }
        }

        if (progressiveBilinear && h > targetHeight) {
            h /= 2;
            if (h < targetHeight) {
                h = targetHeight;
            }
        }

        if(scratchImage == null) {
            scratchImage = new BufferedImage(w, h, type);
            g2 = scratchImage.createGraphics();
        }
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
        prevW = w;
        prevH = h;
        ret = scratchImage;
    } while (w != targetWidth || h != targetHeight);

    if (g2 != null) {
        g2.dispose();
    }

    if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
        scratchImage = new BufferedImage(targetWidth, targetHeight, type);
        g2 = scratchImage.createGraphics();
        g2.drawImage(ret, 0, 0, null);
        g2.dispose();
        ret = scratchImage;
    }
    System.out.println("ret is "+ret);
    return ret;
}
3 голосов
/ 19 октября 2010

Вы всегда будете иметь компромисс между скоростью изменения размера и качеством получаемого изображения. Вы можете попробовать другой алгоритм масштабирования JDK.

Лучший и самый гибкий инструмент для редактирования изображений AFAIK - ImageMagick .

Существует два интерфейса для языка Java:

  • JMagick - это интерфейс JNI для ImageMagick. См. Проекты Wiki для получения дополнительной информации.
  • im4java - это интерфейс командной строки для ImageMagick. Это не, как JMagick, основанный на JNI.

Вы должны предпочесть im4java перед непосредственным использованием командной строки для вызова ImageMagick.

2 голосов
/ 14 июня 2016

Старый вопрос, но на случай, если кто-то другой решит эту проблему: я профилировал ваш код, и вашим самым большим узким местом является звонок:

Image.getScaledInstance()

Этот звонок, как известно, ужасно медленный.Пожалуйста, убедитесь, прочитав этот документ:

Опасности Image.getScaledInstance ()

Самое простое / лучшее решение для существенного улучшения производительности - заменить этот вызов,Вы можете использовать метод из ответа Дпинеды (см. Его ответ / код выше):

private BufferedImage getScaledImage(BufferedImage src, int w, int h){

Я проверил его метод, и он работает очень хорошо.В моем тестировании его реализация (которая позволяет избежать медленного Image.getScaledInstance ()) сократила 80% времени обработки!

1 голос
/ 19 октября 2010

Некоторое улучшение производительности (возможно, незначительное, возможно незначительное, возможно за счет качества) может быть достигнуто путем настройки подсказок рендеринга.Например,

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...