【问题标题】:How to improve the performance of g.drawImage() method for resizing images如何提高 g.drawImage() 方法调整图像大小的性能
【发布时间】:2011-04-27 10:23:56
【问题描述】:

我有一个应用程序,用户可以在其中上传相册中的图片,但上传的图片自然需要调整大小,因此也有可用的拇指,并且显示的图片也适合页面(例如 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 分钟。事实上,在我的电脑上,为一张图片制作 3 种不同的大小调整需要将近 40 秒。

这还不够好,我正在寻找更快的解决方案。我想知道是否有人可以告诉我 Java 中更好的方法,或者通过调用 shell 脚本、命令,无论你知道什么 hack,它必须更快,这次其他一切都不重要了。

【问题讨论】:

    标签: java image-processing


    【解决方案1】:

    您可以使用ImageMagickcreate thumbnails

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

    要从 Java 中使用它,您可以尝试 JMagick,它为 ImageMagick 提供了 Java (JNI) 接口。或者您可以直接使用Runtime.execProcessBuilder 直接调用ImageMagick 命令。

    【讨论】:

      【解决方案2】:

      您需要在调整大小的速度和最终图片的质量之间进行权衡。 你可以试试JDK的另一种缩放算法。

      图像编辑 AFAIK 最好和最灵活的工具是 ImageMagick

      Java 语言有两个接口:

      • JMagick - 是 ImageMagick 的 JNI 接口。请参阅项目Wiki 以获取更多信息。
      • im4java - 是 ImageMagick 的命令行界面。它不像 JMagick 那样基于 JNI。

      在直接使用命令行调用 ImageMagick 之前,您应该更喜欢 im4java。

      【讨论】:

        【解决方案3】:

        您真的需要使用 Image.SCALE_SMOOTH 提供的质量吗?如果没有,您可以尝试使用Image.SCALE_FAST。如果您想坚持使用 Java 提供的内容,您可能会发现此 article 很有帮助。

        【讨论】:

        • 不幸的是,SCALE_FAST 没有提供足够的质量,但是感谢您的文章,这看起来非常有用,我会尝试一下。
        【解决方案4】:

        如果你想要快速的东西,如果你可以放弃可移植性,你可能最好使用一些本机代码。

        但如果您想要纯 Java 解决方案,您也可以尝试其他一些解决方案,例如 Graphics2D.scaleImage.getScaledInstance。 我过去使用过它们,但不记得哪个性能更好或效果更好,抱歉。

        试一试,看看哪一种最适合您的需求。

        【讨论】:

          【解决方案5】:

          我正在使用类似于以下的代码来缩放图像,我删除了处理保留纵横比的部分。每张图像的性能肯定优于 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()));
          }
          

          【讨论】:

          • 一定要抛出IOException吗?
          • @user1991679:我可能从同时保存缩放图像的代码中提取了该 sn-p,如果刚刚返回图像,则 IOException 似乎是不必要的。将代码复制到您的 IDE 中而不是询问会更快:)
          • 我投了赞成票,但注意到它不适用于 png,请给我我的赞成票 :)
          【解决方案6】:

          可以通过调整渲染提示来获得一些性能改进(可能很小,可能可以忽略不计,可能以牺牲质量为代价)。例如

              g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                         RenderingHints.VALUE_INTERPOLATION_BILINEAR);
          

          【讨论】:

            【解决方案7】:

            好吧,Jacob 和我想调整图像的大小,而不是 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;
            }
            

            【讨论】:

            • 这里有一篇很好的文章详细阐述了这个解决方案:today.java.net/pub/a/today/2007/04/03/…
            • 不,说真的,Jacob 是谁?
            • Jacob 是想要调整图像大小的人!呵呵!
            • 4 年后,我们仍然不知道 Jacob 是谁
            • 雅各布被杀的时候你在哪里?
            【解决方案8】:

            我使用im4javaGraphicsMagick 以获得更快的结果(比ImageIO 更快)。

            使用了那种代码:

            public static void createFilePreview(final File originalFile, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
                runThumbnail(new ConvertCmd(), originalFile.getAbsolutePath(), originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
            }
            
            public static void createFilePreview(final InputStream originalFileInputStream, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
                final ConvertCmd cmd = new ConvertCmd();
            
                cmd.setInputProvider(new Pipe(originalFileInputStream, null));
            
                runThumbnail(cmd, "-", originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
            }
            
            private static void runThumbnail(final ConvertCmd cmd, final String originalFile, final String originalFileMimeType, final String destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
                final IMOperation operation = new IMOperation();
                // if it is a PDF, will add some optional parameters to get nicer results
                if (originalFileMimeType.startsWith("application/pdf")) {
                    operation.define("pdf:use-trimbox=true");   // as it is said here http://www.prepressure.com/pdf/basics/page_boxes "The imposition programs and workflows that I know all use the TrimBox as the basis for positioning pages on a press sheet."
                    operation.density(300, 300);    // augment the rendering from 75 (screen size) to 300 dpi in order to create big preview with good quality
                }
                operation.addImage("[0]");  // if it is a PDF or other multiple image source, will extract the first page / image, else it is ignored
                operation.autoOrient(); // Auto-orient the image if it contains some orientation information (typically JPEG with EXIF header)
                operation.thumbnail(maxWidth, maxHeight);
                operation.addImage();
            
                cmd.run(operation, originalFile, destinationPreviewFile);
            }
            

            【讨论】:

              【解决方案9】:

              这对我有用:

              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 添加了白色背景

              【讨论】:

              • 我确认这对我也很有效。这个答案的主要内容是 dpineda 不使用 Image.getScaledInstance() 已知速度非常慢。不使用该调用本质上是使它更快的原因。请参阅下面我的(皮埃尔)其他答案中的更多详细信息。
              【解决方案10】:

              在不损失图像质量的情况下,在 java 中缩放图像的最快方法是使用双线性缩放。双线性仅在您一次将图像缩放 50% 时才有效,因为它的工作方式。 以下代码来自 Chet Haase 的“肮脏的富客户”。他在书中解释了多种技术,但这一技术在质量权衡方面具有最高的性能。

              它支持所有类型的 BufferedImage,所以不用担心兼容性问题。它还可以让 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;
              }
              

              【讨论】:

                【解决方案11】:

                问题的重点是关于Java缩放图像的性能。其他答案显示了不同的方法,没有进一步评估它们。我也很好奇这个,所以我试着写一个小的性能测试。然而,可靠明智客观测试图像缩放性能是困难的。有太多的影响因素需要考虑:

                • 输入图像的大小
                • 输出图像的大小
                • 插值(即“质量”:最近邻、双线性、双三次)
                • 输入图片的BufferedImage.TYPE_*
                • 输出图片的BufferedImage.TYPE_*
                • JVM 版本和操作系统
                • 最后:实际用于执行操作的方法

                我试图涵盖那些我认为最重要的内容。设置是:

                • 输入是一张简单的“平均”照片(特别是来自维基百科的this "Image Of The Day",大小为 2560x1706 像素)

                • 测试了主要的插值类型 - 即,使用RenderingHints,其中INTERPOLATION键设置为值NEAREST_NEIGHBORBILINEARBICUBIC

                • 输入图像被转换为​​不同的类型:

                  • BufferedImage.TYPE_INT_RGB:一种常用的类型,因为它“通常”显示出最佳的性能特征

                  • BufferedImage.TYPE_3BTE_BGR:这是默认读取的类型,当只是用ImageIO读取它时

                • 目标图像大小在 10000(因此,缩放图像)和 100(因此,将图像缩小为缩略图大小)之间变化

                测试在 Win64/AMD K10 3.7 GHz 和 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:正如 Chris Campbell 在他关于 The Perils of 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 的方法似乎表现最好,唯一值得注意的例外是TYPE_INT_RGB 图像的NEAREST_NEIGHBOR 缩放,其中基于Graphics 的方法似乎始终更快。

                底线是:使用AffineTransformOp 的方法,如answer by Jörn Horstmann,似乎是为大多数 应用程序案例提供最佳性能的方法。

                【讨论】:

                  【解决方案12】:

                  老问题,但万一其他人遇到这个问题:我分析了你的代码,你最大的瓶颈是调用:

                  Image.getScaledInstance()
                  

                  众所周知,这个调用非常慢。请通过阅读本文档来说服自己:

                  The Perils of Image.getScaledInstance()

                  显着提高性能的最简单/最佳解决方案是替换该调用。您可以使用 dpineda 答案中的方法(请参阅上面的答案/代码):

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

                  我测试了他的方法,效果非常好。在我的测试中,他的实现(避免了缓慢的 Image.getScaledInstance())减少了 80% 的处理时间!

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2013-02-15
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-06-11
                    • 2015-10-25
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多