Java 以最快和最简单的方式缩放 BufferedImage
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16497853/
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
Scale a BufferedImage the fastest and easiest way
提问by tamas.pflanzner
The task:I have some images, I scale them down, and join them to one image. But I have a little problem with the implementation:
任务:我有一些图像,我将它们缩小,然后将它们合并为一张图像。但是我在实现上有点问题:
The concrete problem:I want to resize/scale a BufferedImage. The getScaledInstance method returns an Image object, but I can't cast it to BufferedImage:
具体问题:我想调整/缩放 BufferedImage。getScaledInstance 方法返回一个 Image 对象,但我无法将其转换为 BufferedImage:
Exception in thread "main" java.lang.ClassCastException: sun.awt.image.ToolkitImage cannot be cast to java.awt.image.BufferedImage
(I don't know why is it a ToolkitImage instead of an Image...)
(我不知道为什么它是 ToolkitImage 而不是 Image...)
I found a solution:
我找到了一个解决方案:
Image tmp = bi.getScaledInstance(SMALL_SIZE, SMALL_SIZE, BufferedImage.SCALE_FAST);
BufferedImage buffered = new BufferedImage(SMALL_SIZE,SMALL_SIZE,BufferedImage.TYPE_INT_RGB);
buffered.getGraphics().drawImage(tmp, 0, 0, null);
But it's slow, and I think there should be a better way to do it.
但它很慢,我认为应该有更好的方法来做到这一点。
I need the BufferedImage, because I have to get the pixels to join the small images.
我需要 BufferedImage,因为我必须让像素加入小图像。
Is there a better (nicer/faster) way to do it?
有没有更好(更好/更快)的方法来做到这一点?
EDIT:If I cast the Image first to ToolkitImage, it has a getBufferedImage() method. But it always returns null. Do you know why?
编辑:如果我首先将图像转换为 ToolkitImage,它有一个 getBufferedImage() 方法。但它总是返回空值。你知道为什么吗?
回答by Azad
Maybe this method will help:
也许这种方法会有所帮助:
public BufferedImage resizeImage(BufferedImage image, int width, int height) {
int type=0;
type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType();
BufferedImage resizedImage = new BufferedImage(width, height,type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(image, 0, 0, width, height, null);
g.dispose();
return resizedImage;
}
Don't forget those "import" lines:
不要忘记那些“导入”行:
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
And about casting:
关于铸造:
The abstract class Image
is the superclass of all classes that represent graphical images.
We can't cast Image
to BufferedImage
because every BufferedImage
is Image
but vice versa is not true.
抽象类Image
是所有表示图形图像的类的超类。我们不能投射Image
到BufferedImage
因为每个BufferedImage
都是Image
,反之亦然不是真的。
Image im = new BufferedImage(width, height, imageType);//this is true
BufferedImage img = new Image(){//.....}; //this is wrong
回答by coobird
The Graphics
object has a method to draw an Image
while also performing a resize operation:
该Graphics
对象有一个绘制Image
while的方法,同时也执行调整大小操作:
Graphics.drawImage(Image, int, int, int, int, ImageObserver)
method can be used to specify the location along with the size of the image when drawing.
Graphics.drawImage(Image, int, int, int, int, ImageObserver)
方法可用于在绘图时指定位置以及图像的大小。
So, we could use a piece of code like this:
所以,我们可以使用这样的一段代码:
BufferedImage otherImage = // .. created somehow
BufferedImage newImage = new BufferedImage(SMALL_SIZE, SMALL_SIZE, BufferedImage.TYPE_INT_RGB);
Graphics g = newImage.createGraphics();
g.drawImage(otherImage, 0, 0, SMALL_SIZE, SMALL_SIZE, null);
g.dispose();
This will take otherImage
and draw it on the newImage
with the width and height of SMALL_SIZE
.
这将把otherImage
它绘制newImage
在宽度和高度为 的 上SMALL_SIZE
。
Or, if you don't mind using a library, Thumbnailatorcould accomplish the same with this:
或者,如果您不介意使用库,Thumbnailator可以通过以下方式完成相同的操作:
BufferedImage newImage = Thumbnails.of(otherImage)
.size(SMALL_SIZE, SMALL_SIZE)
.asBufferedImage();
Thumbnailator will also perform the resize operation quicker than using Image.getScaledInstance
while also performing higher quality resize operations than using only Graphics.drawImage
.
缩略图也将比使用更快地执行调整大小操作Image.getScaledInstance
,同时执行比仅使用更高质量的调整大小操作Graphics.drawImage
。
Disclaimer: I am the maintainer of the Thumbnailator library.
免责声明:我是缩略图库的维护者。
回答by digolloco
I get it with this method, it resizes the Image and tries to maintain the proportions:
我用这种方法得到它,它调整图像的大小并尝试保持比例:
/**
* Resizes an image using a Graphics2D object backed by a BufferedImage.
* @param srcImg - source image to scale
* @param w - desired width
* @param h - desired height
* @return - the new resized image
*/
private BufferedImage getScaledImage(BufferedImage src, int w, int h){
int finalw = w;
int finalh = h;
double factor = 1.0d;
if(src.getWidth() > src.getHeight()){
factor = ((double)src.getHeight()/(double)src.getWidth());
finalh = (int)(finalw * factor);
}else{
factor = ((double)src.getWidth()/(double)src.getHeight());
finalw = (int)(finalh * factor);
}
BufferedImage resizedImg = new BufferedImage(finalw, finalh, BufferedImage.TRANSLUCENT);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(src, 0, 0, finalw, finalh, null);
g2.dispose();
return resizedImg;
}
回答by Victor
None of these answers were fast enough for me. So I finally programmed my own procedure.
这些答案对我来说都不够快。所以我最终编写了自己的程序。
static BufferedImage scale(BufferedImage src, int w, int h)
{
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int x, y;
int ww = src.getWidth();
int hh = src.getHeight();
for (x = 0; x < w; x++) {
for (y = 0; y < h; y++) {
int col = src.getRGB(x * ww / w, y * hh / h);
img.setRGB(x, y, col);
}
}
return img;
}
回答by Bahadir Tasdemir
You can also use OpenCVJava library. It's resize operation is faster than Imgscalr's:
您还可以使用OpenCVJava 库。它的调整大小操作比 Imgscalr 快:
Test
测试
Image 5184 x 3456 scaled to 150 x 100 (this is the smaller version because original file is bigger than 2mb):
图像 5184 x 3456 缩放到 150 x 100(这是较小的版本,因为原始文件大于 2mb):
Imgscalr
图像标度器
Dependency:
依赖:
<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
Code:
代码:
BufferedImage thumbnail = Scalr.resize(img,
Scalr.Method.SPEED,
Scalr.Mode.AUTOMATIC,
150,
100);
Result image:
结果图像:
Average time: 80 millis
平均时间:80 毫秒
OpenCV
OpenCV
Dependency:
依赖:
<dependency>
<groupId>nu.pattern</groupId>
<artifactId>opencv</artifactId>
<version>2.4.9-4</version>
</dependency>
Convert BufferedImage to Mat object (have to):
将 BufferedImage 转换为 Mat 对象(必须):
BufferedImage img = ImageIO.read(image); // load image
byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer())
.getData();
Mat matImg = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3);
matImg.put(0, 0, pixels);
Code:
代码:
Imgproc.resize(matImg, resizeimage, sz);
Additional configuration (for windows):
附加配置(适用于 Windows):
Add opencv_java249.dllto your JDK's bin directory.
将opencv_java249.dll添加到 JDK 的 bin 目录。
Result image:
结果图像:
Average time: 13 millis
平均时间:13 毫秒
Overall Results
总体结果
In the test just "resize" functions times are calculated. Imgscalr resized the given image in 80 milis where OpenCV done the same task in 13 millis. You can find the whole project below here to play around of it a little bit.
在测试中,只计算“调整大小”函数的时间。Imgscalr 在 80 毫秒内调整给定图像的大小,而 OpenCV 在 13 毫秒内完成相同的任务。你可以在下面找到整个项目来稍微玩一下。
As you asked also easy way, if the performance of the Imgscalr library is good for you then it is deadly-easy. Because to use OpenCV as you see a library file must be located at your all development environments and servers. Also you have to use Mat objects.
正如您所问的,也很简单,如果 Imgscalr 库的性能对您有好处,那么它非常容易。因为如您所见,要使用 OpenCV,库文件必须位于您的所有开发环境和服务器中。您还必须使用 Mat 对象。
Whole Project
整个项目
Pom.xml:
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.btasdemir</groupId>
<artifactId>testapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testapp</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
<dependency>
<groupId>nu.pattern</groupId>
<artifactId>opencv</artifactId>
<version>2.4.9-4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>0.9</version>
</plugin>
</plugins>
</build>
</project>
App.java:
应用程序.java:
package com.btasdemir.testapp;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.imgscalr.Scalr;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws IOException
{
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File image = new File("C:\your_dir\test.jpg");
BufferedImage img = ImageIO.read(image); // load image
long startTime = System.currentTimeMillis();//imgscalr------------------------------------------------------
//resize to 150 pixels max
BufferedImage thumbnail = Scalr.resize(img,
Scalr.Method.SPEED,
Scalr.Mode.AUTOMATIC,
150,
100);
// BufferedImage thumbnail = Scalr.resize(img,
// Scalr.Method.SPEED,
// Scalr.Mode.AUTOMATIC,
// 150,
// 100,
// Scalr.OP_ANTIALIAS);
System.out.println(calculateElapsedTime(startTime));//END-imgscalr------------------------------------------------------
File outputfile = new File("C:\your_dir\imgscalr_result.jpg");
ImageIO.write(thumbnail, "jpg", outputfile);
img = ImageIO.read(image); // load image
byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer())
.getData();
Mat matImg = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3);
matImg.put(0, 0, pixels);
Mat resizeimage = new Mat();
Size sz = new Size(150, 100);
startTime = System.currentTimeMillis();//opencv------------------------------------------------------
Imgproc.resize(matImg, resizeimage, sz);
// Imgproc.resize(matImg, resizeimage, sz, 0.5, 0.5, Imgproc.INTER_CUBIC);
System.out.println(calculateElapsedTime(startTime));//END-opencv------------------------------------------------------
Highgui.imwrite("C:\your_dir\opencv_result.jpg", resizeimage);
}
protected static long calculateElapsedTime(long startTime) {
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
return elapsedTime;
}
}
回答by VocoJax
public static double[] reduceQuality(int quality, int width, int height) {
if(quality >= 1 && quality <= 100) {
double[] dims = new double[2];
dims[0] = width * (quality/100.0);
dims[1] = height * (quality/100.0);
return dims;
} else if(quality > 100) {
return new double[] { width, height };
} else {
return new double[] { 1, 1 };
}
}
public static byte[] resizeImage(byte[] data, int width, int height) throws Exception {
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(data));
BufferedImage bo = resizeImage(bi, width, height);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(bo, "jpg", bos);
bos.close();
return bos.toByteArray();
}
private static BufferedImage resizeImage(BufferedImage buf, int width, int height) {
final BufferedImage bufImage = new BufferedImage(width, height,
(buf.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB));
final Graphics2D g2 = bufImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g2.drawImage(buf, 0, 0, width, height, null);
g2.dispose();
return bufImage;
}
This is taken straight from imgscalr at https://github.com/rkalla/imgscalr/blob/master/src/main/java/org/imgscalr/Scalr.java
这直接取自https://github.com/rkalla/imgscalr/blob/master/src/main/java/org/imgscalr/Scalr.java 的imgscalr
My average time reducing the quality of an image of 8mb with dimensions of 5152x3864 was ~800ms.
我降低尺寸为 5152x3864 的 8mb 图像质量的平均时间约为 800 毫秒。
No dependencies. I hate them. Sometimes.
没有依赖性。我恨他们。有时。
THIS WILL ONLY WORK WITH jpg IMAGES. As far as I'm concerned.
这仅适用于 jpg 图像。就我而言。
Example:
例子:
byte[] of = Files.readAllBytes(Paths.get("/home/user/Pictures/8mbsample.jpg"));
double[] wh = ImageUtil.reduceQuality(2, 6600, 4950);
long start = System.currentTimeMillis();
byte[] sof = ImageUtil.resizeImage(of, (int)wh[0], (int)wh[1]);
long end = System.currentTimeMillis();
if(!Files.exists(Paths.get("/home/user/Pictures/8mbsample_scaled.jpg"))) {
Files.createFile(Paths.get("/home/user/Pictures/8mbsample_scaled.jpg"), Util.getFullPermissions());
}
FileOutputStream fos = new FileOutputStream("/home/user/Pictures/8mbsample_scaled.jpg");
fos.write(sof); fos.close();
System.out.println("Process took: " + (end-start) + "ms");
Output:
输出:
Process took: 783ms