Java 如何提高调整图像大小的 g.drawImage() 方法的性能
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3967731/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to improve the performance of g.drawImage() method for resizing images
提问by Balázs Németh
I have an application where users are able to upload pictures in albums but naturally the uploaded images need to be resized so there are also thumbs available and the shown pictures also fit in the page (eg. 800x600). The way I do the resize is like this:
我有一个应用程序,用户可以在其中上传相册中的图片,但上传的图片自然需要调整大小,因此也有可用的拇指,并且显示的图片也适合页面(例如 800x600)。我调整大小的方式是这样的:
Image scaledImage = img.getScaledInstance((int)width, (int)height, Image.SCALE_SMOOTH);
BufferedImage imageBuff = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_RGB);
Graphics g = imageBuff.createGraphics();
g.drawImage(scaledImage, 0, 0, new Color(0,0,0), null);
g.dispose();
And it works okayish. My only problem is that the g.drawImage()
method seems to be awfully slow, and I just cannot imagine the user to be patient enought to wait for an upload of 20 pictures 20*10 secs ~ 3 minutes. In fact, on my computer it takes almost 40 secs for making the 3 different resizes for a single picture.
它工作正常。我唯一的问题是该g.drawImage()
方法似乎非常慢,我无法想象用户会耐心等待上传 20 张图片 20*10 秒 ~ 3 分钟。事实上,在我的电脑上,为一张图片制作 3 种不同的大小调整需要将近 40 秒。
That's not good enough, and I'm looking for a faster solution. I'm wondering if somebody could tell me about a better one in Java OR by calling a shell script, command, whatever hack you know, it has to be quicker, everything else does not matter this time.
这还不够好,我正在寻找更快的解决方案。我想知道是否有人可以通过调用 shell 脚本、命令或您知道的任何 hack 来告诉我 Java 中更好的一个,它必须更快,这次其他一切都无关紧要。
采纳答案by dogbane
You can use ImageMagickto create thumbnails.
您可以使用的ImageMagick来创建缩略图。
convert -define jpeg:size=500x180 hatching_orig.jpg -auto-orient \
-thumbnail 250x90 -unsharp 0x.5 thumbnail.gif
To use it from Java you can try JMagickwhich provides a Java (JNI) interface to ImageMagick. Or you can simply invoke the ImageMagick commands directly using Runtime.exec
or ProcessBuilder
.
要从 Java 使用它,您可以尝试JMagick,它为 ImageMagick 提供 Java (JNI) 接口。或者您可以直接使用Runtime.exec
或直接调用 ImageMagick 命令ProcessBuilder
。
回答by echox
You will ever have a trade off between the speed of the resizing and the quality of the resulting picture. You might try another scaling algorithm of the JDK.
您将在调整大小的速度和生成的图片质量之间进行权衡。您可以尝试 JDK 的另一种缩放算法。
The best and most flexible tool for image editing AFAIK is ImageMagick.
AFAIK 最好和最灵活的图像编辑工具是ImageMagick。
There are two interfaces for the Java Language:
Java 语言有两个接口:
- JMagick- is a JNI Interface to ImageMagick. See the projects Wikito get more information.
- im4java- is a command line interface for ImageMagick. It is not, like JMagick, based on JNI.
- JMagick- 是 ImageMagick 的 JNI 接口。请参阅项目Wiki以获取更多信息。
- im4java- 是 ImageMagick 的命令行界面。它不像 JMagick 那样基于 JNI。
You should prefer im4java before using the command line directly to call ImageMagick.
在直接使用命令行调用 ImageMagick 之前,您应该更喜欢 im4java。
回答by Klarth
Do you really need the quality that is provided by using Image.SCALE_SMOOTH? If you don't, you can try using Image.SCALE_FAST. You might find this articlehelpful if you want to stick with something provided by Java.
您真的需要使用 Image.SCALE_SMOOTH 提供的质量吗?如果没有,您可以尝试使用Image.SCALE_FAST。如果您想坚持使用 Java 提供的东西,您可能会发现这篇文章很有帮助。
回答by mdrg
If you want something fast, you're probably better with some native code, if you can give up on portability.
如果您想要一些快速的东西,如果您可以放弃可移植性,那么使用一些本机代码可能会更好。
But if you want a pure Java solution, you can try some other solutions as well, like Graphics2D.scaleand Image.getScaledInstance. I've used them in the past, but can't remember which had better performance or better looking results, sorry.
但是如果你想要一个纯 Java 解决方案,你也可以尝试一些其他的解决方案,比如Graphics2D.scale和Image.getScaledInstance。我过去使用过它们,但不记得哪个具有更好的性能或更好的效果,抱歉。
Try them out, and see which one best fits your needs.
尝试一下,看看哪一个最适合您的需求。
回答by J?rn Horstmann
I'm using code similar to the following to scale images, I removed the part that deals with preserving the aspect ratio. The performance was definitely better than 10s per image, but I don't remember any exact numbers. To archive better quality when downscaling you should scale in several steps if the original image is more than twice the size of the wanted thumbnail, each step should scale the previous image to about half its size.
我正在使用类似于以下的代码来缩放图像,我删除了处理保留纵横比的部分。每张图像的性能肯定优于 10 秒,但我不记得任何确切的数字。为了在缩小时存档更好的质量,如果原始图像的大小是所需缩略图的两倍以上,您应该分几步进行缩放,每一步都应将前一个图像缩放到其大小的一半左右。
public static BufferedImage getScaledImage(BufferedImage image, int width, int height) throws IOException {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double scaleX = (double)width/imageWidth;
double scaleY = (double)height/imageHeight;
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY);
AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR);
return bilinearScaleOp.filter(
image,
new BufferedImage(width, height, image.getType()));
}
回答by leonbloy
Some improvement in performance (perhaps small, perhaps negligible, perhaps at the expense of quality) can be attained by tweaking the rendering hints. E.g.
通过调整渲染提示可以实现性能的一些改进(也许很小,也许可以忽略不计,也许以牺牲质量为代价)。例如
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
回答by johnstosh
Well, Jacob and I wanted to resize an Image, not a BufferedImage. So we ended up with this code:
好吧,Jacob 和我想调整 Image 的大小,而不是 BufferedImage。所以我们最终得到了这个代码:
/**
* we want the x and o to be resized when the JFrame is resized
*
* @param originalImage an x or an o. Use cross or oh fields.
*
* @param biggerWidth
* @param biggerHeight
*/
private Image resizeToBig(Image originalImage, int biggerWidth, int biggerHeight) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage resizedImage = new BufferedImage(biggerWidth, biggerHeight, type);
Graphics2D g = resizedImage.createGraphics();
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);
g.drawImage(originalImage, 0, 0, biggerWidth, biggerHeight, this);
g.dispose();
return resizedImage;
}
回答by Anthony O.
I used im4java
with GraphicsMagickin order to have really faster results (faster than ImageIO).
我im4java
与GraphicsMagick一起使用是为了获得更快的结果(比 ImageIO 快)。
Used that sort of code :
使用了那种代码:
public static void createFilePreview(final File originalFile, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
runThumbnail(new ConvertCmd(), originalFile.getAbsolutePath(), originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
}
public static void createFilePreview(final InputStream originalFileInputStream, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
final ConvertCmd cmd = new ConvertCmd();
cmd.setInputProvider(new Pipe(originalFileInputStream, null));
runThumbnail(cmd, "-", originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
}
private static void runThumbnail(final ConvertCmd cmd, final String originalFile, final String originalFileMimeType, final String destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
final IMOperation operation = new IMOperation();
// if it is a PDF, will add some optional parameters to get nicer results
if (originalFileMimeType.startsWith("application/pdf")) {
operation.define("pdf:use-trimbox=true"); // as it is said here http://www.prepressure.com/pdf/basics/page_boxes "The imposition programs and workflows that I know all use the TrimBox as the basis for positioning pages on a press sheet."
operation.density(300, 300); // augment the rendering from 75 (screen size) to 300 dpi in order to create big preview with good quality
}
operation.addImage("[0]"); // if it is a PDF or other multiple image source, will extract the first page / image, else it is ignored
operation.autoOrient(); // Auto-orient the image if it contains some orientation information (typically JPEG with EXIF header)
operation.thumbnail(maxWidth, maxHeight);
operation.addImage();
cmd.run(operation, originalFile, destinationPreviewFile);
}
回答by dpineda
this works for me:
这对我有用:
private BufferedImage getScaledImage(BufferedImage src, int w, int h){
int original_width = src.getWidth();
int original_height = src.getHeight();
int bound_width = w;
int bound_height = h;
int new_width = original_width;
int new_height = original_height;
// first check if we need to scale width
if (original_width > bound_width) {
//scale width to fit
new_width = bound_width;
//scale height to maintain aspect ratio
new_height = (new_width * original_height) / original_width;
}
// then check if we need to scale even with the new height
if (new_height > bound_height) {
//scale height to fit instead
new_height = bound_height;
//scale width to maintain aspect ratio
new_width = (new_height * original_width) / original_height;
}
BufferedImage resizedImg = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setBackground(Color.WHITE);
g2.clearRect(0,0,new_width, new_height);
g2.drawImage(src, 0, 0, new_width, new_height, null);
g2.dispose();
return resizedImg;
}
also i added white background for png
我还为png添加了白色背景
回答by nemo
The fastest way to scale an image in java without loosing image quality is to use Bilinear scaling. Bilinear is only good if you scale the image by 50% at a time because of the way it works. The following code is from 'Filthy rich clients' by Chet Haase. He explains multiple techniques in the book, but this one has the highest performance to quality trade-off.
在不降低图像质量的情况下在 java 中缩放图像的最快方法是使用双线性缩放。由于其工作方式,双线性仅在您一次将图像缩放 50% 时才有效。以下代码来自 Chet Haase 的“肮脏的富客户”。他在书中解释了多种技术,但这种技术在质量权衡方面具有最高的性能。
It supports all types of BufferedImages so don't worry about compatability. It also lets java2D hardware accelerate your image because the calculations are done by Java2D. Don't worry if you don't understand that last part. The most important thing is that this is the fastest way to do it.
它支持所有类型的 BufferedImages,所以不用担心兼容性。它还可以让 java2D 硬件加速您的图像,因为计算是由 Java2D 完成的。如果您不理解最后一部分,请不要担心。最重要的是,这是最快的方法。
public static BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, boolean progressiveBilinear)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
BufferedImage scratchImage = null;
Graphics2D g2 = null;
int w, h;
int prevW = ret.getWidth();
int prevH = ret.getHeight();
if(progressiveBilinear) {
w = img.getWidth();
h = img.getHeight();
}else{
w = targetWidth;
h = targetHeight;
}
do {
if (progressiveBilinear && w > targetWidth) {
w /= 2;
if(w < targetWidth) {
w = targetWidth;
}
}
if (progressiveBilinear && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
if(scratchImage == null) {
scratchImage = new BufferedImage(w, h, type);
g2 = scratchImage.createGraphics();
}
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
prevW = w;
prevH = h;
ret = scratchImage;
} while (w != targetWidth || h != targetHeight);
if (g2 != null) {
g2.dispose();
}
if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
scratchImage = new BufferedImage(targetWidth, targetHeight, type);
g2 = scratchImage.createGraphics();
g2.drawImage(ret, 0, 0, null);
g2.dispose();
ret = scratchImage;
}
System.out.println("ret is "+ret);
return ret;
}