tam*_*ner 22 java image image-processing image-scaling
任务: 我有一些图像,我缩小它们,并将它们连接到一个图像.但是我对实现有一点问题:
具体问题: 我想调整大小/缩放BufferedImage.getScaledInstance方法返回一个Image对象,但我无法将其强制转换为BufferedImage:
Exception in thread "main" java.lang.ClassCastException: sun.awt.image.ToolkitImage cannot be cast to java.awt.image.BufferedImage
Run Code Online (Sandbox Code Playgroud)
(我不知道为什么它是ToolkitImage而不是Image ...)
我找到了解决方案:
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);
Run Code Online (Sandbox Code Playgroud)
但它很慢,我认为应该有更好的方法来做到这一点.
我需要BufferedImage,因为我必须让像素加入小图像.
有更好的(更好/更快)的方式吗?
编辑: 如果我首先将Image转换为ToolkitImage,它有一个getBufferedImage()方法.但它总是返回null.你知道为什么吗?
coo*_*ird 31
该Graphics对象有一个方法来绘制一段Image时间也执行调整大小操作:
Graphics.drawImage(Image, int, int, int, int, ImageObserver) 方法可用于在绘制时指定位置以及图像的大小.
所以,我们可以像这样使用一段代码:
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();
Run Code Online (Sandbox Code Playgroud)
这将采取otherImage和绘制它newImage的宽度和高度SMALL_SIZE.
或者,如果您不介意使用库,Thumbnailator可以完成相同的操作:
BufferedImage newImage = Thumbnails.of(otherImage)
.size(SMALL_SIZE, SMALL_SIZE)
.asBufferedImage();
Run Code Online (Sandbox Code Playgroud)
Thumbnailator还将比使用更快地执行调整大小操作,Image.getScaledInstance同时还执行比仅使用更高质量的调整大小操作Graphics.drawImage.
免责声明:我是Thumbnailator库的维护者.
小智 9
我用这个方法得到它,它调整Image的大小并尝试保持比例:
/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用OpenCV Java库.调整大小的操作比Imgscalr更快:
图像5184 x 3456缩放到150 x 100(这是较小的版本,因为原始文件大于2mb):

Imgscalr
相关性:
<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
码:
BufferedImage thumbnail = Scalr.resize(img,
Scalr.Method.SPEED,
Scalr.Mode.AUTOMATIC,
150,
100);
Run Code Online (Sandbox Code Playgroud)
结果图片:
平均时间:80毫升
OpenCV的
相关性:
<dependency>
<groupId>nu.pattern</groupId>
<artifactId>opencv</artifactId>
<version>2.4.9-4</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
将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);
Run Code Online (Sandbox Code Playgroud)
码:
Imgproc.resize(matImg, resizeimage, sz);
Run Code Online (Sandbox Code Playgroud)
其他配置(对于Windows):
将opencv_java249.dll添加到JDK的bin目录中.
结果图片:
平均时间:13毫升
在测试中,只计算"调整大小"函数的时间.Imgscalr在80 milis中调整给定图像的大小,其中OpenCV在13毫米中完成相同的任务.你可以在这里找到整个项目来玩一下它.
正如您所说的那样,如果Imgscalr库的性能对您有好处,那么它就是致命的.因为要使用OpenCV,所以库文件必须位于所有开发环境和服务器上.您还必须使用Mat对象.
整个项目
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>
Run Code Online (Sandbox Code Playgroud)
App.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;
}
}
Run Code Online (Sandbox Code Playgroud)
这些答案对我来说都不够快.所以我最终编写了自己的程序.
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;
}
Run Code Online (Sandbox Code Playgroud)
也许这个方法会有所帮助:
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;
}
Run Code Online (Sandbox Code Playgroud)
不要忘记那些“导入”行:
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
Run Code Online (Sandbox Code Playgroud)
关于选角:
抽象类Image是所有表示图形图像的类的超类。我们不能转换Image为,BufferedImage因为每个BufferedImage都是Image,但反之亦然。
Image im = new BufferedImage(width, height, imageType);//this is true
BufferedImage img = new Image(){//.....}; //this is wrong
Run Code Online (Sandbox Code Playgroud)