Как обрабатывать огромные данные / изображения в оперативной памяти в Java? - PullRequest
11 голосов
/ 14 мая 2019

Сводка

  1. Я читаю большой двоичный файл, который содержит данные изображения.
  2. Кумулятивный анализ Cut Count выполняется для данных [Требуется другой массив с таким же размером, как у изображения].
  3. Данные растягиваются от 0 до 255 и сохраняются в BufferedImage пиксель за пикселем, чтобы нарисовать изображение на JPanel.
  4. На этом изображении масштабирование выполняется с использованием AffineTransform.

Проблемы

  1. Маленькое изображение (<. 5 ГБ) </p>

    1.1 Когда я увеличиваю масштабный коэффициент для выполнения масштабирования, после
    выдается исключение точки: -

java.lang.OutOfMemoryError: пространство кучи Java.

Ниже приведен код, используемый для увеличения-

    scaled = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
    Graphics2D g2d = (Graphics2D)scaled.createGraphics();
    AffineTransform transformer = new AffineTransform();
    transformer.scale(scaleFactor, scaleFactor); 
    g2d.setTransform(transformer);
Большое изображение (> 1,5 ГБ)
  • При загрузке огромного изображения (> 1,5 ГБ) происходит то же исключение, что и в 1.1, даже если изображение достаточно маленькое для загрузки, иногда я получаюта же ошибка.

Пробные решения

  1. Я пытался использовать BigBufferedImage вместо BufferedImage для хранения растянутых данных.BigBufferedImage image = BigBufferedImage.create(newCol,newRow, BufferedImage.TYPE_INT_ARGB);
  2. Но его нельзя передать на g2d.drawImage(image, 0, 0, this);, потому что метод перерисовки JPanel просто почему-то останавливается.

  3. Я попытался загрузитьизображение в низком разрешении, при котором считывается пиксель, а несколько столбцов и строк прыгают / пропускаются .Но проблема в том, как решить, какое количество пикселей пропустить при изменении размера изображения, поэтому я не могу решить, как определить параметр jump .

    MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY,0, inChannel.size());
    buffer.order(ByteOrder.LITTLE_ENDIAN);
    FloatBuffer floatBuffer = buffer.asFloatBuffer();
    for(int i=0,k=0;i<nrow;i=i+jump)  /*jump is the value to be skipped, nrow is height of image*/
    {
        for(int j=0,l=0;j<ncol1;j=j+jump)   //ncol is width of image
        {
                index=(i*ncol)+j;
                oneDimArray[(k*ncolLessRes)+l] = floatBuffer.get(index);//oneDimArray is initialised to size of Low Resolution image.
                l++;
        }
        k++;
    }

Проблема состоит в том, чтобы решить, сколько столбцов и строк пропустить, т.е. какое значение перехода должно быть установлено.

Я попытался установить Xmx, но размер изображения меняется, и мы не можем динамически установить значения Xmx.Вот некоторые значения -

table, th, td {
  border: 1px solid black;
}
<table style="width:100%">
  <tr>
    <th>Image Size</th>
    <th>Xmx</th>
    <th>Xms</th>
    <th>Problem</th>
  </tr>
  <tr>
    <td>83Mb</td>
    <td>512m</td>
    <td>256m</td>
    <td>working</td>
  </tr>
  <tr>
    <td>83Mb</td>
    <td>3096m</td>
    <td>2048m</td>
    <td>System hanged</td>
  </tr>
   <tr>
    <td>3.84Gb</td>
    <td>512m</td>
    <td>256m</td>
    <td>java.lang.OutOfMemoryError: Java heap space
  </tr>
  <tr>
    <td>3.84Gb</td>
    <td>3096m</td>
    <td>512m</td>
    <td>java.lang.OutOfMemoryError: Java heap space
  </tr>
</table>
Для этого я попытался найти память, выделенную для программы: -
 try(BufferedWriter bw= new BufferedWriter(new FileWriter(dtaFile,true))){
    Runtime runtime=Runtime.getRuntime();
    runtime.gc();
    double oneMB=Math.pow(2,20);
    long[] arr= Instream.range(0,(int)(10.432*long.BYTES*Math.pow(2,20))).asLongStream().toArray();
    runtime.gc();
    long freeMemory= runtime.freeMemory();
    long totalMemory= runtime.totalMemory();
    long usedMemory= totalMemory-freeMemory;
    long maxMemory= runtime.maxMemory();
    String fileLine= String.format(" %9.3f  %9.3f   %9.3f " , usedMemory/oneMb, freeMemory/oneMB, totalMemory/oneMb, maxMemory/oneMB);
    bw.write();
}

