web-dev-qa-db-ja.com

BufferedImageをスケーリングする方法

Javadocに従って、BufferedImageをスケーリングしようとしましたが成功しませんでした:

BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();

なぜ機能していないのか理解できません、助けてください?

40
Thiago Diniz

AffineTransformOp は、補間タイプを選択する追加の柔軟性を提供します。

BufferedImage before = getBufferedImage(encoded);
int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(2.0, 2.0);
AffineTransformOp scaleOp = 
   new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);

示されているフラグメントは、 resampling ではなく、 cropping ;この関連 answerissue ;に対処します。いくつかの関連する例が調べられます here

61
trashgod

残念ながら、getScaledInstance()のパフォーマンスは問題がなければ非常に劣っています。

別の方法は、新しいBufferedImageを作成し、新しいものにオリジナルのスケーリングされたバージョンを描画することです。

BufferedImage resized = new BufferedImage(newWidth, newHeight, original.getType());
Graphics2D g = resized.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(original, 0, 0, newWidth, newHeight, 0, 0, original.getWidth(),
    original.getHeight(), null);
g.dispose();

newWidth、newHeightは、新しいBufferedImageサイズを示し、適切に計算する必要があります。因子スケーリングの場合:

int newWidth = new Double(original.getWidth() * widthFactor).intValue();
int newHeight = new Double(original.getHeight() * heightFactor).intValue();

[〜#〜] edit [〜#〜]:パフォーマンスの問題を説明する記事を見つけました: The Perils of Image.getScaledInstance()

33
charisis

@Bozhoが言うように、おそらくgetScaledInstanceを使いたいでしょう。

ただし、grph.scale(2.0, 2.0)がどのように機能するかを理解するには、次のコードを参照してください。

import Java.awt.*;
import Java.awt.image.BufferedImage;
import Java.io.*;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;


class Main {
    public static void main(String[] args) throws IOException {

        final int SCALE = 2;

        Image img = new ImageIcon("duke.png").getImage();

        BufferedImage bi = new BufferedImage(SCALE * img.getWidth(null),
                                             SCALE * img.getHeight(null),
                                             BufferedImage.TYPE_INT_ARGB);

        Graphics2D grph = (Graphics2D) bi.getGraphics();
        grph.scale(SCALE, SCALE);

        // everything drawn with grph from now on will get scaled.

        grph.drawImage(img, 0, 0, null);
        grph.dispose();

        ImageIO.write(bi, "png", new File("duke_double_size.png"));
    }
}

duke.png
enter image description here

duke_double_size.png:を生成します
enter image description here

12
aioobe

imgscalr – Java Image Scaling Library :の使用

BufferedImage image =
     Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight);

これは私にとって十分速いです。

9
ceklock

外部ライブラリを使用してもかまわない場合、 ThumbnailatorBufferedImage sのスケーリングを実行できます。

Thumbnailatorは、 Java 2D 処理( Graphics2D および適切な設定 レンダリングヒント )を使用して、シンプルな流れるようなAPI呼び出しを使用して画像のサイズを変更できます。

BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();

Thumbnailatorは、その名前が示すように、画像の縮小を対象としていますが、デフォルトのリサイザー実装で双線形補間を使用して、画像を拡大することもできます。


免責事項:私は Thumbnailator ライブラリのメンテナーです。

6
coobird

画像を拡大縮小するには、新しい画像を作成して描画する必要があります。 1つの方法は、 here のように、AffineTransferOpfilter()メソッドを使用することです。これにより、補間手法を選択できます。

_private static BufferedImage scale1(BufferedImage before, double scale) {
    int w = before.getWidth();
    int h = before.getHeight();
    // Create a new image of the proper size
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp 
        = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);

    scaleOp.filter(before, after);
    return after;
}
_

別の方法は、スケーリング操作を使用してスケーリングを実行し、元の画像を新しい画像に単純に描画することです。この方法は非常に似ていますが、最終画像に必要なものを描画する方法も示しています。 (2つの方法が異なる開始する空白行に入れます。)

_private static BufferedImage scale2(BufferedImage before, double scale) {
    int w = before.getWidth();
    int h = before.getHeight();
    // Create a new image of the proper size
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp 
        = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);

    Graphics2D g2 = (Graphics2D) after.getGraphics();
    // Here, you may draw anything you want into the new image, but we're
    // drawing a scaled version of the original image.
    g2.drawImage(before, scaleOp, 0, 0);
    g2.dispose();
    return after;
}
_

補遺:結果

違いを説明するために、以下の5つの方法の結果を比較しました。パフォーマンスデータとともに、スケールアップとスケールダウンの両方の結果が表示されます。 (パフォーマンスは実行ごとに異なるため、これらの数値は大まかなガイドラインとしてのみ使用してください。)一番上の画像はオリジナルです。ダブルサイズとハーフサイズにスケーリングします。

ご覧のとおり、AffineTransformOp.filter()で使用されるscaleBilinear()は、Graphics2D.drawImage()scale2()の標準的な描画方法よりも高速です。また、BiCubic補間は最も遅くなりますが、画像を拡大するときに最良の結果が得られます。 (パフォーマンスのために、scaleBilinear()およびscaleNearest().とのみ比較する必要があります)Bilinearは、画像を縮小するのに適しているようですが、それは難しい呼び出しです。そして、NearestNeighborは最も速く、最悪の結果をもたらします。バイリニアは、速度と品質の最適な妥協点のようです。 Image.getScaledInstance()メソッドで呼び出されたquestionable()は、パフォーマンスが非常に低く、NearestNeighborと同じ低品質を返しました。 (パフォーマンス番号は、画像を拡大するためにのみ与えられています。)

enter image description here

_public static BufferedImage scaleBilinear(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_BILINEAR;
    return scale(before, scale, interpolation);
}

public static BufferedImage scaleBicubic(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_BICUBIC;
    return scale(before, scale, interpolation);
}

public static BufferedImage scaleNearest(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
    return scale(before, scale, interpolation);
}

@NotNull
private static 
BufferedImage scale(final BufferedImage before, final double scale, final int type) {
    int w = before.getWidth();
    int h = before.getHeight();
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, before.getType());
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type);
    scaleOp.filter(before, after);
    return after;
}

/**
 * This is a more generic solution. It produces the same result, but it shows how you 
 * can draw anything you want into the newly created image. It's slower 
 * than scaleBilinear().
 * @param before The original image
 * @param scale The scale factor
 * @return A scaled version of the original image
 */
private static BufferedImage scale2(BufferedImage before, double scale) {
    int w = before.getWidth();
    int h = before.getHeight();
    // Create a new image of the proper size
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, before.getType());
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp
            = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);

    Graphics2D g2 = (Graphics2D) after.getGraphics();
    // Here, you may draw anything you want into the new image, but we're just drawing
    // a scaled version of the original image. This is slower than 
    // calling scaleOp.filter().
    g2.drawImage(before, scaleOp, 0, 0);
    g2.dispose();
    return after;
}

/**
 * I call this one "questionable" because it uses the questionable getScaledImage() 
 * method. This method is no longer favored because it's slow, as my tests confirm.
 * @param before The original image
 * @param scale The scale factor
 * @return The scaled image.
 */
private static Image questionable(final BufferedImage before, double scale) {
    int w2 = (int) (before.getWidth() * scale);
    int h2 = (int) (before.getHeight() * scale);
    return before.getScaledInstance(w2, h2, Image.SCALE_FAST);
}
_
3
MiguelMunoz

scale(..)の動作は少し異なります。 bufferedImage.getScaledInstance(..)を使用できます

3
Bozho