Java で画像のリサイズ
ちょっと Java で画像のリサイズをする必要が出てきて調べたので、簡単にまとめです。
何種類かの方法が存在しているようなのですが、2つめで画質的に満足してしまったので、それ以上は追求していません。
以下のサンプルプログラムは、
java ImageResize [入力画像ファイル名] [出力画像ファイル名]
として起動すると、幅640ピクセルに縮小した画像ファイルを作り出す Java プログラムで試しています。
元の画像ファイルは、2592 x 1944 の WindowsXP Home Edition パッケージの写真です。
・・・何でもよかったのですが、直線的な写真なので画質がわかりやすいのです。
ちなみに、Intel Mac の BootCamp 用に購入したやつ。
まず試したのが、AffineTransform です。
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Iterator;
import java.util.Locale;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
public class ImageResize {
public static void main(String[] args) throws Exception {
File orgImageFile = new File(args[0]);
BufferedImage orgImage = ImageIO.read(orgImageFile);
int width = 640;
int height = (int)(((double)width / orgImage.getWidth()) * (double)orgImage.getHeight());
BufferedImage resizeImage = new BufferedImage(width, height, orgImage.getType());
// ここからリサイズ処理
AffineTransformOp ato = null;
ato = new AffineTransformOp(
AffineTransform.getScaleInstance((double)width / orgImage.getWidth(),
(double) height / orgImage.getHeight()),
null);
ato.filter(orgImage, resizeImage);
// ここまでリサイズ処理
ImageOutputStream imageStream = ImageIO.createImageOutputStream(new File(args[1]));
ImageWriter writer = null;
Iterator it = (Iterator) ImageIO.getImageWritersByFormatName("jpg");
writer = (ImageWriter) it.next();
writer.setOutput(imageStream);
JPEGImageWriteParam jpgWriter = new JPEGImageWriteParam(Locale.getDefault());
jpgWriter.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriter.setCompressionQuality(1f);
writer.write(null, new IIOImage(resizeImage, null, null), jpgWriter);
imageStream.flush();
writer.dispose();
imageStream.close();
}
}
結果がこの画像です。
シャギーがひどいです。
次に、AffineTransform にヒントを与えてみます。
以下、リサイズ処理のところだけ抜粋。
// ここからリサイズ処理
AffineTransformOp ato = null;
ato = new AffineTransformOp(
AffineTransform.getScaleInstance((double)width / orgImage.getWidth(),
(double) height / orgImage.getHeight()),
AffineTransformOp.TYPE_BILINEAR);
ato.filter(orgImage, resizeImage);
// ここまでリサイズ処理
結果がこの画像です。
なんら変わりません。
調べてみると、縮小する場合は与えるヒントが TYPE_BILINEAR でも、TYPE_BICUBIC でもほとんど効かないとの話が。
与えるヒントには、RenderingHints クラスを使って多種多様なヒントを与えることができるのでそれもやってみましたが、どのように指定してもほとんど綺麗になりません。
さらに調べてみると、縮小には 面積平均法 を使った AreaAveragingScaleFilter を使うと綺麗に縮小できるとの話が。
BufferedImage#getScaledInstance() が、AreaAveragingScaleFilter を使っているらしいとのことで、早速これでやってみます。
// ここからリサイズ処理
resizeImage.getGraphics().drawImage(
orgImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING),
0, 0, width, height, null);
// ここまでリサイズ処理
結果がこの画像です。
AffineTransform でやった時と比べると劇的に綺麗になっています。
その代わり、劇的に処理速度も遅くなってしまいました。
色々な画像を縮小してみると、どうも元画像のサイズや画像の内容によって処理速度が変わってくるようです。
でも、ここまで綺麗さが違うと速度が遅いのもやむを得ないよなぁと思ってしまいます。
しかし、画像処理は奥が深いです・・・。
付け焼き刃では理解できません。