Java 如何缩放 BufferedImage
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4216123/
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 scale a BufferedImage
提问by Thiago Diniz
Following the javadocs, I have tried to scale a BufferedImage
without success here is my code:
遵循javadocs,我尝试在BufferedImage
没有成功的情况下进行缩放,这是我的代码:
BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();
I can't understand why it is not working, any help?
我不明白为什么它不起作用,有什么帮助吗?
采纳答案by trashgod
AffineTransformOp
offers the additional flexibility of choosing the interpolation type.
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);
The fragment shown illustrates resampling, not cropping; this related answeraddresses the issue; some related examples are examined here.
回答by Bozho
scale(..)
works a bit differently. You can use bufferedImage.getScaledInstance(..)
scale(..)
工作方式有点不同。您可以使用bufferedImage.getScaledInstance(..)
回答by aioobe
As @Bozho says, you probably want to use getScaledInstance
.
正如@Bozho 所说,您可能想要使用getScaledInstance
.
To understand how grph.scale(2.0, 2.0)
works however, you could have a look at this code:
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"));
}
}
Given duke.png:
鉴于duke.png:
it produces duke_double_size.png:
它产生duke_double_size.png:
回答by charisis
Unfortunately the performance of getScaledInstance() is very poor if not problematic.
不幸的是,如果没有问题, getScaledInstance() 的性能非常差。
The alternative approach is to create a new BufferedImage and and draw a scaled version of the original on the new one.
另一种方法是创建一个新的 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 indicate the new BufferedImage size and have to be properly calculated. In case of factor scaling:
newWidth,newHeight 表示新的 BufferedImage 大小,必须正确计算。在因子缩放的情况下:
int newWidth = new Double(original.getWidth() * widthFactor).intValue();
int newHeight = new Double(original.getHeight() * heightFactor).intValue();
EDIT: Found the article illustrating the performance issue: The Perils of Image.getScaledInstance()
编辑:找到说明性能问题的文章:The Perils of Image.getScaledInstance()
回答by ceklock
Using imgscalr – Java Image Scaling Library:
BufferedImage image =
Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight);
This is fast enough for me.
这对我来说已经足够快了。
回答by coobird
If you do not mind using an external library, Thumbnailatorcan perform scaling of BufferedImage
s.
如果您不介意使用外部库,Thumbnailator可以执行BufferedImage
s 的缩放。
Thumbnailator will take care of handling the Java 2Dprocessing (such as using Graphics2D
and setting appropriate rendering hints) so that a simple fluent API call can be used to resize images:
Thumbnailator 将负责处理Java 2D处理(例如使用Graphics2D
和设置适当的渲染提示),以便可以使用简单流畅的 API 调用来调整图像大小:
BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();
Although Thumbnailator, as its name implies, is geared toward shrinking images, it will do a decent job enlarging images as well, using bilinear interpolation in its default resizer implementation.
虽然 Thumbnailator 顾名思义,是针对缩小图像的,但它也可以很好地放大图像,在其默认的调整器实现中使用双线性插值。
Disclaimer: I am the maintainer of the Thumbnailatorlibrary.
免责声明:我是缩略图库的维护者。
回答by MiguelMunoz
To scale an image, you need to create a new image and draw into it. One way is to use the filter()
method of an AffineTransferOp
, as suggested here. This allows you to choose the interpolation technique.
要缩放图像,您需要创建一个新图像并在其中绘制。一种方法是使用filter()
的方法AffineTransferOp
,如建议在这里。这允许您选择插值技术。
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;
}
Another way is to simply draw the original image into the new image, using a scaling operation to do the scaling. This method is very similar, but it also illustrates how you can draw anything you want in the final image. (I put in a blank line where the two methods start to differ.)
另一种方法是简单地将原始图像绘制到新图像中,使用缩放操作进行缩放。这种方法非常相似,但它也说明了如何在最终图像中绘制任何你想要的东西。(我在这两种方法开始不同的地方加了一个空行。)
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;
}
Addendum: Results
附录:结果
To illustrate the differences, I compared the results of the five methods below. Here is what the results look like, scaled both up and down, along with performance data. (Performance varies from one run to the next, so take these numbers only as rough guidelines.) The top image is the original. I scale it double-size and half-size.
为了说明差异,我比较了以下五种方法的结果。这是结果的样子,放大和缩小,以及性能数据。(性能因一次运行而异,因此仅将这些数字作为粗略的指导方针。)顶部图像是原始图像。我把它放大成双倍尺寸和半尺寸。
As you can see, AffineTransformOp.filter()
, used in scaleBilinear()
, is faster than the standard drawing method of Graphics2D.drawImage()
in scale2()
. Also BiCubic interpolation is the slowest, but gives the best results when expanding the image. (For performance, it should only be compared with scaleBilinear()
and scaleNearest().
) Bilinear seems to be better for shrinking the image, although it's a tough call. And NearestNeighbor is the fastest, with the worst results. Bilinear seems to be the best compromise between speed and quality. The Image.getScaledInstance()
, called in the questionable()
method, performed very poorly, and returned the same low quality as NearestNeighbor. (Performance numbers are only given for expanding the image.)
正如你所看到的,AffineTransformOp.filter()
,用于scaleBilinear()
,比标准绘制方法快Graphics2D.drawImage()
的scale2()
。BiCubic 插值也是最慢的,但在扩展图像时给出了最好的结果。(对于性能,它应该只与scaleBilinear()
和进行比较scaleNearest().
)双线性似乎更适合缩小图像,尽管这是一个艰难的决定。NearestNeighbor 是最快的,结果最差。双线性似乎是速度和质量之间的最佳折衷。的Image.getScaledInstance()
,堪称questionable()
方法,进行非常差,并返回相同的品质较低,因为最近邻。(性能数字仅用于扩展图像。)
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);
}