使用Java读取图像文件的有效方法

使用Java读取图像文件的有效方法,java,javax.imageio,Java,Javax.imageio,我使用的是javax.imageio.imageio.read,读取一个大小为5MB、位于windows temp位置的图像几乎需要9秒钟,这是Jprofiler的屏幕截图。我想要一个更有效的方法,可以减少时间至少2-3秒 该文件作为org.springframework.web.multipart.MultipartFile请求通过rest端点发送,然后被复制到windows临时位置以进行进一步处理。完整的代码块: String fileName = StringUtils.cleanPath

我使用的是javax.imageio.imageio.read,读取一个大小为5MB、位于windows temp位置的图像几乎需要9秒钟,这是Jprofiler的屏幕截图。我想要一个更有效的方法,可以减少时间至少2-3秒

该文件作为org.springframework.web.multipart.MultipartFile请求通过rest端点发送,然后被复制到windows临时位置以进行进一步处理。完整的代码块:

String fileName = StringUtils.cleanPath(multipartFile.getOriginalFilename());
Path destinationPath = Paths.get(System.getProperty("java.io.tmpdir"));
String tempPath = destinationPath.resolve(fileName).toString();
File uploadedImageFile = new File(tempPath);
File originalFileInTempLocation = file.transferTo(uploadedImageFile);
BufferedImage originalImage = ImageIO.read(originalFileInTempLocation);
在谷歌搜索时,我发现了一个声称比javax.imageio.imageio更高效的第三方库,但它是付费的:


有谁能推荐一种更好、更有效的方法来读取图像文件。

我建议尝试使用javax.imageio.stream包中的类:

如果要将解决方案保留在临时文件中: FileInputStream FileInputStream=新文件InputStreamUploadedImageFile; FileCacheMageInputStream fileCache=新的FileCacheMageInputStream​fileInputStream,新的FiletempPath; BuffereImage BuffereImage=ImageIO.readfileCache; 如果您有足够的内存,您可以使用带有内存缓存的解决方案: FileInputStream FileInputStream=新文件InputStreamUploadedImageFile; MemoryCacheMageInputStream memoryCache=新的MemoryCacheMageInputStreamFileInputStream; BuffereImage BuffereImage=ImageIO.readmemoryCache;
我建议尝试使用javax.imageio.stream包中的类:

如果要将解决方案保留在临时文件中: FileInputStream FileInputStream=新文件InputStreamUploadedImageFile; FileCacheMageInputStream fileCache=新的FileCacheMageInputStream​fileInputStream,新的FiletempPath; BuffereImage BuffereImage=ImageIO.readfileCache; 如果您有足够的内存,您可以使用带有内存缓存的解决方案: FileInputStream FileInputStream=新文件InputStreamUploadedImageFile; MemoryCacheMageInputStream memoryCache=新的MemoryCacheMageInputStreamFileInputStream; BuffereImage BuffereImage=ImageIO.readmemoryCache;
首先,我不得不说,对于现代硬件来说,5Mb图像的9秒钟似乎太长了。当然,复杂的图像或复杂的格式(如多页tiff)可能需要更长的时间,但我的低端PC需要1.3秒才能获得5mb JPG。我肯定会尝试确定硬件和/或操作系统级别的问题

话虽如此,这里是我建议的更快图像加载的解决方案,因为用例是生成缩略图。这里的关键点是,我们可以指示图像读取器仅从磁盘读取部分图像,因为生成缩略图需要较少的图像数据。这种技术可以通过以下方式为JPG实现子采样:

迭代器imageReadersByFormatName=ImageIO.getImageReadersByFormatNamejpg; ImageReader ir=imageReadersByFormatName.next//至少应安装默认的com.sun.imageio.plugins.jpeg.JPEGImageReader ImageReadParam defaultReadParam=ir.getDefaultReadParam; defaultReadParam.setSourceSubsampling8,8,0,0//将读卡器配置为每隔第8行/第8列读取一次图像 ir.setInputImageIO.createImageInputStreamnew文件InputStreamImage_路径,true; BuffereImage image=ir.read0,defaultReadParam; 您必须确定X轴和Y轴子采样的最佳值设置资源子采样的前两个参数。这将是速度/图像质量的权衡

有更多的配置选项可供选择


奖励材料:既然你正在使用AWS,你可以考虑把这个任务委托给lambda函数,也就是说,如果这个任务可以异步完成。

首先,我不得不说5MB图像的9秒对于现代硬件来说似乎太多了。当然,复杂的图像或复杂的格式(如多页tiff)可能需要更长的时间,但我的低端PC需要1.3秒才能获得5mb JPG。我肯定会尝试确定硬件和/或操作系统级别的问题

话虽如此,这里是我建议的更快图像加载的解决方案,因为用例是生成缩略图。这里的关键点是,我们可以指示图像读取器仅从磁盘读取部分图像,因为生成缩略图需要较少的图像数据。这种技术可以通过以下方式为JPG实现子采样:

