JAIを使って画像を縮小する

Java Advanced Imaging(JAI)を使って画像を縮小する方法.
JAIは高機能画像処理ライブラリで,標準のJDKには入っていないので,別途https://jai.dev.java.net/binary-builds.htmlよりjarをダウンロードしてくる.
とりあえず良く分からなかったので,最新版の
jai-1_1_3-lib.zip(Java (no native acceleration))をダウンロード.展開してできた,2つのjarをビルドパスに追加.

んで,サンプルコードは以下のとおり.(sourceFileで指定した画像を1/3に縮小変換するサンプル)


/**
* JAIを使って画像を縮小する
* @param sourceFile 元画像ファイルパス
* @param targetFile 変換先画像ファイルパス
* @throws IOException
*/

public void resizeByJAI(String sourceFile, String targetFile) throws IOException{
// 元画像の読み込み
FileSeekableStream stream = new FileSeekableStream(sourceFile);
RenderedOp image = JAI.create("stream", stream);

// 補間方法の指定
Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_BICUBIC_2);

ParameterBlock params = new ParameterBlock();
params.addSource(image);
params.add(0.33F);//縮小率
params.add(0.33F);//縮小率
params.add(0.0F);
params.add(0.0F);
params.add(interp);

// 画像の縮小
RenderedOp resizedImage = JAI.create("scale", params);

// 変換画像の出力
JAI.create("filestore", resizedImage, targetFile, "png", null);
}

こいつを実行してみたところ,以下のようなエラーが発生するものの,縮小変換後の画像ファイルはちゃんと出力されてる.謎.

(エラーが発生したのは,上記コードの下から4行目のJAI.create("scale", params)の箇所)


Error: Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode.
Occurs in: com.sun.media.jai.mlib.MediaLibAccessor
java.lang.NoClassDefFoundError: com/sun/medialib/mlib/Image
at com.sun.media.jai.mlib.MediaLibAccessor$1.run(MediaLibAccessor.java:248)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.media.jai.mlib.MediaLibAccessor.setUseMlib(MediaLibAccessor.java:245)
at com.sun.media.jai.mlib.MediaLibAccessor.useMlib(MediaLibAccessor.java:177)
at com.sun.media.jai.mlib.MediaLibAccessor.isMediaLibCompatible(MediaLibAccessor.java:357)
at com.sun.media.jai.mlib.MediaLibAccessor.isMediaLibCompatible(MediaLibAccessor.java:315)
at com.sun.media.jai.mlib.MlibScaleRIF.create(MlibScaleRIF.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at javax.media.jai.FactoryCache.invoke(FactoryCache.java:122)
at javax.media.jai.OperationRegistry.invokeFactory(OperationRegistry.java:1674)
at javax.media.jai.ThreadSafeOperationRegistry.invokeFactory(ThreadSafeOperationRegistry.java:473)
at javax.media.jai.registry.RIFRegistry.create(RIFRegistry.java:332)
at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:819)
at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
at javax.media.jai.RenderedOp.getSampleModel(RenderedOp.java:2233)
at com.sun.media.jai.codecimpl.PNGCodec.canEncodeImage(PNGCodec.java:54)
at javax.media.jai.operator.FileStoreDescriptor.validateArguments(FileStoreDescriptor.java:176)
at javax.media.jai.JAI.createNS(JAI.java:1086)
at javax.media.jai.JAI.create(JAI.java:973)
at javax.media.jai.JAI.create(JAI.java:1668)
at ImageResizeApp.resizeByJAI(ImageResizeApp.java:76)

エラーメッセージによれば,com.sun.medialib.mlib.Imageってぇのが見つからないらしい.なるほど追加した2つのjar(jai_core.jarとjai_codec.jar)の中にはそれらしきものは入っていない.調べてみたところ,com.sun.medialib.mlib.Imageは,mlibwrapper_jai.jarの中にあるらしい.

しょーがないので,JAIのサイトから,JAIのJDK(jai-1_1_3-lib-windows-i586-jdk.exe)をダウンロードしてインストール.

%JAVA_HOME%/jre/lib/ext/ に展開された中に,mlibwrapper_jai.jarがあったので,こいつをビルドパスに追加して実行してやったところ,めでたくエラーが消えた.

というわけで,縮小は可能になったのだけど,期待していたほど画質が良くない.画質は縮小時の補間方法で決まるのでInterpolation.getInstanceの値をいろいろ変えてみたけど,もっとも画質が良いはずの「Interpolation.INTERP_BICUBIC_2」でもたいしたことない.

ScaledInstanceを使って画像を縮小する

別の方法として,java.awt.image.getScaledInstance()を使う方法があるらしい.
こっちは別途特別に何かをインストールする必要もない.

サンプルは以下のとおり.(JAIの場合と同じサンプル)


/**
* ScaledInstanceを使って画像を縮小する
* @param sourceFile 元画像ファイルパス
* @param targetFile 変換先画像ファイルパス
* @throws IOException
*/

public void resizeByScaledInstance(String sourceFile, String targetFile) throws IOException{
// 元画像の読み込み
BufferedImage sourceImage = ImageIO.read(new File(sourceFile));

// 縮小率・縮小方法を指定して縮小
Image targetImage = sourceImage.getScaledInstance(sourceImage.getWidth() / 3, -1, Image.SCALE_SMOOTH);

// Image → BufferedImageの変換
BufferedImage targetBufferedImage = new BufferedImage(targetImage.getWidth(null), targetImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g = targetBufferedImage.createGraphics();
g.drawImage(targetImage, 0, 0, null);

// 変換画像の出力
ImageIO.write(targetBufferedImage, "png", new File(targetFile));
}

こっちのほうが手間がかからない割りに,画質はJAIの場合よりも綺麗になった.

画像を縮小する

というわけでおまけとしてC#.NETの場合も.
このサンプルを作ったときのコンテキストの都合上,こちらは元画像がTIFのものを縮小した上,pngに変換して出力している.


/**
* 縮小ボタンが押されたときの処理
*/
private void button1_Click(object sender, System.EventArgs e)
{
// 変換元画像の読み込み
string srcTif = textBox1.Text;
string tifName = Path.GetFileName(srcTif);
Image img = Image.FromFile(srcTif);

// 縮小
int resizeRatio = 33; //縮小率(%)
Image img2 = new Bitmap(img.Width * resizeRatio / 100, img.Height * resizeRatio / 100);
Graphics g = Graphics.FromImage(img2);
g.InterpolationMode = InterpolationMode.Bicubic; //補間方法の指定
g.DrawImage(img,0,0,img2.Width, img2.Height);
img = img2;

// 変換画像を出力
img.Save(stCurrentDir + "\\resize" + resizeRatio + ".png",System.Drawing.Imaging.ImageFormat.Png);
}

変換時の補間方法として,HighQualityBicubic(高品質双三次補間)を指定している.
結局これが上記Javaの例と比べても,一番綺麗だった.