Javaで画像の縦横比を維持してリサイズする

アップロードされた画像を特定の高さに揃える、みたいな作業をすることになった。

サムネイル画像など、表示用であればCloudinaryimgixのような、リサイズ機能付きCDNサービスを使えばいいが、サーバー側で別途画像処理する必要があったので使えず。

Javaで実装しているので、ImageMagickをインストールして ProcessBuilder で動かそうとも思ったが、 ImageIO 使えばPure Javaで実装できるかと思ったが、縦横比を自前で計算する必要がありそうだった。

自動でリサイズする方法はないかと調べてみると、ライブラリがあったのでメモ。

縦横比を維持してリサイズするライブラリ

imgscalrというライブラリがあった。Mavenリポジトリにも登録済み

ライブラリといっても、含まれるのはstaticメソッドを持つ2クラスだけ、うち1クラスは非同期処理用なので実質1クラス。

実装内容もJava標準の画像処理系クラスのラッパーで、縦横比を維持してリサイズ後の幅と高さを自動で計算し、 java.awt.image.BufferedImage 経由でリサイズしている。

最新バージョンは4.2だが、リリースが2012年1月なので枯れている。ライセンスもApache License 2.0で使いやすいので、こちらを利用することとした。

使い方

ImageIO.read などで変換したい画像を BufferedImage として読み込み、 Scalr.resize メソッドを実行する。

引数違いで同名メソッドが複数オーバーロードされているが、 Scalr.Mode を指定することで、リサイズ方法を変更できる。

モード 概要
FIT_EXACT 指定された幅・高さにリサイズ
FIT_TO_WIDTH 指定された幅にリサイズ
FIT_TO_HEIGHT 指定された高さにリサイズ

今回のように高さを固定したい場合、 FIT_TO_HEIGHT してやれば、縦横比を保ったままリサイズされる。

File originalImageFile = Path.of(...).toFile();
int newHeight = 120;
BufferedImage originalImage = ImageIO.read(originalImageFile);
BufferedImage resizedImage = Scalr.resize(
        originalImage,
        Scalr.Method.QUALITY,
        Scalr.Mode.FIT_TO_HEIGHT,
        newHeight
);
File tempFile = Files.createTempFile("resized-", ".png").toFile();
ImageIO.write(resizedImage, "png", tempFile);

と書いてやると、画像の高さを120pxに変換できる。

Modeを FIT_EXACT にすると縦横120px、FIT_TO_WIDTH にすると幅120pxになる。

この例では数値を1つしか渡していないが、 widthheight の2つを渡すメソッドもある。幅と高さをそれぞれ指定し、Modeで FIT_EXACT を使用すると、指定した幅と高さに変換できる模様。

振り返り

地味に面倒な縦横比計算を任せられたので助かった。

余談

当初はImageMagickを使おうかなと思ったが、JavaからImageMagickを使うライブラリもある模様。

backport.net

JNIを使ってAPIを実行するJMagickと、ProcessBuilder を使うim4javaの2つ。

両方ともライセンスがLGPLなので、Javaで依存性管理ツール経由で利用するならアプリケーション側をLGPLにする必要はなさそう。

LGPLとJava - GNUプロジェクト - フリーソフトウェアファウンデーション

使うならim4javaのほうかと思う。最終リリースが2012年12月なので、最新のバージョンで使用できるかわからなかったが、こちらの記事を見ると、ImageMagick 7系でも動く模様。