web-dev-qa-db-ja.com

品質を損なうことなく画像のサイズを変更する

このコードを作成して、2つの要素で画像のサイズを変更しました。動作しますが、サイズ変更後の画像の品質は非常に悪いです!手伝って頂けますか?

これはコードです

public class ImageTest {

private static final int factor1 = 3;
private static final int factor2 = 4;
public static void main(String [] args){

    JFileChooser cs = new JFileChooser();
    cs.setFileSelectionMode(cs.DIRECTORIES_ONLY);
    int i = cs.showOpenDialog(null);
    if(i==cs.APPROVE_OPTION){
        File f = cs.getSelectedFile();
        File[] ff = f.listFiles();
        for(int j=0;j<ff.length;j++){
            String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1);
            System.out.println(end);
            try{
                BufferedImage originalImage = ImageIO.read(ff[j]);
                int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
                BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type);
                ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName()));
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }


}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){
    int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2;
    int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2;
    BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type);
    Graphics2D g = resizedImage.createGraphics();
    g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null);
    g.dispose();    
    g.setComposite(AlphaComposite.Src);

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.setRenderingHint(RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    return resizedImage;
}   
   }

ResizeImageWithHintは品質を落とさないようにスコープ内で行われていることをWeb上で見ました。どうして?これを手伝ってくれませんか?

14
JackTurky

このトピックについて私が今まで読んだ中で最高の記事は The Perils of Image.getScaledInstance() (web archive)です。

つまり、良い画像を取得するには、いくつかのサイズ変更手順を使用する必要があります。記事のヘルパーメソッド:

public BufferedImage getScaledInstance(BufferedImage img,
                                       int targetWidth,
                                       int targetHeight,
                                       Object hint,
                                       boolean higherQuality)
{
    int type = (img.getTransparency() == Transparency.OPAQUE) ?
        BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = (BufferedImage)img;
    int w, h;
    if (higherQuality) {
        // Use multi-step technique: start with original size, then
        // scale down in multiple passes with drawImage()
        // until the target size is reached
        w = img.getWidth();
        h = img.getHeight();
    } else {
        // Use one-step technique: scale directly from original
        // size to target size with a single drawImage() call
        w = targetWidth;
        h = targetHeight;
    }

    do {
        if (higherQuality && w > targetWidth) {
            w /= 2;
            if (w < targetWidth) {
                w = targetWidth;
            }
        }

        if (higherQuality && h > targetHeight) {
            h /= 2;
            if (h < targetHeight) {
                h = targetHeight;
            }
        }

        BufferedImage tmp = new BufferedImage(w, h, type);
        Graphics2D g2 = tmp.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
        g2.drawImage(ret, 0, 0, w, h, null);
        g2.dispose();

        ret = tmp;
    } while (w != targetWidth || h != targetHeight);

    return ret;
}
30
nfechner

次のコードは、アスペクト比を維持したまま、最高品質のサイズ変更を生成しました。いくつかのことを試して、他の回答でここに提示されているいくつかのエントリを読みました。 2日間失われ、最終的に私は単純なJavaメソッド(ImageMagickおよびJava-image-scalingライブラリも試してみました)で最高の結果を得ました):

public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
  BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
  double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
  if (width < 1) {
    width = (int) (height * ratio + 0.4);
  } else if (height < 1) {
    height = (int) (width /ratio + 0.4);
  }

  Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
  BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
  Graphics2D g2d = bufferedScaled.createGraphics();
  g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  g2d.drawImage(scaled, 0, 0, width, height, null);
  dest.createNewFile();
  writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
  return true;
}


/**
* Write a JPEG file setting the compression quality.
*
* @param image a BufferedImage to be saved
* @param destFile destination file (absolute or relative path)
* @param quality a float between 0 and 1, where 1 means uncompressed.
* @throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
      throws IOException {
  ImageWriter writer = null;
  FileImageOutputStream output = null;
  try {
    writer = ImageIO.getImageWritersByFormatName("jpeg").next();
    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(quality);
    output = new FileImageOutputStream(new File(destFile));
    writer.setOutput(output);
    IIOImage iioImage = new IIOImage(image, null, null);
    writer.write(null, iioImage, param);
  } catch (IOException ex) {
    throw ex;
  } finally {
    if (writer != null) {
      writer.dispose();
    }
    if (output != null) {
      output.close();
    }
  }
}
10
Mladen Adamovic

質問が古いことを知っています... Webサーフィンしてから別のソリューションを試しましたが、getScaledInstance()を使用して最高の結果を得て、Image.SCALE_SMOOTHを引数として。実際、結果の画像品質は本当に優れていました。以下の私のコード:

final int THUMB_SIDE = 140;
try {
    BufferedImage masterImage = ImageIO.read(startingImage);
    BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = thumbImage.createGraphics();
    g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null);
    g2d.dispose();
    String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png";
    ImageIO.write(thumbImage, "png", new File(thumb_path));
} catch (IOException e) {
    e.printStackTrace();
}
6
user3564042

画像ソースがpngの場合は、次のように使用します。

Image imgSmall = imgBig.getScaledInstance(
        targetWidth, targetHeight, Image.SCALE_SMOOTH);

あまり品質を落とさずにjpegまたはgifのサイズを変更したい場合は、2010年に次のライブラリを作成しました: beautylib on github 内部でこの他のライブラリを使用: Java-image-scaling 。ソースコードを直接見て何か役立つものを見つけることができます: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.Java

3
Felipe

答えはどれも、あなたが望む本当の品質を手に入れるのに役立ちません。プロジェクトにthumbailator.jarを含めます(ここからフォームをダウンロードしてください)。

https://code.google.com/p/thumbnailator/

https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2

次に、最初に画像をアップロードし(Thumbnailatorなしでファイルとして-親指を作成するために使用しますが、もちろん、大きな画像を作成することもできます)、必要なすべてのサイズに変更します(Thumbnailator 800x600などを使用)。品質はとても良いでしょう。私はこの長い間探していましたが、この.jarは私がやりたいことを達成するのに役立ちました。

2
DarioBB

はい、同じ問題を抱えて解決しました 私の質問を読んでください (回答は質問に埋め込まれています)。 imgscalrおよびJava-image-scalingライブラリを試し、2番目のライブラリを見つけましたはるかに良い品質。サムネイルの例の違いを理解するには、モニターに近づいてください。

私の最初の考えにもかかわらず、画像のサイズを変更することは非常に複雑なことのように思われます。自分でそれをしたくないのです。たとえば、私はJava-image-scalingResampleFilters.getLanczos3Filter()を使用してより良い結果を得るように指示します。

また、標準の75よりも高い品質でJPGを保存する方法についても説明します。これにより、特にサムネイルで悪い結果が生じます。

また、バイト配列からの画像の読み取り、ファイルからの拡大、幅のみまたは高さのみの指定によるスケーリングなどの一般的なタスクを支援するために、 MyImage と呼ばれる小さなクラスも作成しました。境界ボックスを指定して拡大縮小し、幅と高さを指定して拡大縮小し、画像を歪ませないように白い帯を追加してJPGファイルに書き込みます。

1
stivlo

投稿されたすべてのメソッドが機能しないので、QrCodeのサイズを小さくする必要がありますが、上記の方法では、元の写真を撮ってペイントでサイズを変更すると、スキャナーの機能が低下し、スキャナーが機能しません。

0
cyril