Загрузка больших изображений в виде эскизов без проблем с памятью в Java?

Я пытаюсь позволить пользователю загружать изображения со своего жесткого диска и представлять их визуально в графическом интерфейсе в виде списка миниатюр (JPanels с значками, добавленными в JList). В настоящее время я использую ImageIO.read() для получения BufferedImage и использования getScaledInstance для каждого изображения (слышал, что вы не предполагали использовать это, хотя).

Он работает достаточно хорошо с небольшими изображениями, но загружает более четырех фотографий (5000x3000 или некоторые из них), и я получаю "java.lang.OutOfMemoryError: Java heap space". Ссылка на полный размер BufferedImage не сохраняется, поэтому я решил, что сборщик мусора избавится от него и сохранит только масштабированное изображение (которое не должно занимать много памяти), но это не похоже на это. Я также прогоняю getRuntime(). Gc() и System.gc(), без каких-либо последствий.

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

Текущий код:

BufferedImage unscaledImage = ImageIO.read(imageFile);
int unscaledHeight = unscaledImage.getHeight();
int unscaledWidth = unscaledImage.getWidth();
int imageRatio = unscaledHeight/unscaledWidth;
if (imageRatio >= 1) {
 return new ImageIcon(unscaledImage.getScaledInstance(width,-1,Image.SCALE_FAST));
} else {
 return new ImageIcon(unscaledImage.getScaledInstance(-1,height,Image.SCALE_FAST));
}
2 ответа

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

Я бы рекомендовал использовать устройство для чтения изображений. Например, чтобы получить считыватель изображений, код будет выглядеть примерно так:

// Create an image input stream on the image
 ImageInputStream iis = ImageIO.createImageInputStream(o);
 // Find all image readers that recognize the image format
 Iterator iter = ImageIO.getImageReaders(iis);
 if (!iter.hasNext()) {
 // No readers found
 return null;
 }
 // Use the first reader
 ImageReader reader = (ImageReader)iter.next();

От: http://www.exampledepot.com/egs/javax.imageio/DiscType.html

У вас есть ImageReader, вы можете получить аспектный рацион, вызвав reader.getAspectRatio()

Я не уверен, как вы могли бы перейти от ImageReader к миниатюре.


JVM имеет неприятную привычку кэшировать изображения. Один из способов обойти это:

  • Создайте InputStream, указывающий на изображение.
  • Прочитайте все данные изображения в byte[] (используя стандартные API ввода-вывода - вне imageio и т.д.).
  • Создайте ******************** из byte[].
  • Используйте ******************** как источник для ImageIO.read(InputStream).

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

Помните, я не могу найти документацию, подтверждающую то, что я говорю. Это только из прошлого опыта.

licensed under cc by-sa 3.0 with attribution.