Java 如何缩放BuffereImage
遵循javadocs,我尝试缩放Java 如何缩放BuffereImage,java,image,image-processing,bufferedimage,image-scaling,Java,Image,Image Processing,Bufferedimage,Image Scaling,遵循javadocs,我尝试缩放BufferedImage,但没有成功。下面是我的代码: BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded); Graphics2D grph = image.createGraphics(); grph.scale(2.0, 2.0); grph.dispose(); 我不明白它为什么不起作用,有什么帮助吗?scale(…)的工作原理有点不同。您可以使用bufferedImag
BufferedImage
,但没有成功。下面是我的代码:
BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();
我不明白它为什么不起作用,有什么帮助吗?
scale(…)
的工作原理有点不同。您可以使用bufferedImage.getScaledInstance(..)
正如@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\u double\u size.png:
不幸的是,getScaleInstance()的性能非常差,如果没有问题的话 另一种方法是创建一个新的BuffereImage,并在新的BuffereImage上绘制原始的缩放版本
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表示新的BuffereImage大小,必须正确计算。
在因子缩放的情况下:
int newWidth = new Double(original.getWidth() * widthFactor).intValue();
int newHeight = new Double(original.getHeight() * heightFactor).intValue();
编辑:找到了说明性能问题的文章:提供了选择插值类型的额外灵活性
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);
所示片段说明了,而不是;这涉及到:;使用imgscalr–Java图像缩放库检查了一些相关示例。:
BufferedImage image =
Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight);
这对我来说已经足够快了。如果您不介意使用外部库,可以执行s的缩放 Thumbnailator将负责处理处理(如使用和设置适当),以便使用简单的fluent API调用调整图像大小:
BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();
虽然Thumbnailator,顾名思义,是面向缩小图像的,但它也可以很好地放大图像,在默认的大小调整器实现中使用双线性插值
免责声明:我是库的维护者。要缩放图像,您需要创建一个新图像并绘制到其中。一种方法是使用
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;
}
另一种方法是简单地将原始图像绘制到新图像中,使用缩放操作进行缩放。此方法非常相似,但它也说明了如何在最终图像中绘制所需的任何内容。(我在两种方法开始不同的地方放了一个空白行。)
附录:结果
为了说明差异,我比较了下面五种方法的结果。下面是结果的样子,放大和缩小,以及性能数据。(每次跑步的表现各不相同,因此仅将这些数字作为粗略的指南。)上图为原始图像。我把它放大两倍半
如您所见,scaleBilinear()
中使用的AffineTransformOp.filter()
,比scale2()
中的Graphics2D.drawImage()
标准绘图方法要快。此外,双三次插值速度最慢,但在扩展图像时效果最好。(就性能而言,只能将其与scaleBilinear()
和scaleNearest()。
进行比较)双线性似乎更适合缩小图像,尽管这是一个困难的要求。而最近的邻居是最快的,结果最差。双线性似乎是速度和质量之间的最佳折衷。在problem()
方法中调用的Image.getScaledInstance()
,性能非常差,返回的质量与NearestNeighbor相同。(性能编号仅用于扩展图像。)
我尝试过这种方法,但GetScaleInstance返回ToolkitImage,就我而言,它不适合我。谢谢您可以通过将其光栅复制到新的BuffereImage来将其转换为BuffereImage。对于“将图像转换为缓冲区图像”来说,当您有一个类似于:
after=…
?@Martijn的语句时,是否真的需要为after
分配所有内存?这取决于您想要加入哪个ColorModel
。它返回一个引用,因此没有额外的内存。有没有办法用所需的宽度和高度而不是比例因子来实现这一点?是的,比例参数只是x和y的新旧比例;保持它们相等以保持纵横比。我刚刚测试过。正如我所怀疑的,之后的大小相同,它只是原版的左上角四分之一。修复方法是在
之后创建时将w和h乘以比例。我认为GetScaleInstance()现在更快了,至少如果你有一张像样的图形卡,这要归功于优化的Java2D渲染管道。仅供参考,我看到了渲染提示的其他可能值。按键插值
非常有效!这在不改变图书馆的情况下对我帮助很大。同意,这是最好的解决方案,避免了使用仿射变换和其他各种方法时出现的透明度、错误翻译、错误颜色等问题。太棒了!我在这篇文章中看到的第一个解决方案让我得到了我所需要的。一个优秀的教程:在撰写本文时,最流行的答案是错误的答案。它放大图像,但返回相同大小的图像,图像丢失了3/4。这是垃圾神给的答案。很近,但有个小虫子。谢谢,@MiguelMunoz。由于我们有更多的反馈,我可以更改答案。这是一个优秀的库!与Graphics2DGreat库相比,缩略图简直太神奇了!与科特林也很相配。也似乎比其他一些选项更新。我尝试了这段代码,但没有得到显示的结果。我得到的结果是更严重的别名。如果放大浏览器中的第一个图像,直到得到与第二个图像大小相同的图像,您将更好地了解此代码生成的内容。(我尝试将生成的图像放入此注释中,但没有效果。我猜注释中不允许使用图像。)您可以尝试grph.setRenderingHint(R
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;
}
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);
}