根据Javadocs的指导,我尝试缩放BufferedImage
,但没有成功,这是我的代码:
BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();
我不明白为什么它不能正常工作,有帮助吗?
根据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);
after = ...
这样的语句时,是否真的需要为 after
分配所有内存? - Martijn Courteauxafter
的尺寸没有变化,只是原图的左上角四分之一。解决方法是在创建after
时将宽度和高度乘以比例尺。 - MiguelMunoz不幸的是,如果不出问题的话,getScaledInstance()的性能非常差。
另一种替代方法是创建一个新的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);
要缩放图像,您需要创建一个新图像并绘制到其中。一种方法是使用 AffineTransferOp
的 filter()
方法,如此处所建议的这里。 这使您可以选择插值技术。
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;
}
附加说明:结果
为了说明差异,我比较了下面五种方法的结果。以下是结果的缩放和性能数据,包括放大和缩小。 (性能因运行次数而异,因此仅将这些数字作为粗略指导。)顶部图像是原始图像。我将其缩放为两倍和一半大小。
正如您所看到的,在scaleBilinear()
中使用的AffineTransformOp.filter()
比scale2()
中的标准绘图方法Graphics2D.drawImage()
快。双立方插值最慢,但在扩展图像时效果最好。(对于性能,它应该只与scaleBilinear()
和scaleNearest()
进行比较。)双线性似乎更适合缩小图像,尽管这很难说。 NearestNeighbor是最快的,但效果最差。双线性似乎是速度和质量之间的最佳折衷方案。questionable()
方法中调用的Image.getScaledInstance()
表现非常差,并返回与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);
}
getScaledInstance
与Image.SCALE_SMOOTH
可以实现,但众所周知它非常缓慢。我尝试过的其他所有方法(包括AffineTransformOp
和应用了任何组合的RenderingHints进行变换绘制)都会给我带来锯齿边缘。 - user2543253正如@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:
grph.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation);
,其中interpolation
是RenderingHints.VALUE_INTERPOLATION_...
,例如VALUE_INTERPOLATION_BICUBIC
吗? - aioobeBufferedImage
进行缩放。Graphics2D
并设置适当的 渲染提示),因此可以使用简单流畅的 API 调用来调整图像大小:BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();
声明:我是Thumbnailator库的维护者。
scale(..)
的工作方式有些不同。您可以使用 bufferedImage.getScaledInstance(..)
。
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);