サイズを変更する必要がある写真が10,000枚あるので、Javaプログラムがあります。残念ながら、画像の品質はほとんど失われず、非圧縮画像にアクセスできません。
import Java.awt.Graphics;
import Java.awt.AlphaComposite;
import Java.awt.Graphics2D;
import Java.awt.Image;
import Java.awt.RenderingHints;
import Java.awt.image.BufferedImage;
import Java.io.File;
import Java.io.IOException;
import javax.imageio.ImageIO;
/**
* This class will resize all the images in a given folder
* @author
*
*/
public class JavaImageResizer {
public static void main(String[] args) throws IOException {
File folder = new File("/Users/me/Desktop/images/");
File[] listOfFiles = folder.listFiles();
System.out.println("Total No of Files:"+listOfFiles.length);
BufferedImage img = null;
BufferedImage tempPNG = null;
BufferedImage tempJPG = null;
File newFilePNG = null;
File newFileJPG = null;
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
System.out.println("File " + listOfFiles[i].getName());
img = ImageIO.read(new File("/Users/me/Desktop/images/"+listOfFiles[i].getName()));
tempJPG = resizeImage(img, img.getWidth(), img.getHeight());
newFileJPG = new File("/Users/me/Desktop/images/"+listOfFiles[i].getName()+"_New");
ImageIO.write(tempJPG, "jpg", newFileJPG);
}
}
System.out.println("DONE");
}
/**
* This function resize the image file and returns the BufferedImage object that can be saved to file system.
*/
public static BufferedImage resizeImage(final Image image, int width, int height) {
int targetw = 0;
int targeth = 75;
if (width > height)targetw = 112;
else targetw = 50;
do {
if (width > targetw) {
width /= 2;
if (width < targetw) width = targetw;
}
if (height > targeth) {
height /= 2;
if (height < targeth) height = targeth;
}
} while (width != targetw || height != targeth);
final BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
final Graphics2D graphics2D = bufferedImage.createGraphics();
graphics2D.setComposite(AlphaComposite.Src);
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.drawImage(image, 0, 0, width, height, null);
graphics2D.dispose();
return bufferedImage;
}
私が作業している画像はこれです:
これは、Microsoft Paintで行った手動のサイズ変更です。
これは私のプログラムからの出力です[双線形]:
PDATE:BICUBIC
を使用しても大きな違いはありません
これは私のプログラムからの出力です[bicubic]:
とにかくプログラム出力の品質を向上させるために、すべての写真を手動でサイズ変更する必要はありませんか?
前もって感謝します!
残念ながら、視覚的に良好な結果を提供するJavaには、すぐに使用できる推奨スケーリングはありません。とりわけ、スケーリングに推奨する方法は次のとおりです。
Graphics2d
バイキュービックの高速で良好な結果、通常Lanczos3ほど良好ではない)すべての方法の例は、この回答に記載されています。
異なるメソッド/ライブラリを使用して96x140
にスケーリングした画像を次に示します。画像をクリックしてフルサイズを取得します。
Graphics2d
バイキュービック補間Graphics2d
最近傍補間残念ながら、スケーリングアルゴリズムを判断するには単一の画像では不十分です。シャープなエッジのアイコン、テキストのある写真などをテストする必要があります。
アップスケーリング、特にダウンスケーリングに適していると言われています。残念ながら 現在のJDKにはネイティブ実装はありません ですから、自分で実装して Morten Nobel's lib のようなlibを使用してください。上記のlibを使用した簡単な例:
ResampleOp resizeOp = new ResampleOp(dWidth, dHeight);
resizeOp.setFilter(ResampleFilters.getLanczos3Filter());
BufferedImage scaledImage = resizeOp.filter(imageToScale, null);
Libは maven-centralで公開 ですが、残念ながら言及されていません。欠点は、高度に最適化されたハードウェアアクセラレーションの実装が私に知られていなければ、通常非常に遅いことです。ノーベルの実装は、Graphics2d
を使用した1/2ステップのプログレッシブスケーリングアルゴリズムよりも約8倍遅いです。 彼のブログでこのライブラリの詳細を読んでください 。
スケーリングに関するChris Campbellのブログ で述べられているように、プログレッシブスケーリングは基本的に最終的なサイズに達するまで画像を小さなステップで段階的にスケーリングします。キャンベルは、目標に到達するまで、幅/高さを半分にすることで説明します。これにより、良好な結果が得られ、Graphics2D
とともに使用できます。これはハードウェアアクセラレーションが可能なため、ほとんどの場合、許容できる結果で非常に優れたパフォーマンスを発揮します。これの主な欠点は、Graphics2D
を使用して半分以下にダウンスケールすると、1回しかスケーリングされないため、同じ平凡な結果が得られることです。
仕組みの簡単な例を次に示します。
次のライブラリには、Graphics2d
に基づいたプログレッシブスケーリングの形式が組み込まれています。
ターゲットがすべての次元の少なくとも半分である場合、プログレッシブ双線形アルゴリズムを使用します。それ以外の場合、アップスケーリングには単純なGraphics2d
双線形スケーリングと双三次を使用します。
Resizer resizer = DefaultResizerFactory.getInstance().getResizer(
new Dimension(imageToScale.getWidth(), imageToScale.getHeight()),
new Dimension(dWidth, dHeight))
BufferedImage scaledImage = new FixedSizeThumbnailMaker(
dWidth, dHeight, false, true).resizer(resizer).make(imageToScale);
私の ベンチマーク でGraphics2d
が平均6.9秒を記録する1ステップのスケーリングと同じか、わずかに高速です。
プログレッシブバイキュービックスケーリングを使用します。 QUALITY
設定では、Campbellスタイルのアルゴリズムを使用して、ステップごとに寸法を半分にし、ULTRA_QUALITY
には細かいステップを追加し、増分ごとにサイズを1/7減らします。 1回の反復が使用されます。
BufferedImage scaledImage = Scalr.resize(imageToScale, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_EXACT, dWidth, dHeight, bufferedImageOpArray);
主な欠点はパフォーマンスです。 ULTRA_QUALITY
は、他のライブラリよりもかなり低速です。 QUALITY
でさえ、Thumbnailatorの実装よりも少し遅くなります。私の単純な ベンチマーク は、それぞれ平均26.2秒と11.1秒になりました。
すべての基本的なGraphics2d
(双一次、双三次、および最近傍)のプログレッシブスケーリングの実装もあります。
BufferedImage scaledImage = new MultiStepRescaleOp(dWidth, dHeight, RenderingHints.VALUE_INTERPOLATION_BILINEAR).filter(imageToScale, null);
画像をスケーリングする現在のjdkの方法は次のようになります
scaledImage = new BufferedImage(dWidth, dHeight, imageType);
Graphics2D graphics2D = scaledImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(imageToScale, 0, 0, dWidth, dHeight, null);
graphics2D.dispose();
しかし、ほとんどの場合、どんな補間や他のRenderHints
が使用されても、ダウンスケーリングの結果には非常に失望します。一方、アップスケーリングは許容可能な画像を生成するようです(最良のバイキュービック)。以前のJDKバージョン(90年代v1.1の話)でImage.getScaledInstance()
が導入され、パラメーターSCALE_AREA_AVERAGING
で良好な視覚的結果が得られましたが、使用することはお勧めしません- 詳細な説明はこちら =。
Thumbnailator は、高品質のサムネイルを簡単な方法で作成するために作成されたライブラリです。既存の画像のバッチ変換を行うことは、そのユースケースの1つです。
たとえば、Thumbnailatorを使用して例を適合させるには、次のコードで同様の結果を達成できる必要があります。
File folder = new File("/Users/me/Desktop/images/");
Thumbnails.of(folder.listFiles())
.size(112, 75)
.outputFormat("jpg")
.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
これは先に進み、images
ディレクトリ内のすべてのファイルを取得し、それらを1つずつ処理し、112 x 75のサイズに収まるようにサイズを変更し、アスペクト比を維持しようとします。画像の「ゆがみ」を防ぐための元の画像。
Thumbnailatorは、画像の種類に関係なくすべてのファイルを読み取り(Java Image IOが形式をサポートしている限り、Thumbnailatorはそれを処理します)、サイズ変更操作を実行し、サムネイルをJPEGファイルとして、thumbnail.
をファイル名の先頭に追加します。
以下は、上記のコードを実行した場合に、元のファイル名がサムネイルのファイル名でどのように使用されるかを示しています。
images/fireworks.jpg -> images/thumbnail.fireworks.jpg
images/illustration.png -> images/thumbnail.illustration.png
images/mountains.jpg -> images/thumbnail.mountains.jpg
Marco13の答え で述べたように、画質に関しては、Chris Campbellが The Imagers of Image.getScaledInstance() で説明した手法がThumbnailatorに実装されており、複雑な処理を必要としない高品質のサムネイル。
以下は、Thumbnailatorを使用して元の質問に表示されている花火のサイズを変更するときに生成されるサムネイルです。
上記の画像は、次のコードで作成されました。
BufferedImage thumbnail =
Thumbnails.of(new URL("http://i.stack.imgur.com/X0aPT.jpg"))
.height(75)
.asBufferedImage();
ImageIO.write(thumbnail, "png", new File("24745147.png"));
このコードは、URLを入力として受け入れることも、ThumbnailatorもBufferedImage
sを作成できることを示しています。
免責事項:私は Thumbnailator ライブラリのメンテナーです。
入力画像が与えられると、コメントの最初のリンクの回答(Chris Campbellに対するクドス)のメソッドは、次のサムネイルのいずれかを生成します。
(もう1つは、MS Paintで作成したサムネイルです。他のものよりも「良い」と呼ぶのは難しいです...)
編集:これも指摘するために:あなたの元のコードの主な問題は、実際には複数のステップでスケールしなかったことでした。奇妙なループを使用して、ターゲットサイズを「計算」しました。重要な点は、実際に複数のステップでスケーリングを実行することです。
完全を期すために、MVCE
import Java.awt.Graphics2D;
import Java.awt.RenderingHints;
import Java.awt.Transparency;
import Java.awt.image.BufferedImage;
import Java.io.File;
import Java.io.FileOutputStream;
import Java.io.IOException;
import Java.io.OutputStream;
import Java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
public class ResizeQuality
{
public static void main(String[] args) throws IOException
{
BufferedImage image = ImageIO.read(new File("X0aPT.jpg"));
BufferedImage scaled = getScaledInstance(
image, 51, 75, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
writeJPG(scaled, new FileOutputStream("X0aPT_tn.jpg"), 0.85f);
}
public static 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;
}
public static void writeJPG(
BufferedImage bufferedImage,
OutputStream outputStream,
float quality) throws IOException
{
Iterator<ImageWriter> iterator =
ImageIO.getImageWritersByFormatName("jpg");
ImageWriter imageWriter = iterator.next();
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageWriteParam.setCompressionQuality(quality);
ImageOutputStream imageOutputStream =
new MemoryCacheImageOutputStream(outputStream);
imageWriter.setOutput(imageOutputStream);
IIOImage iioimage = new IIOImage(bufferedImage, null, null);
imageWriter.write(null, iioimage, imageWriteParam);
imageOutputStream.flush();
}
}
数日間の研究の後、私はjavaxtを好むでしょう。
使用 javaxt.io.Image
クラスには次のようなコンストラクタがあります:
public Image(Java.awt.image.BufferedImage bufferedImage)
あなたができるように( another example
):
javaxt.io.Image image = new javaxt.io.Image(bufferedImage);
image.setWidth(50);
image.setOutputQuality(1);
出力は次のとおりです。
TwelveMonkeys Library を忘れてはいけません
本当に印象的なフィルターコレクションが含まれています。
使用例:
BufferedImage input = ...; // Image to resample
int width, height = ...; // new width/height
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS);
BufferedImage output = resampler.filter(input, null);
サイズ変更の前にガウスぼかしを適用すると、結果は(プログラムの結果よりも)良いようです:
これは、sigma * (scale factor) = 0.3
の結果です。
ImageJ を使用すると、これを行うコードは非常に短くなります。
import ij.IJ;
import ij.ImagePlus;
import ij.io.Opener;
import ij.process.ImageProcessor;
public class Resizer {
public static void main(String[] args) {
processPicture("X0aPT.jpg", "output.jpg", 0.0198, ImageProcessor.NONE, 0.3);
}
public static void processPicture(String inputFile, String outputFilePath, double scaleFactor, int interpolationMethod, double sigmaFactor) {
Opener opener = new Opener();
ImageProcessor ip = opener.openImage(inputFile).getProcessor();
ip.blurGaussian(sigmaFactor / scaleFactor);
ip.setInterpolationMethod(interpolationMethod);
ImageProcessor outputProcessor = ip.resize((int)(ip.getWidth() * scaleFactor), (int)(ip.getHeight()*scaleFactor));
IJ.saveAs(new ImagePlus("", outputProcessor), outputFilePath.substring(outputFilePath.lastIndexOf('.')+1), outputFilePath);
}
}
ところで:必要なのはij-1.49d.jar
(または他のバージョンの同等物)だけです。 ImageJをinstallする必要はありません。
以下は、外部ライブラリを使用しない、プログレッシブスケーリングの独自の実装です。この助けを願っています。
private static BufferedImage progressiveScaling(BufferedImage before, Integer longestSideLength) {
if (before != null) {
Integer w = before.getWidth();
Integer h = before.getHeight();
Double ratio = h > w ? longestSideLength.doubleValue() / h : longestSideLength.doubleValue() / w;
//Multi Step Rescale operation
//This technique is describen in Chris Campbell’s blog The Perils of Image.getScaledInstance(). As Chris mentions, when downscaling to something less than factor 0.5, you get the best result by doing multiple downscaling with a minimum factor of 0.5 (in other words: each scaling operation should scale to maximum half the size).
while (ratio < 0.5) {
BufferedImage tmp = scale(before, 0.5);
before = tmp;
w = before.getWidth();
h = before.getHeight();
ratio = h > w ? longestSideLength.doubleValue() / h : longestSideLength.doubleValue() / w;
}
BufferedImage after = scale(before, ratio);
return after;
}
return null;
}
private static BufferedImage scale(BufferedImage imageToScale, Double ratio) {
Integer dWidth = ((Double) (imageToScale.getWidth() * ratio)).intValue();
Integer dHeight = ((Double) (imageToScale.getHeight() * ratio)).intValue();
BufferedImage scaledImage = new BufferedImage(dWidth, dHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = scaledImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(imageToScale, 0, 0, dWidth, dHeight, null);
graphics2D.dispose();
return scaledImage;
}