从文件中读/写图像到 BufferedImage 的最快方法是什么?

10
  1. 在Java/Grails中,从文件读取图像到BufferedImage的最快方法是什么?
  2. 在Java/Grails中,将BufferedImage写入文件的最快方法是什么?

我的翻译(read):

byte [] imageByteArray = new File(basePath+imageSource).readBytes()
InputStream inStream = new ByteArrayInputStream(imageByteArray)
BufferedImage bufferedImage = ImageIO.read(inStream)

我的变量(写):

BufferedImage bufferedImage = // some image
def fullPath = // image page + file name
byte [] currentImage

try{

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write( bufferedImage, "jpg", baos );
    baos.flush();
    currentImage = baos.toByteArray();
    baos.close();

    }catch(IOException e){
        System.out.println(e.getMessage());
    }       
   }    


def newFile = new FileOutputStream(fullPath)
newFile.write(currentImage)
newFile.close()
3个回答

9
您的解决方案基本上是读两次字节,一次从文件中读取,一次从ByteArrayInputStream读取。不要这样做。使用Java 7可以这样读取:
BufferedImage bufferedImage = ImageIO.read(Files.newInputStream(Paths.get(basePath + imageSource)));

使用Java 7编写

ImageIO.write(bufferedImage, "jpg", Files.newOutputStream(Paths.get(fullPath)));

调用 Files.newInputStream 方法将返回一个 ChannelInputStream 对象,该对象(据我所知)不具备缓存功能。您需要对其进行包装。

new BufferedInputStream(Files.newInputStream(...));

为了减少磁盘的IO调用次数,这取决于你使用它的方式。

我看到Java Toolkit在读取方面要快得多,这是真的吗?如果是这样,您能否修改您的答案? - user2722142
1
@stephan1001 这些是Java 7类。包名为java.nio.file.Filesjava.nio.file.Paths - Sotirios Delimanolis
1
@stephan1001 一个未缓冲的FileInputStream将会产生大量IO操作系统调用来从磁盘读取数据。通过缓存,您可以读取比所需更多的数据,以便减少对磁盘的调用次数。在这种情况下,这是有意义的,因为您最终将读取整个文件。 - Sotirios Delimanolis
很好,那我对于写操作也需要改些什么吗? - user2722142
1
@stephan1001 使用相应的BufferedOutputStream以同样的方式。 - Sotirios Delimanolis
显示剩余8条评论

5

虽然我来晚了,但是无论如何...

实际上,使用:

ImageIO.read(new File(basePath + imageSource));

并且
ImageIO.write(bufferedImage, "jpeg", new File(fullPath));

如果使用分析器进行测试,这些变体可能会更快。这是因为这些变体在幕后使用了基于RandomAccessFileImageInputStream/ImageOutputStream实现,而InputStream/OutputStream版本默认情况下将使用一个磁盘备份的可寻址流实现。磁盘备份涉及将整个流的内容写入临时文件,并可能从中读取(这是因为图像I/O通常受益于非线性数据访问)。

如果想避免使用基于流的版本进行额外的I/O操作,代价是使用更多的内存,可以调用名称不明确的ImageIO.setUseCache(false),以禁用可寻址输入流的磁盘缓存。如果处理非常大的图像,则显然不是一个好主意。


所以这个版本 ImageIO.read(new File(basePath + imageSource)); 比 ImageIO.read(new BufferedInputStream(Files.newInputStream(Paths.get(basePath + imageSource)))); 更快,这是真的吗? - user2722142
1
@stephan1001 理论上是可以的(假设useCache默认设置为false)。但是我建议你进行测量以确保。 :-) - Harald K
1
只是为了好玩,我写了一个示例程序,使用文件版本、流版本和无磁盘缓存的流版本读取了一个中等大小的JPEG图像100次。时间是平均时间。 使用文件的时间:36.76毫秒 使用流的时间:40.32毫秒 使用流(useCache==false)的时间:37.63毫秒 JDK 1.6.0_26-b03-383在OS X上运行,2.66GHz i7 + SSD。你的结果可能会有所不同... - Harald K
@stephan1001 相对于什么?我不认为我理解这个问题...读取JPEG并重新编码为JPEG将始终导致一些质量损失,但这与ImageIO或BufferedImages无关。 - Harald K
1
问题是:当我将一个jpg图像加载到BufferedImage中,并从这个BufferedImage保存回jpg文件时,是否会导致质量损失? - user2722142
显示剩余2条评论

0
你写作的能力已经很不错了。只是不要使用Intermediate ByteArrayOutputStream,它会成为你代码中的一个巨大瓶颈。相反,应该将FileOutputStream包装在BufferedOutputStream中,并执行相同的操作。
同样的道理也适用于读取。请移除Intermediate ByteArrayInputStream。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接