Эффективный способ чтения файла изображения с использованием Java - PullRequest
1 голос
/ 11 июля 2020

Я использую javax.imageio.ImageIO.read(), что почти занимает 9 секунд, чтобы прочитать изображение размером 5 мб и расположенное в windows временном местоположении, PFB снимок экрана Jprofiler. Мне нужен более эффективный способ, который может уменьшить время как минимум до 2-3 секунд.

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

Кто-нибудь может предложить лучший и эффективный способ чтения файла изображения.

Ответы [ 3 ]

3 голосов
/ 12 июля 2020

Ваше время в 9 секунд выглядит плохо - интересно, включили ли вы весь код и время только ImageIO.read(File)?

Однако я обнаружил, что ImageIO.read(File) довольно медленно работает на дисках без SSD (ie 500 мс до 1 секунды). При тестировании NAS и HDD на моих образах P C 4-6 МБ, называемых InputStream.read (), до 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 мсек на моем P C.

Очевидно, что время, которое вы наблюдаете, будет зависеть от вашего оборудования, размера и типов файлов изображений, но, возможно, стоит попробовать в вашем конкретном случае.

Если вам нужно загрузить несколько изображений, вы можете разделите файловый ввод-вывод и вызовы ImageIO на отдельные потоки для небольшого дополнительного выигрыша, но за счет дополнительной сложности и объема памяти.

2 голосов
/ 12 июля 2020

Прежде всего, я должен сказать, что 9 секунд для образа размером 5Мб кажутся слишком большими для современного оборудования. Конечно, сложные изображения или сложные форматы (например, многостраничные tiff) могут занять больше времени, но моему младшему P C требуется 1,3 секунды для 5 МБ JPG. Я бы определенно попытался выявить проблемы на уровне оборудования и / или ОС.

С учетом сказанного, вот мое предлагаемое решение для более быстрой загрузки изображений, так как вариант использования - создание эскизов. Ключевым моментом здесь является то, что мы можем проинструктировать программу чтения изображений читать только часть изображения с диска, поскольку нам нужно меньше данных изображения для создания эскиза. Этот метод (подвыборка) может быть реализован для JPG как:

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

Вам нужно будет определить оптимальные значения для подвыборки по оси X и оси Y (первые два параметра до setSourceSubsampling() ). Это будет компромисс между скоростью и качеством изображения.

Существуют дополнительные параметры конфигурации для ImageReadParam .

Дополнительный материал: поскольку вы используете AWS, вы мог бы рассмотреть этот подход , чтобы делегировать эту задачу лямбда-функции (т. е. если эта задача может быть выполнена асинхронно).

1 голос
/ 11 июля 2020

Я бы рекомендовал попробовать использовать классы из пакета javax.imageio.stream:

  • , если вы хотите сохранить свое решение с временным файлом :
FileInputStream fileInputStream = new FileInputStream(uploadedImageFile);
FileCacheImageInputStream fileCache = new FileCacheImageInputStream​(fileInputStream, new File(tempPath));
BufferedImage bufferedImage =  ImageIO.read(fileCache);
  • если у вас достаточно памяти, вы можете использовать решение с кешем памяти :
FileInputStream fileInputStream = new FileInputStream(uploadedImageFile);
MemoryCacheImageInputStream memoryCache = new MemoryCacheImageInputStream(fileInputStream);
BufferedImage bufferedImage =  ImageIO.read(memoryCache);
...