迭代器imageReadersByFormatName=ImageIO.getImageReadersByFormatNamejpg; ImageReader ir=imageReadersByFormatName.next//至少应安装默认的com.sun.imageio.plugins.jpeg.JPEGImageReader ImageReadParam defaultReadParam=ir.getDefaultReadParam; defaultReadParam.setSourceSubsampling8,8,0,0//将读卡器配置为每隔第8行/第8列读取一次图像 ir.setInputImageIO.createImageInputStreamnew文件InputStreamImage_路径,true; BuffereImage image=ir.read0,defaultReadParam; 您必须确定X轴和Y轴子采样的最佳值设置资源子采样的前两个参数。这将是速度/图像质量的权衡

有更多的配置选项可供选择< /p>

奖励材料:既然你正在使用AWS,你可以考虑把这个任务委托给lambda函数,也就是说,如果这个任务可以异步完成。

你的9秒的时间看起来很糟糕。我想知道你是否包含了IMAIIO?Read Frpult/

的所有代码和时间? 然而,我发现ImageIO.readFile在非SSD驱动器(即500毫秒到1秒)上的速度非常慢。在我的PC上测试NAS和HDD驱动器时,名为InputStream.read的4-6MB映像最多可读取1000次,如果在调用ImageIO.read之前将整个映像读取到内存中,则加载所用的时间会减少:

static BufferedImage load(File f) throws IOException
{
    byte[] bytes = Files.readAllBytes(f.toPath());
    try (InputStream is = new ByteArrayInputStream(bytes))
    {
        return ImageIO.read(is);
    }
}
此解决方案可以稍微降低SSD驱动器上的访问时间,这对我来说在20毫秒左右不是问题,但在我的电脑上,NAS/网络驱动器上的增益幅度为50-250毫秒

显然,您观察到的时间取决于您的硬件、图像文件大小和类型,但在您的特定情况下,可能值得尝试


如果您需要加载多个映像,您可以将文件I/O和ImageIO调用拆分为单独的线程,以获得一点额外收益,但代价是额外的复杂性和内存占用。

您9秒的时间看起来很糟糕-我想知道您是否只包含了ImageIO.readFile的所有代码和计时

然而,我发现ImageIO.readFile在非SSD驱动器(即500毫秒到1秒)上的速度非常慢。在我的PC上测试NAS和HDD驱动器时,名为InputStream.read的4-6MB映像最多可读取1000次,如果在调用ImageIO.read之前将整个映像读取到内存中,则加载所用的时间会减少:

static BufferedImage load(File f) throws IOException
{
    byte[] bytes = Files.readAllBytes(f.toPath());
    try (InputStream is = new ByteArrayInputStream(bytes))
    {
        return ImageIO.read(is);
    }
}
此解决方案可以稍微降低SSD驱动器上的访问时间,这对我来说在20毫秒左右不是问题,但在我的电脑上,NAS/网络驱动器上的增益幅度为50-250毫秒

显然,您观察到的时间取决于您的硬件、图像文件大小和类型,但在您的特定情况下,可能值得尝试


如果需要加载多个映像,您可以将文件I/O和ImageIO调用拆分为单独的线程,以获得一点额外收益,但代价是额外的复杂性和内存占用。

请说明如何加载映像。此外,询问库的建议也是离题的。你能发布一个到图像的链接,这样我就可以看到加载到我的机器上需要多长时间吗?@Cascader我需要使用java.awt.Graphics2D调整这些图像的大小。我有一堆6-7张图片,每一张图片都有两个版本。对于缩略图,第一个变量是350*300,另一个变量是100*100。这两种不同的图像随后将存储到aws s3。我已经准备好了所有的东西,但唯一担心的是图像处理,尤其是ImageIO.read需要10秒以上的时间,我们负担不起。1-你昨天问了这个问题,要求1发布你的演示问题,2获取图像链接。您重复了相同的问题,没有提供任何其他信息。您的问题是关于ImageIO的,因此请仅使用ImageIO创建一个示例。你发布的额外代码与ImageIO无关,我想这才是真正的问题所在。谢谢你的宝贵建议。我尝试了你们给出的所有解决方案。我没有使用imageIO,而是使用Image Image=new-ImageIconfile.getAbsolutePath.getImage;用于文件读取,它将时间减少了近一半。请显示如何加载图像。此外,询问库的建议也是离题的。你能发布一个到图像的链接,这样我就可以看到加载到我的机器上需要多长时间吗?@Cascader我需要使用java.awt.Graphics2D调整这些图像的大小。我有一堆6-7张图片,每一张图片都有两个版本。对于缩略图,第一个变量是350*300,另一个变量是100*100。这两种不同的图像随后将存储到aws s3。我已经准备好了所有的东西,但唯一担心的是图像处理,尤其是ImageIO.read需要10秒以上的时间,我们负担不起。1-你昨天问了这个问题,要求1发布你的演示问题,2获取图像链接。您重复了相同的问题,没有提供任何其他信息。您的问题是关于ImageIO的,因此请仅使用ImageIO创建一个示例。你发布的额外代码与ImageIO无关,我想这才是真正的问题所在。谢谢你的宝贵建议。我尝试了你们给出的所有解决方案。我没有使用imageIO,而是使用Image Image=new-ImageIconfile.getAbsolutePath.getImage;用于文件读取,它将时间减少了近一半。