また同じ事で嵌まることになるだろう・・・
半年以上前だが、Java で画像を縮小してサムネイルを作成する際に、なるべく画質を落としたくなかったので色々試したのでその記録を。
結局ブログ書くために再調査したわけだけが・・・
元となる画像(1024 x 768)は、
これから 120 x 90 のサムネイルを作成することとする。
共通処理として、
画像読み込み
public static BufferedImage loadImage(String filename) {
try (FileInputStream in = new FileInputStream(filename)) {
return ImageIO.read(in);
}
}
画像出力
// 品質 95 Jpeg
public static void outputImage(String filename, BufferedImage image) throws IOException {
JPEGImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault());
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.95f);
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
writer.setOutput(ImageIO.createImageOutputStream(new File(filename)));
writer.write(null, new IIOImage(image, null, null), param);
writer.dispose();
}
の 2 つを用意。縮小処理は、2 パターン。
パターン1 BufferedImage#getScaledInstance() を使用
public static BufferedImage reduce1(BufferedImage image, int dw, int dh) {
BufferedImage thumb = new BufferedImage(dw, dh, image.getType());
thumb.getGraphics().drawImage(image.getScaledInstance(dw, dh, Image.SCALE_AREA_AVERAGING), 0, 0, dw, dh, null);
return thumb;
}
パターン 2 Graphics2D#drawImage() を使用
public static BufferedImage reduce2(BufferedImage image, int dw, int dh) {
BufferedImage thumb = new BufferedImage(dw, dh, image.getType());
Graphics2D g2d = thumb.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
g2d.drawImage(image, 0, 0, dw, dh, null);
return thumb;
}
パターン1、パターン2 をそれぞれ呼び出し
try {
BufferedImage base = loadImage("base.jpg");
int dw = 120, dh= 90;
long start1_1 = System.currentTimeMillis();
BufferedImage reduce1_1 = reduce1(base, dw, dh);
System.out.println("reduce1_1: " + (System.currentTimeMillis() - start1_1));
outputImage("reduce1_1.jpg", reduce1_1);
long start2_1 = System.currentTimeMillis();
BufferedImage reduce2_1 = reduce2(base, dw, dh);
System.out.println("reduce2_1: " + (System.currentTimeMillis() - start2_1));
outputImage("reduce2_1.jpg", reduce2_1);
} catch (Exception e) { e.printStackTrace(); }
パターン1はぼやけた感じ、パターン2はざらついた感じ。
連続で 4 回実行し、1 回目を除いた 3 回の平均時間を計測。
速度はパターン2が早いが、今回は作成した画像をキャッシュするので速度より画質。
パターン1: 80.3ms

パターン2: 0.7ms

段階的に縮小
近傍縮小法だと、大きな画像から一気に縮小すると色情報の欠落が大きくなるので、段階的に縮小してみる。- 1024x768 -> 640x480 -> 120x90 の 2 段階
- 1024x768 -> 800x600 -> 480x360 -> 120x90 の 3 段階
int dw2_1 = 640, dh2_1 = 480;
int dw3_1 = 800, dh3_1 = 600, dw3_2 = 480, dh3_2 = 360;
long start1_2 = System.currentTimeMillis();
outputImage("reduce1_2.jpg", reduce1(reduce1(base, dw2_1, dh2_1), dw, dh));
System.out.println("reduce1_2: " + (System.currentTimeMillis() - start1_2));
long start2_2 = System.currentTimeMillis();
outputImage("reduce2_2.jpg", reduce2(reduce2(base, dw2_1, dh2_1), dw, dh));
System.out.println("reduce2_2: " + (System.currentTimeMillis() - start2_2));
long start1_3 = System.currentTimeMillis();
outputImage("reduce1_3.jpg", reduce1(reduce1(reduce1(base, dw3_1, dh3_1), dw3_2, dh3_2), dw, dh));
System.out.println("reduce1_3: " + (System.currentTimeMillis() - start1_3));
long start2_3 = System.currentTimeMillis();
outputImage("reduce2_3.jpg", reduce2(reduce2(reduce2(base, dw3_1, dh3_1), dw3_2, dh3_2), dw, dh));
System.out.println("reduce2_3: " + (System.currentTimeMillis() - start2_3));
パターン1 2段階: 141.7ms

パターン1 3段階: 215ms

パターン2 2段階: 7.3ms

パターン2 3段階: 16.3ms

続く

0 件のコメント:
コメントを投稿