LoginSignup
22
19

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-15

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負荷が許容できないくらい大きくなるので、使えるシーンはかなり限定的だと思います。

22
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
19