How to scale a BufferedImage
按照javadocs,我尝试缩放
1 2 3 4 | BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded); Graphics2D grph = image.createGraphics(); grph.scale(2.0, 2.0); grph.dispose(); |
我不明白为什么它不起作用,有帮助吗?
1 2 3 4 5 6 7 8 9 | 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); |
显示的片段说明了重采样,而非裁剪;这个相关的答案解决了这个问题;这里检查一些相关的例子。
不幸的是,即使没有问题,getScaledInstance()的性能也很差。
另一种方法是创建一个新的BufferedImage,并在新图像上绘制原始图像的缩放版本。
1 2 3 4 5 6 7 | 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指示新的BufferedImage大小,必须正确计算。
在因子缩放的情况下:
1 2 |
编辑:找到了说明性能问题的文章:Image.getScaledInstance()的危险
正如@Bozho所说,您可能要使用
要了解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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")); } } |
给定duke.png:
它产生duke_double_size.png:
使用imgscalr – Java图像缩放库:
1 2 |
这对我来说足够快。
如果您不介意使用外部库,则Thumbnailator可以缩放
Thumbnailator将负责处理Java 2D处理(例如使用
1 |
尽管顾名思义,Thumbnailator旨在缩小图像,但在默认缩放器实现中使用双线性插值,它也会做得不错,可以放大图像。
免责声明:我是Thumbnailator库的维护者。
要缩放图像,您需要创建一个新图像并将其绘制。一种方法是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
另一种方法是使用缩放操作进行缩放,将原始图像简单地绘制到新图像中。此方法非常相似,但是它也说明了如何在最终图像中绘制所需的任何内容。 (我在空白处放置了两种方法开始不同的地方。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |
附录:结果
为了说明差异,我比较了以下五种方法的结果。这是结果的样子,随性能数据一起向上和向下缩放。 (每次运行的性能各不相同,因此仅将这些数字作为大致指导。)顶部图像为原始图像。我将其缩放为两倍大小和一半大小。
如您所见,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 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); } |