以最快最简单的方式扩展BufferedImage

任务:我有一些图像,我缩小它们,并将它们连接到一个图像。 但我对实现有一点问题:

具体问题:我想resize/缩放BufferedImage。 getScaledInstance方法返回一个Image对象,但我无法将其强制转换为BufferedImage:

Exception in thread "main" java.lang.ClassCastException: sun.awt.image.ToolkitImage cannot be cast to java.awt.image.BufferedImage 

(我不知道为什么它是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); 

但它很慢,我认为应该有更好的方法来做到这一点。

我需要BufferedImage,因为我必须让像素加入小图像。

有更好的(更好/更快)的方式吗?

编辑:如果我首先将Image转换为ToolkitImage,它有一个getBufferedImage()方法。 但它总是返回null。 你知道为什么吗?

Graphics对象有一个绘制Image的方法,同时还执行resize操作:

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(); 

这将采用otherImage并在newImage上绘制它,其宽度和高度为SMALL_SIZE


或者,如果您不介意使用库,则Thumbnailator可以完成相同的操作:

 BufferedImage newImage = Thumbnails.of(otherImage) .size(SMALL_SIZE, SMALL_SIZE) .asBufferedImage(); 

Thumbnailator还将比使用Image.getScaledInstance更快地执行resize操作,同时还执行比仅使用Graphics.drawImage更高质量的resize操作。

免责声明:我是Thumbnailator库的维护者。

我用这个方法得到它,它调整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; } 

您还可以使用OpenCV Java库。 resize的操作比Imgscalr更快:

测试

图像5184 x 3456缩放到150 x 100(这是较小的版本,因为原始文件大于2mb): 在此处输入图像描述

Imgscalr

相关性:

  org.imgscalr imgscalr-lib 4.2  

码:

 BufferedImage thumbnail = Scalr.resize(img, Scalr.Method.SPEED, Scalr.Mode.AUTOMATIC, 150, 100); 

结果图片:

在此处输入图像描述

平均时间:80毫升

OpenCV的

相关性:

  nu.pattern opencv 2.4.9-4  

将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); 

码:

 Imgproc.resize(matImg, resizeimage, sz); 

其他配置(对于Windows):

将opencv_java249.dll添加到JDK的bin目录中。

结果图片:

在此处输入图像描述

平均时间:13毫升

总体结果

在测试中,只计算“resize”函数的时间。 Imgscalr在80 milis中调整给定图像的大小,其中OpenCV在13毫米中完成相同的任务。 你可以在这里找到整个项目来玩一下它。

正如你所说的那样,如果Imgscalr库的性能对你有好处,那么它就是致命的。 因为要使用OpenCV,所以库文件必须位于所有开发环境和服务器上。 您还必须使用Mat对象。

整个项目

pom.xml中:

  4.0.0 com.btasdemir testapp 0.0.1-SNAPSHOT jar testapp http://maven.apache.org  UTF-8    junit junit 3.8.1 test   org.imgscalr imgscalr-lib 4.2   nu.pattern opencv 2.4.9-4      org.bytedeco javacpp 0.9     

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; } } 

这些答案对我来说都不够快。 所以我最终编写了自己的程序。

 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; } 

使用imgscalr – Java Image Scaling Library :

 BufferedImage image = Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight); 

这对我来说足够快了。

也许这种方法会有所帮助:

 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; } 

不要忘记那些“导入”行:

 import java.awt.Graphics2D; import java.awt.image.BufferedImage; 

关于铸造:

抽象类Image是表示图形图像的所有类的超类。 我们无法将ImageBufferedImage因为每个BufferedImage都是Image ,反之亦然。

 Image im = new BufferedImage(width, height, imageType);//this is true BufferedImage img = new Image(){//.....}; //this is wrong 
 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; } 

这可以直接来自imgscalr, url是https://github.com/rkalla/imgscalr/blob/master/src/main/java/org/imgscalr/Scalr.java

我降低图像质量的平均时间[5152×3864]是~800ms。

没有依赖。 我恨他们。 有时。

这只会与jpg图像一起使用。 就我而言。

例:

 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"); 

输出:

 Process took: 783ms