Java中缩放图像的最佳方法是什么?

14

我有一个使用Java编写的Web应用程序(Spring、Hibernate/JPA、Struts2),用户可以上传图像并将它们存储在文件系统中。我想缩放这些图像,以便它们在网站上显示时具有一致的大小。哪些库或内置功能能够提供最好的结果?我将考虑以下标准来做出决定(按顺序):

  • 自由/开源(必需)
  • 易于实现
  • 结果质量
  • 性能
  • 可执行文件大小
7个回答

10

我真的很推荐你看一下imgscalr。

它发布在Apache 2许可证下,托管在GitHub上,已经在一些Web应用程序中部署过了,具有非常简单但严格记录的API,其中代码会透明地解决JDK中的两个主要图像错误问题,您只有在缩放操作后突然开始获得“黑色”图像或可怕的结果时才会注意到,可以提供Java中最好的可能结果,通过Maven和ZIP提供,并且只有一个类。

基本用法如下:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);

这是最简单的调用方式,该库将根据最佳猜测质量、保持图像比例并将结果适配到320x320的边界框中。请注意,边界框仅是使用的最大W / H,因为您的图像比例得到了尊重,因此生成的图像仍然会遵循此规则,例如320x200。

如果您想覆盖自动模式,并强制它为您提供最佳外观结果,并甚至对结果应用非常轻微的抗锯齿滤镜,以便看起来更好(尤其适用于缩略图),那么调用将如下所示:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 
                                       150, 100, Scalr.OP_ANTIALIAS);

这些只是示例,API非常广泛,涵盖了从超级简单的用例到非常专业的用例。您甚至可以传递自己的BufferedImageOps以应用于图像(并且该库会自动为您修复6年的BufferedImageOp JDK错误!)

在Java中成功缩放图像要比库做的更多,例如始终在操作它时将图像保持在最佳支持的RGB或ARGB图像类型之一。在幕后,如果任何图像操作所使用的图像类型得到了较差的支持,Java2D图像处理管道会退回到一个劣质的软件管道。

如果所有这些听起来都很麻烦,那么实际上就是...这就是为什么我编写了这个库并开源它,这样人们就可以只调整图像大小并继续他们的生活而无需担心它。

希望对您有所帮助。


4

4

你引用的例子是使用getScaledInstance()而不是AffineTransform。那么到底是哪个呢?我读过一些评论,说getScaledInstance的性能不如其他方法... - gdbj

1

我知道这是一个非常老的问题,但我使用标准的Java API得到了自己的解决方案。

import java.awt.*;
import java.awt.event.*;
import javax.imageio.*
import java.awt.image.*;

BufferedImage im, bi, bi2;
Graphics2D gfx;
int imWidth, imHeight, dstWidth, dstHeight;
int DESIRED_WIDTH = 500, DESIRED_HEIGHT = 500;

im = ImageIO.read(new File(filePath));
imWidth = im.getWidth(null);
imHeight = im.getHeight(null);

dstWidth = DESIRED_WIDTH;
dstHeight = (dstWidth * imHeight) / imWidth;

bi = new BufferedImage(dstWidth, dstHeight, im.getType());

gfx = bi.createGraphics();
gfx.drawImage(im, 0, 0, dstWidth, dstHeight, 0, 0, imWidth, imHeight, null);

bi2 = new BufferedImage(DESIRED_WIDTH, DESIRED_HEIGHT, im.getType());
gfx = bi2.createGraphics();

gfx.drawImage(bi, 0, 0, DESIRED_WIDTH, DESIRED_HEIGHT, null);

ImageIO.write(bi2, "jpg", new File(filePath));

我相信它可以被改进和适应。


1
最好的图像编辑工具是ImageMagick,并且它是开源的。
Java语言有两个接口: JMagick使用JNI接口到ImageMagick
以及 im4java是ImageMagick的命令行接口

1
发现这个更快:
public static BufferedImage getScaledInstance(final BufferedImage img, final int targetWidth, final int targetHeight,
                                              final Object hint) {
    final int type = BufferedImage.TYPE_INT_RGB;
    int drawHeight = targetHeight;
    int drawWidth = targetWidth;
    final int imageWidth = img.getWidth();
    final int imageHeight = img.getHeight();
    if ((imageWidth <= targetWidth) && (imageHeight <= targetHeight)) {
        logger.info("Image " + imageWidth + "/" + imageHeight + " within desired scale");
        return img;
    }
    final double sar = ((double) imageWidth) / ((double) imageHeight);
    if (sar != 0) {
        final double tar = ((double) targetWidth) / ((double) targetHeight);
        if ((Math.abs(tar - sar) > .001) && (tar != 0)) {
            final boolean isSoureWider = sar > (targetWidth / targetHeight);
            if (isSoureWider) {
                drawHeight = (int) (targetWidth / sar);
            }
            else {
                drawWidth = (int) (targetHeight * sar);
            }
        }
    }
    logger.info("Scaling image from " + imageWidth + "/" + imageHeight + " to " + drawWidth + "/" + drawHeight);
    final BufferedImage result = new BufferedImage(drawWidth, drawHeight, type);
    try {
        final Graphics2D g2 = result.createGraphics();
        try {
            if (hint != null) {
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            }
            g2.drawImage(img, 0, 0, drawWidth, drawHeight, null);
        }
        finally {
            g2.dispose();
        }
        return result;
    }
    finally {
        result.flush();
    }
}

0

我尝试了与标准Java 1.6相比的imgscalr,但我不能说它更好。

我尝试过的是

BufferedImage bufferedScaled = Scalr.resize(sourceImage, Method.QUALITY, 8000, height);

并且

  Image scaled = sourceImage.getScaledInstance(-1, height, Image.SCALE_SMOOTH);
  BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null),  scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
  bufferedScaled.getGraphics().drawImage(scaled, 0, 0, null);

通过我的测试,大约5分钟的时间,我得出的印象是第二个东西(纯Java 1.6)产生了更好的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接