Были получены следующие результаты
Распределение памяти
Этот подход не удался, так как доступныйпамять увеличивается в соответствии с использованием моего кода.В результате для меня будет бесполезно принимать решение о прыжке.

Ожидаемый результат

Способ доступа к количеству доступной памяти перед загрузкой изображения, чтобы я могиспользуйте его, чтобы принять решение о стоимости прыжка.Есть ли другая альтернатива для определения значения jump (т. Е. Насколько я могу уменьшить разрешение?).

Ответы [ 2 ]

2 голосов
/ 15 мая 2019

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

Таким образом, в вашем случае вы можете прочитать изображение в виде фрагмента (читать части изображения так же, как мы читаем данные из БД по строкам)

Например:

// Define the portion / row size 50px or 100px
int rowHeight = 50;
int rowsToScan = imageHeight / rowHeight;
if(imageHeight % rowHeight > 0) rowsToScan++;

int x = 0;
int y = 0;
int w = imageWidth;
int h = rowHeight;

ArrayList<BufferedImage> scaledImagePortions = new ArrayList<>();

for(int i = 1; i <= rowsToScan; i++) {
    // Read the portion of an image scale it
    // and push the scaled version in lets say array
    BufferedImage scalledPortionOfImage = this.getScaledPortionOfImage(img, x, y, w, h);
    scaledImagePortions.add(scalledPortionOfImage);

    y = (rowHeight * i);
}

// Create single image out of scaled images portions

Поток, который может помочь вам получить часть изображения Считать область из очень большого файла изображения в Java

Тема, которая может помочь вам масштабировать изображение (мой быстрый результат поиска :)) как изменить размер изображения в java?

Тема, которая может помочь вам в объединении буферизованных изображений: Объединение двух изображений

Вы всегда можете настроить фрагменты:)

2 голосов
/ 15 мая 2019
  1. OutOfMemoryError, что самоочевидно - у вас недостаточно памяти.Это значит, что речь идет не о физической оперативной памяти, установленной на вашем компьютере, а о том, что JVM достигает верхнего предела выделения памяти, установленного -xmx значением
  2. Ваше тестирование настроек xmx не имеет большого смысла, поскольку вы пытаетесь установить размер 3,8 ГБ.изображение в блок памяти 512 МБ.Это не может работать - вы не можете положить 10 литров воды в 5 литровую бутылку.Для использования памяти вам нужен как минимум размер изображения x3, поскольку вы храните каждый пиксель отдельно, и он содержит 3 байта (RGB).И это только для чистых данных изображения.Остается только накладные расходы на целое приложение и структуру объектов данных + дополнительное пространство, необходимое для вычислений, и, возможно, еще много чего я не упомянул, и я даже не в курсе.
  3. Вы не хотите "динамически устанавливать"-xmx.Установите максимально возможное значение в вашей системе (методом проб и ошибок).JVM не займет столько памяти, если в этом нет необходимости.Благодаря дополнительным настройкам -X вы можете указать JVM освободить неиспользуемую память, чтобы вам не приходилось беспокоиться о том, что неиспользуемая память «зависает» от JVM.
  4. Я никогда не работал с приложениями для обработки изображений.Может ли Photoshop или Gimp открывать и делать что-то полезное с такими большими изображениями?Может быть, вам стоит поискать подсказки об обработке такого большого количества данных там (если это работает)
  5. Если вышеприведенный пункт является просто наивным, поскольку вам это нужно для научных целей (а это не то, что сделано в Photoshop или Gimpпотому что, если вы не любите дальше :)), вам понадобится оборудование научного уровня.
  6. Одна вещь , которая приходит мне в голову, это вообще не считывать изображение в память, а обрабатывать его налетать.Это может уменьшить потребление памяти до порядка мегабайт.

. Более подробно рассмотрим API ImageReader , так как он предлагает (метод readTile), что возможно прочитать только областьизображение (например, для увеличения)

...