質問

写真をFacebookにアップロードするオープンソースアプリを持っています。帯域幅を節約するために、写真はアップロード前に自動的にサイズ変更されます(Facebookは最大サイズ制限を課しています)。数人が写真の品質について不平を言っており、実際に違いを見ることができます(一部のデモ画像ではこの問題

だから私の質問は、「最高」とは何ですか?品質を損なうことなく、または少なくとも品質の低下/アーティファクトを最小限に抑えながら、Javaで画像(写真など)を縮小する方法?

現在持っているコードを見ることができますこちらこのページ)。

役に立ちましたか?

解決

すべてを試しました-トリックこちら、そしてすべてのインターフェイスでImageMagickを使用した方が良いと言えますが、Javaのイメージングライブラリはこれに関しては十分に理解できません。正しく機能させるには、非常に多くの形式とアルゴリズムをサポートする必要があります。

他のヒント

フィル、最終的にどのソリューションを使用したかはわかりませんが、Javaでの画像のスケーリングは次の場合にかなり良く見える可能性があります。

  • JDKで十分にサポートされていないBufferedImageタイプは避けてください。
  • 増分スケーリングを使用
  • インクリメンタルスケーリングを使用する場合はバイキュービックに固定する

これらの方法でかなりのテストを行ってきましたが、インクリメンタルスケーリングと、サポートされている画像タイプへのこだわりが重要です-アレクサンダーは、彼はまだうまくいかなかったと言っていますが、残念です。

imgscalrライブラリ(Apache 2)をリリースしました約6か月前に「この画像の見栄えの良い拡大縮小版のコピーが欲しいのです!」の問題に対処するためにSOでこのような10の質問のようなものを読んだ後。

標準的な使用法は次のようになります。

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 640);

2番目の引数は、imgscalrが画像を拡大縮小するために使用する境界の幅と高さです-無効な寸法を渡した場合でもその比率を維持します-多くのより詳細なメソッドですが、これが最も簡単な使用法です。

たとえば、Facebookが画像を800x600ピクセルに制限する場合のユースケースは次のようになります。

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600);

これにより、画像は、サポートされている最適な画像タイプのままであり、Javaが集結できる最高品質の方法でスケーリングされます。

私自身の高解像度テストでは、ImageIOローダーによってサポートされていない画像タイプに画像が入れられた場合を除き、このライブラリ/これらのメソッドを使用して、スケーリングされた画像とのギャップの違いに気づきませんでした-たとえば、これはGIFがたくさんあります。それらをそのように残し、それらの不十分にサポートされているタイプからそれらを取得しない場合、それは本当にディザリングされ、ひどいように見えます。

この理由は、Java2Dチームが実際に、JDKが処理できるすべての異なるタイプのBufferedImagesに対して異なるハードウェアアクセラレーションパイプラインを持っているためです。あまり一般的ではないイメージタイプのサブセットは、すべて同じソフトウェアレンダリングの使用にフォールバックしますJava2Dの隠れたパイプラインで、見栄えの悪い、時には完全に不正確な画像になります。これは、そのロジックをライブラリに直接書き込んだことを説明し、理解しようとするPIAでした。

最もよくサポートされている2つのタイプは、BufferedImage.TYPE_INT_RGBおよび_ARGBです(興味がある場合)。

現在、Javaの画像サイズ変更に特化した2つの最も人気のあるオープンソースライブラリは次のとおりです。

さらにJavaの < code> Graphics2D その方法についてはこの質問をご覧ください)。これは特にダウンスケーリングで悪い結果。また、 ImageMagickへのJavaインターフェースもありますが、外部ツールが必要なため、ここでは省略します。

ビジュアル品質

580x852 pngを 145x213 にリサイズ/ダウンスケーリングした結果の比較です。参考として、Photoshop CS5「ウェブ用に保存」サイズ変更が使用されます。 注:結果は、作成されたライブラリが一緒にコピーしたものと1:1です。ズームはフィルタリングを使用せず、単純な最近傍アルゴリズムのみを使用します。 ここでできます元の画像を見つけます。

 comparison

  
      
  1. Thumbnailator 0.4.8、デフォルト設定、寸法調整なし
  2.   
  3. バイキュービックアルゴリズムを備えたPhotoshop CS5
  4.   
  5. ULTRA_QUALITY設定のimgscalr 4.2、寸法調整なし
  6.   
  7. Graphics2D(Java 8)とレンダリングヒントVALUE_INTERPOLATION_BICUBIC、VALUE_RENDER_QUALITY、VALUE_ANTIALIAS_ON
  8.   

これは主観的なものなので、最良の結果を選択するのは読者に任せます。一般に、 Graphics2D 以外のすべての出力は良好です。 ThumbnailatorはPhotoshopの出力に非常によく似たシャープな画像を生成しますが、imgscalrの出力はかなりソフトです。アイコン/テキストなどの場合はシャープな出力が必要で、写真の場合はソフト出力が必要な場合があります。

計算時間

このツールと、約< code> 96x96 から 2560x1440 までの425%の画像として処理:100%、150%、200%、300%、400%のスケーリングバージョン(したがって、114 * 5スケーリング操作) )。すべてのライブラリは、品質比較と同じ設定を使用します(可能な限り最高の品質)。時間はプロセス全体ではなくスケーリングのみです。 8GBのRAMと5回の実行でi5-2520Mを実行します。

  
      
  • サムネイル:7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700.3ms
  •   
  • imgscalr :25218.5ms | 25786.6ms | 25095.7ms | 25790.4ms | 29296.3ms
  •   
  • Graphics2D :7387.6ms | 7177.0ms | 7048.2ms | 7132.3ms | 7510.3ms
  •   

このベンチマークで使用されるコードは次のとおりです。

興味深いことに Thumbnailatorは平均時間が6.9秒で最速です 7.2秒でJava2D が続き、 imgscalrが26.2秒で劣っています >。 imgscalrは ULTRA_QUALITY に設定されているため、これはおそらく公平ではありません。 QUALITY 設定では、平均でより競争力のある11.1秒になります。

カスタム品質で画像のサイズを変更するには、thumbnailator.jarを使用します。

サンプルコード    http://code.google.com/p/thumbnailator/wiki/Examples

使用しているレンダリングヒントは何ですか?通常、バイキュービックリサンプリングが最適です。あなたがリンクしている写真では、それらは非常にぎくしゃくしているので、あなたはあなたのヒントとして最も近い隣人を使用していると思います。

リンクする PictureScaler クラス内 paintComponent メソッドでは、画像のサイズ変更に6つの異なる方法を使用します。 6つすべてを試して、どれが最良の結果をもたらすかを確認しましたか?

いくつかのイライラする実験の後、次の評価のサイズを変更し、プロジェクトでマルチパスアプローチを採用しました。

それを行うには、getScaledInstance()メソッドをサムネイルジェネレータークラスにコピーし、ImageIO(BufferedImageを返す)を使用するように画像読み取り方法を変更し、非常に満足! >

Photoshop CS3で行われたサイズ変更と結果を比較しましたが、結果はほとんど同じです。

アスペクト比を維持したまま、最高品質のサイズ変更が必要でした。 いくつかのことを試し、いくつかのエントリを読みました。 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();
    }
  }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top