Java中的图像缩放以减小图像大小

12

我有一张尺寸为800x800、大小为170 kb的图片。我想将这张图片调整为600x600的尺寸,并减小图像大小。如何实现?


您可以查看 https://dev59.com/Q2ct5IYBdhLWcg3wnurg#11959928 - MadProgrammer
  1. "调整大小后,我希望图像的尺寸能够减小。" 当保存在内存中或序列化为PNG、JPG或GIF时呢?第一种方式会导致字节数的减少,而第二种方式通常不会。
  2. 图像是什么格式?(PNG、GIF、JPEG..)
- Andrew Thompson
如果格式是JPEG,通过增加压缩比例来减小图像字节大小可能会更有效。请参考ImageCompressionDemo获取示例源代码。 - Andrew Thompson
3个回答

22

仅仅调整图片大小并不需要使用一个完整的图像处理库。

建议的方法是使用渐进双线性缩放,如下所示(可以直接在你的代码中使用此方法):

public BufferedImage scale(BufferedImage img, int targetWidth, int targetHeight) {

    int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = img;
    BufferedImage scratchImage = null;
    Graphics2D g2 = null;

    int w = img.getWidth();
    int h = img.getHeight();

    int prevW = w;
    int prevH = h;

    do {
        if (w > targetWidth) {
            w /= 2;
            w = (w < targetWidth) ? targetWidth : w;
        }

        if (h > targetHeight) {
            h /= 2;
            h = (h < targetHeight) ? targetHeight : h;
        }

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

    return ret;

}

以下内容是从 Filthy Rich Clients 获取并经过修改和清理的代码。


根据您的评论,可以通过以下方式降低质量并对JPEG字节进行编码:

image 是BufferedImage对象。

ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageWriter writer = (ImageWriter) ImageIO.getImageWritersByFormatName("jpeg").next();

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.2f); // Change this, float between 0.0 and 1.0

writer.setOutput(ImageIO.createImageOutputStream(os));
writer.write(null, new IIOImage(image, null, null), param);
writer.dispose();

现在,由于os是一个ByteArrayOutputStream,你可以对它进行Base64编码。

String base64 = Base64.encode(os.toByteArray());

我想在调整图像大小后减小其大小(以字节为单位),以便可以通过套接字发送。DataOutputStream 表示编码的字符串(Base64)太大了。 - Madeyedexter
使用Base64来编码JPEG字节。您可以使用ImageIO实现此操作。 - LanguagesNamedAfterCofee
1
天才,这个方法奏效了,现在我意识到我甚至不需要Scalr,谢谢。但真正起作用的是使用.jpg格式而不是.png。 - user1187534
1
只是出于好奇,既然你已经有了库,为什么不使用它呢?我的意思是,为什么要使用自定义代码? - Sudarshan
Gracias, te amo - user2930137

7
你可以使用100% Java,Apache2开源的imgscalr库(一个单一的静态类),并像这样进行操作:
import org.imgscalr.Scalr.*; // static imports are awesome with the lib

... some class code ...

// This is a super-contrived method name, I just wanted to incorporate
// the details from your question accurately.
public static void resizeImageTo600x600(BufferedImage image) {
    ImageIO.write(resize(image, 600), "JPG", new File("/path/to/file.jpg"));
}

注意: 如果上面的看起来奇怪,静态导入允许我直接使用resize调用而不需要指定Scalr.resize(...)

此外,如果缩放后写出的图像质量不够好(尽管会快速地写出),您可以使用更多参数来调用resize方法,例如:

public static void resizeImageTo600x600(BufferedImage image) {
    ImageIO.write(resize(image, Method.ULTRA_QUALITY, 600), "JPG", new File("/path/to/file.jpg"));
}

如果缩小图像使其看起来不够平滑,您甚至可以将BufferedImageOp应用于结果以软化它:

public static void resizeImageTo600x600(BufferedImage image) {
    ImageIO.write(resize(image, Method.ULTRA_QUALITY, 600, Scalr.OP_ANTIALIAS), "JPG", new File("/path/to/file.jpg"));
}

您可以通过在Maven POM中添加以下依赖项来开始使用该库(imgscalr在Maven中央仓库中):

<dependency> 
  <groupId>org.imgscalr</groupId>
  <artifactId>imgscalr-lib</artifactId>
  <version>4.2</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>

我不理解这个许可证。我能在个人网站上使用它吗? - Ced
如果图片尺寸较小,它会被放大吗? - Ced
链接已经损坏,并且指向一个恶意网站。 - yurin
@yurin 谢谢你提醒我 - 已经修复! - Riyad Kalla

3

我已经查看了它。我想在我的Java程序中实现这个! - Madeyedexter
1
我在回答中加入了核心Java基本支持信息。希望这有所帮助。 - Yogendra Singh

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