按照javadocs,我尝试缩放
BufferedImage
但没有成功,这是我的代码:
BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();
我不明白为什么它不起作用,有什么帮助吗?
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);
不幸的是,getScaledInstance() 的性能即使没有问题,也非常差。
另一种方法是创建一个新的 BufferedImage 并在新的 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 表示新的 BufferedImage 大小,必须正确计算。 在因子缩放的情况下:
int newWidth = new Double(original.getWidth() * widthFactor).intValue();
int newHeight = new Double(original.getHeight() * heightFactor).intValue();
编辑:找到说明性能问题的文章:The Perils of Image.getScaledInstance()
使用 imgscalr – Java 图像缩放库:
BufferedImage image =
Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight);
要缩放图像,您需要创建一个新图像并在其中绘制。一种方法是使用
filter()
的 AffineTransferOp
方法,如 here 所建议。这允许您选择插值技术。
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;
}
另一种方法是简单地将原始图像绘制到新图像中,使用缩放操作来进行缩放。此方法非常相似,但它也说明了如何在最终图像中绘制任何您想要的内容。 (我在这两种方法开始有所不同的地方放置了一个空行。)
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;
}
附录:结果
为了说明差异,我比较了以下五种方法的结果。以下是按比例放大和缩小的结果以及性能数据。 (每次运行的性能都会有所不同,因此这些数字仅作为粗略指导。)顶部图像是原始图像。我将其缩放为两倍大小和一半大小。
如您所见,
AffineTransformOp.filter()
中使用的scaleBilinear()
比Graphics2D.drawImage()
中scale2()
的标准绘制方法更快。此外,双三次插值是最慢的,但在扩展图像时给出最佳结果。 (就性能而言,它只能与 scaleBilinear()
和 scaleNearest().
进行比较)双线性似乎更适合缩小图像,尽管这是一个艰难的选择。而 NearestNeighbor 是最快的,但结果也最差。双线性似乎是速度和质量之间的最佳折衷方案。在 Image.getScaledInstance()
方法中调用的 questionable()
表现非常差,并且返回与 NearestNeighbor 相同的低质量。 (性能数据仅用于扩展图像。)
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);
}
正如@Bozho所说,你可能想使用
getScaledInstance
。
要了解
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"));
}
}
鉴于duke.png:
它产生duke_double_size.png:
如果您不介意使用外部库,Thumbnailator可以执行
BufferedImage
的缩放。
Thumbnailator 将负责处理 Java 2D 处理(例如使用
Graphics2D
并设置适当的 渲染提示),以便可以使用简单流畅的 API 调用来调整图像大小:
BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();
虽然 Thumbnailator,顾名思义,是针对缩小图像的,但它在放大图像方面也能做得不错,在其默认缩放器实现中使用双线性插值。
免责声明:我是 Thumbnailator 库的维护者。
scale(..)
的工作原理有点不同。您可以使用bufferedImage.getScaledInstance(..)
trashgod 评价最高的答案有一些小问题,所以我将发布一个改进的版本(由于声誉点不足,我不能这样做作为对他的解决方案的评论)。
BufferedImage before = getBufferedImage(encoded);
int w = before.getWidth();
int h = before.getHeight();
double scaleFactor = 2.0;
BufferedImage after = new BufferedImage((int)(w * scaleFactor), (int)(h * scaleFactor), before.getType());
AffineTransform at = new AffineTransform();
at.scale(scaleFactor, scaleFactor);
AffineTransformOp scaleOp =
new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);
改进:
after
图像具有正确的尺寸来容纳缩放后的图像after
图像与编码图像具有相同的图像类型(否则转换将失败)