Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
16
Help us understand the problem. What is going on with this article?

More than 3 years have passed since last update.

@oohira

JavaでJPEG画像の保存品質を変更する

JPEG画像をリサイズして保存したいことはたまにあると思います。ImageMagickを使っていいなら話は早いのですが、どうしてもJavaだけで完結させたい場合は、ImageIOを使うことになります。

ただし、一番シンプルな実装方法ではJPEG画像の保存品質が自動的に「75」にされてしまうようなので注意が必要です(Java 8で確認)。見事にハマりました。

保存品質を指定できない方法

実装自体はシンプルだけど、保存品質を変更したい場合には NG です。

    public static byte[] resize(final byte[] src, final double scale) throws IOException {
        try (ByteArrayInputStream is = new ByteArrayInputStream(src);
             ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            BufferedImage srcImage = ImageIO.read(is);
            BufferedImage destImage = resizeImage(srcImage, scale);

            // 保存品質は「75」になる
            ImageIO.write(destImage, "jpeg", os);
            return os.toByteArray();
        }
    }

保存品質を指定できる方法

こちらが正解。保存品質 quality を 0 〜 1.0 で指定できます。1.0 が品質 100 相当になります。

    public static byte[] resize(final byte[] src, final double scale, final float quality) throws IOException {
        try (ByteArrayInputStream is = new ByteArrayInputStream(src);
             ByteArrayOutputStream os = new ByteArrayOutputStream();
             ImageOutputStream ios = ImageIO.createImageOutputStream(os)) {
            BufferedImage srcImage = ImageIO.read(is);
            BufferedImage destImage = resizeImage(srcImage, scale);

            // 保存品質はユーザー指定に従う
            ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);
            writer.setOutput(ios);
            writer.write(null, new IIOImage(destImage, null, null), param);
            writer.dispose();

            return os.toByteArray();
        }
    }

補足

先の2つの例で出てきたリサイズ用のメソッド resizeImage の実装は次のようになります。画像のリサイズもデフォルトでは画質がダメダメですが、アフィン変換するときに TYPE_BICUBIC を指定することで、画質を高めることができます。

    public static BufferedImage resizeImage(final BufferedImage image, final double scale) throws IOException {
        int width = (int) (image.getWidth() * scale);
        int height = (int) (image.getHeight() * scale);
        BufferedImage resizedImage = new BufferedImage(width, height, image.getType());

        // アフィン変換でリサイズ(画質優先)
        AffineTransform transform = AffineTransform.getScaleInstance(scale, scale);
        AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);
        op.filter(image, resizedImage);

        return resizedImage;
    }

Javaでのリサイズは画像データが大きくなるとメモリ&CPU負荷が許容できないくらい大きくなるので、使えるシーンはかなり限定的だと思います。

16
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
16
Help us understand the problem. What is going on with this article?