1

I am using javax.imageio.ImageIO.read() which almost takes 9 seconds to read an image having size 5 mb and located at windows temp location, PFB the screenshot of Jprofiler. i want a more efficient way which can decrease the time to at least 2-3 seconds.

enter image description here

The file is coming as a org.springframework.web.multipart.MultipartFile request through rest endpoint and then getting copied to the windows temp location for further proccessing. The complete code block:

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

While googling i found a 3rd party library which claims to be more efficient than javax.imageio.ImageIO but its a paid one: https://files.idrsolutions.com/maven/site/jdeli/apidocs/com/idrsolutions/image/JDeli.html#read(java.io.File).

Can anyone suggest a better and efficient way to read the image file.

Tasos P.
  • 3,994
  • 2
  • 21
  • 41
rohit
  • 177
  • 4
  • 12
  • 1
    Please show **how** you are loading the image. Also, asking for library suggestions is off-topic. – Mark Rotteveel Jul 11 '20 at 17:57
  • Can you post a link to the image so that I can see how long it takes to load on my machine? – Abra Jul 11 '20 at 18:09
  • https://stackoverflow.com/questions/10593935/java-load-images-faster – bigbounty Jul 11 '20 at 18:15
  • @Mark edited the quest for more clarity. – rohit Jul 11 '20 at 18:19
  • Could you avoid making a temp file? Perhaps by using something like BufferedImage originalImage = ImageIO.read(theRequest.getInputStream()); – rickz Jul 11 '20 at 18:59
  • Please, update the question with the Java version used in your application. – Ilya Lapitan Jul 11 '20 at 19:01
  • i am using jdk-14.0.1 @rickz i cannot avoid copying the file to temp dir as i need the original file. – rohit Jul 11 '20 at 19:33
  • What is the actual use case? Are you loading the image in order to generate thumbnails? Batch conversion/editing? There are performance techniques you could use for certain use cases. – Tasos P. Jul 11 '20 at 20:02
  • 1
    @Cascader i need to resize these images using `java.awt.Graphics2D` . i have a bunch of 6-7 images and every images will be resized in 2 variants. First variant would be **350*300 (for thumbnail)** and another variant would be **100*100**. Both Variant of images will be then stored to aws s3. i have everything ready but the only concern is the image processing(especially `ImageIO.read`) takes more than 10 seconds which we cannot afford. – rohit Jul 11 '20 at 21:59
  • 2
    (1-) you asked this question yesterday and where asked 1) to post your [mre] demonstrating the problem and 2) for a link to the image. You have repeated the same question and not provided any additional information. Your question is about ImageIO, so create an example only using ImageIO. The extra code you posted has nothing to do with ImageIO and I would guess that is where the real problem is. – camickr Jul 11 '20 at 23:19
  • @camickr I knew this question was familiar! However, I couldn't find it. Looks like the OP deleted it and re-posted it as a new question. – Abra Jul 12 '20 at 08:18
  • It is worth checking results on latest JDK. If loading progressive JPG see also https://stackoverflow.com/questions/29705050/reading-a-progressively-encoded-9000x9000-jpeg-in-java-takes-1-minute/29750246 – DuncG Jul 13 '20 at 07:11
  • 1
    Thanks for all your valuable suggestions.I tried all solutions given by you guys. Instead of using imageIO i used `Image image = new ImageIcon(file.getAbsolutePath()).getImage();` for file reading and it reduced the time by almost half. – rohit Jul 14 '20 at 10:56

3 Answers3

3

Your time of 9 seconds looks bad - I wonder if you've included all code and timing of just ImageIO.read(File)?

However I found that ImageIO.read(File) quite slow on non-SSD drives (ie 500ms to 1 second). When testing NAS and HDD drives on my PC 4-6MB some image reads used InputStream.read() up to 1000 times. I found that that elapsed time for loads are reduced if entire image was read into memory before calling 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);
    }
}

This solution can slow down access time on SSD drives a little (which was not an issue for me at ~ 20ms), but the margin of gain on NAS/network drive was 50-250ms on my PC.

Obviously the timings you observe would depend on your hardware, image file size and types but it may be worth trying in your particular case.

If you need to load several images you can split the file I/O and ImageIO calls to separate threads for a small extra gain, but at cost of extra complexity and memory footprint.

DuncG
  • 12,137
  • 2
  • 21
  • 33
2

First of all, I have to say that 9 seconds for a 5Mb image seem too much for modern hardware. Sure, complex images or sophisticated formats (i.e. multi-page tiff) can take longer but my low-end PC needs 1.3 seconds for a 5mb JPG. I would definitely try to identify hardware and/or OS level issues.

Having that said, here is my proposed solution for faster image loading since the use case is to generate thumbnails. The key point here is that we can instruct the image reader to only read part of the image from disk, since we need less image data to generate a thumbnail. This technique (sub-sampling) can be realized for JPG as:

Iterator<ImageReader> imageReadersByFormatName = ImageIO.getImageReadersByFormatName("jpg");
ImageReader ir = imageReadersByFormatName.next(); //There should be at least the default com.sun.imageio.plugins.jpeg.JPEGImageReader installed
ImageReadParam defaultReadParam = ir.getDefaultReadParam();
defaultReadParam.setSourceSubsampling(8, 8, 0, 0); //Configure the reader to read every 8th row / 8th column of the image
ir.setInput(ImageIO.createImageInputStream(new FileInputStream(IMAGE_PATH)), true);
BufferedImage image = ir.read(0, defaultReadParam);

You will have to determine the optimal values for X-axis and Y-axis sub-sampling (first two parameters to setSourceSubsampling()). This will be a speed/image quality trade-off.

There are more configuration options for ImageReadParam.

Bonus material: Since you are using AWS, you might consider this approach in order to delegate this task to a lambda function (i.e. if this task can be done asynchronously).

Tasos P.
  • 3,994
  • 2
  • 21
  • 41
1

I would recommend trying to use classes from the javax.imageio.stream package:

  • if you want to keep your solution with the temp file:
FileInputStream fileInputStream = new FileInputStream(uploadedImageFile);
FileCacheImageInputStream fileCache = new FileCacheImageInputStream​(fileInputStream, new File(tempPath));
BufferedImage bufferedImage =  ImageIO.read(fileCache);
  • if you have enough memory you can use solution with the memory cache:
FileInputStream fileInputStream = new FileInputStream(uploadedImageFile);
MemoryCacheImageInputStream memoryCache = new MemoryCacheImageInputStream(fileInputStream);
BufferedImage bufferedImage =  ImageIO.read(memoryCache);
Ilya Lapitan
  • 986
  • 15
  • 23