- PR -

画像の縮小について

投稿者投稿内容
さくらば
大ベテラン
会議室デビュー日: 2002/11/12
投稿数: 145
投稿日時: 2005-04-04 19:40
こんにちは、さくらばです。

ヒントは画像の拡大には効くのですが、縮小にはあまり有効に働かないです。
とくに RenderingHints.VALUE_INTERPOLATION_BILINEAR とか
RenderingHints.VALUE_INTERPOLATION_BICUBIC は縮小の時には役に立ちません。

問題はジャギーですよね。
それならば、縮小する前にいちどぼかしをかけるとうまくいきます。
ぼかしをかけるには ConvolveOp クラスを使います。

添付したプログラムで行ったサンプルを示しておきます。
元の画像は 3008 x 2000 で、それを 300x200 にしています。

オリジナル: http://www5.airnet.ne.jp/sakuraba/java/temp/sample.jpg
単純に縮小: http://www5.airnet.ne.jp/sakuraba/java/temp/result1.jpg
ぼかしてから縮小: http://www5.airnet.ne.jp/sakuraba/java/temp/result2.jpg

この方法は時間がかかるという欠点がありますが、処理時間が気になら
ない場合であれば有効だと思います。

コード:
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.File;
import javax.imageio.ImageIO;

public class ScalingSample {
    public ScalingSample(String file, double scale) throws Exception {
        BufferedImage orig = ImageIO.read(new File(file));
        AffineTransformOp atOp = new AffineTransformOp(
                                           AffineTransform.getScaleInstance(scale, scale), null);

        // 単純に縮小
        BufferedImage dest1 = new BufferedImage((int)(orig.getWidth() * scale),
                                                (int)(orig.getHeight() * scale),
                                                orig.getType()); 
        atOp.filter(orig, dest1);
        ImageIO.write(dest1, "jpg", new File("result1.jpg")); 


        // 一度ぼかしてから縮小
        BufferedImage dest2 = new BufferedImage((int)(orig.getWidth() * scale),
                                                (int)(orig.getHeight() * scale),
                                                orig.getType()); 
        int size = (int)(1.0/scale);
        float[] kernelData = new float[size*size];
        for (int i = 0; i < size * size; i++) {
            kernelData[i] = 1.0f / size / size;
        }

        Kernel kernel = new Kernel(size, size, kernelData);
        ConvolveOp coOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); 
        BufferedImage inter = new BufferedImage(orig.getWidth(), orig.getHeight(), orig.getType()); 
        coOp.filter(orig, inter);
        atOp.filter(inter, dest2);
        ImageIO.write(dest2, "jpg", new File("result2.jpg")); 
    }
    
    public static void main(String[] args) throws Exception {
        String file = args[0];
        double scale = Double.parseDouble(args[1]);
        new ScalingSample(file, scale);
    }
}


おちゃ
常連さん
会議室デビュー日: 2005/01/07
投稿数: 25
投稿日時: 2005-04-05 18:55
未記入さん、ありがとうございます。
えー、そんなばかな…とも思いつつ試してみました。
得られた出力は、通常のスケーリングで縮小したものと同程度のクオリティでした。残念。

シュンさん、ありがとうございます。
そうですね、ベクター画像なら、いくら縮小拡大してもクオリティは変わりませんものね。
今回、使用しているのはjpegのラスタ画像なので、ラスタ画像をベクター画像に変換するものはないかなと探してみましたが、さすがにそんなに都合のよいものはありませんでした。

さくらばさん、ありがとうございます。
私のほうでもPhotoShopではどうやって縮小しているんだろう、と思いまして調べたところ縮小するだけでなく、ぼかし等のフィルタをかけていることがわかりました。
サンプルソースためさせていただきました。たしかに使えそうですね。
フィルタの種類や順番などに、いろいろノウハウがあるようなので
その辺調べてみます。


みなさま、いろいろ考えてくださってありがとうございます。
調査結果は随時書き込んでいきますので、よろしくおねがいします。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2005-04-05 23:30
unibon です。こんにちわ。

引用:

おちゃさんの書き込み (2005-04-05 18:55) より:
未記入さん、ありがとうございます。
えー、そんなばかな…とも思いつつ試してみました。
得られた出力は、通常のスケーリングで縮小したものと同程度のクオリティでした。残念。


ダメですか?私はキレイに見えますが。以下、試したコードです。
拡大率は、0.5倍×0.5倍=0.25倍 です(面積が 1/16 になる)。
コード:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.imageio.*;

public class Chiccha extends JPanel {

    private BufferedImage image;

    public Chiccha(BufferedImage anImage) {
        image = anImage;
        setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
    }

    public void paintComponent(Graphics g) {
        g.drawImage(image, 0, 0, null);
    }

    private static BufferedImage enlarge(BufferedImage srcImage, double scale) {
        int w = srcImage.getWidth();
        int h = srcImage.getHeight();
        Graphics2D srcGraphics = (Graphics2D) srcImage.getGraphics();
        BufferedImage dstImage = new BufferedImage((int) (w * scale), (int) (h * scale), BufferedImage.TYPE_3BYTE_BGR);
        Graphics2D dstGraphics = (Graphics2D) dstImage.getGraphics();
        Map hints = new HashMap(); 
        hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
        // hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 
        hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
        dstGraphics.setRenderingHints(hints);
        AffineTransform tx = AffineTransform.getScaleInstance(scale, scale);
        dstGraphics.drawImage(srcImage, tx, null);
        dstGraphics.dispose();
        return dstImage;
    }

    public static void main(String[] args) throws IOException {
        BufferedImage image0 = ImageIO.read(new File(args[0]));

        BufferedImage image1 = enlarge(image0, 0.5);
        BufferedImage image2 = enlarge(image1, 0.5);

        JPanel panel = new Chiccha(image2);

        final JFrame frame = new JFrame();
        frame.addWindowListener(new WindowListener() {

            public void windowActivated(WindowEvent e) {
            }
            public void windowClosed(WindowEvent e) {
            }
            public void windowClosing(WindowEvent e) {
                frame.dispose();
            }
            public void windowDeactivated(WindowEvent e) {
            }
            public void windowDeiconified(WindowEvent e) {
            }
            public void windowIconified(WindowEvent e) {
            }
            public void windowOpened(WindowEvent e) {
            }
        });
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}


未記入じゃけんど
ベテラン
会議室デビュー日: 2004/11/09
投稿数: 65
お住まい・勤務地: Osaka City
投稿日時: 2005-04-07 12:13
ImageFilterでの縮小はだめでしょうか?

ImageFilter imgfilter = new AreaAveragingScaleFilter(500,500);
W_Image = createImage(new FilteredImageSource(readImage.getSource(), imgfilter));
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2005-04-08 00:28
unibon です。こんにちわ。

引用:

未記入さんさんの書き込み (2005-04-07 12:13) より:
ImageFilterでの縮小はだめでしょうか?

ImageFilter imgfilter = new AreaAveragingScaleFilter(500,500);
W_Image = createImage(new FilteredImageSource(readImage.getSource(), imgfilter));



おお、こんな便利なクラスがあるのですね。動作原理は私には良く分かりませんが、でも動かしてみると、1/4ほどの縮小(面積で1/16ほど)でも確かにキレイな画像です。勉強になりました。

スキルアップ/キャリアアップ(JOB@IT)