2015-06-01 4 views
0

Я читал многие связанные вопросы и другие веб-ресурсы в течение нескольких дней, но я просто не могу найти решение.Масштабировать большие изображения с помощью Java

  1. Я хочу уменьшить очень большие изображения (например, 1300 x 27000 пикселей).
  2. Я не могу использовать большее пространство кучи для затмения, чем 1024.
  3. Я скорее не хочу использовать внешний инструмент, такой как JMagick, так как я хочу экспортировать одну исполняемую банку для запуска на других устройствах. Также из того, что я читал, я не уверен, что даже JMagick мог бы делать это масштабирование очень больших изображений. Кто-нибудь знает?
  4. Все, что я пробовал до сих пор, приводит к «OutOfMemoryError: Java heap space» I trieg, eg. coobird.thumbnailator или awt.Graphics2D, ...

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

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

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

Спасибо и приветствую!

[EDIT:] С Thumbnailator

 Thumbnails.of(new File(".../20150601161616.png")) 
     .size(160, 160); 

работ для конкретного изображения, но

 Thumbnails.of(new File(".../20150601161616.png")) 
     .size(160, 160) 
     .toFile(new File(".../20150601161616_t.png")); 

бежит из памяти.

+1

Загружается ли только оригинальное изображение из памяти? Или это изменение размера, которое исчерпывает память? – satnam

+1

Вы посмотрели ImageProducer/ImageConsumer? не помните, есть ли производители, загружающие куски, но это то, что вам нужно: загрузка в куски изображения, масштабирование их и запись в файл перед обработкой следующего фрагмента. если для этого нет производителя по умолчанию, вам придется декодировать изображение самостоятельно. Изменить: если вы найдете подходящего производителя, вы можете использовать PixelGrabber для выполнения этой задачи. – BeyelerStudios

+0

Необработанное изображение такого размера с глубиной цвета 24 бит должно по-прежнему потреблять только 100 мегабайт (133 для глубины цвета 32 бит). – biziclop

ответ

0

С вашими подсказками и вопросами я смог написать класс, который действительно делает то, что я хочу. Он может не масштабировать все размеры, но работает для очень больших изображений. Производительность очень плохая (10-15 секунд для 1300 x 27000 png), но она работает для моих целей.

import java.awt.Color; 
import java.awt.Graphics2D; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import javax.imageio.ImageIO; 
import net.coobird.thumbnailator.Thumbnails; 


public class ImageManager { 
    private int tileHeight; 
    private String pathSubImgs; 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     int tileHeightL = 2000; 
     String imageBasePath = "C:.../screenshots/"; 
     String subImgsFolderName = "subImgs/"; 
     String origImgName = "TestStep_319_20150601161652.png"; 
     String outImgName = origImgName+"scaled.png"; 

     ImageManager imgMngr = new ImageManager(tileHeightL,imageBasePath+subImgsFolderName); 

     if(imgMngr.scaleDown(imageBasePath+origImgName, imageBasePath+outImgName)) 
      System.out.println("Scaled."); 
     else 
      System.out.println("Failed."); 



    } 

    /** 
    * @param origImgPath 
    * @param outImgPath 
    * @param tileHeight 
    * @param pathSubImgs 
    */ 
    public ImageManager(int tileHeight, 
      String pathSubImgs) { 
     super(); 
     this.tileHeight = tileHeight; 
     this.pathSubImgs = pathSubImgs; 
    } 

    private boolean scaleDown(String origImgPath, String outImgPath){ 
     try { 
     BufferedImage image = ImageIO.read(new File(origImgPath)); 
     int origH = image.getHeight(); 
     int origW = image.getWidth(); 

     int tileRestHeight; 
     int yTiles = (int) Math.ceil(origH/tileHeight); 
     int tyleMod = origH%tileHeight; 

     for(int tile = 0; tile <= yTiles ; tile++){ 
      if(tile == yTiles) 
       tileRestHeight = tyleMod; 
      else 
       tileRestHeight = tileHeight; 
      BufferedImage out = image.getSubimage(0, tile * tileHeight, origW, tileRestHeight); 

       ImageIO.write(out, "png", new File(pathSubImgs + tile + ".png")); 

      Thumbnails.of(new File(pathSubImgs + tile + ".png")) 
      .size(400, 400) 
      .toFile(new File(pathSubImgs + tile + ".png")); 
     } 

     image = ImageIO.read(new File(pathSubImgs + 0 + ".png")); 
     BufferedImage img2; 
     for(int tile = 1; tile <= yTiles ; tile++){ 
      if(tile == yTiles) 
       tileRestHeight = tyleMod; 
      else 
       tileRestHeight = tileHeight; 
      img2 = ImageIO.read(new File(pathSubImgs + tile + ".png")); 
      image = joinBufferedImage(image, img2); 
     } 
     ImageIO.write(image, "png", new File(outImgPath)); 
     return true; 
     } catch (IOException e) { 
      e.printStackTrace(); 
      return false; 
     } 
    } 

    public static BufferedImage joinBufferedImage(BufferedImage img1,BufferedImage img2) { 

     //do some calculate first 
     int height = img1.getHeight()+img2.getHeight(); 
     int width = Math.max(img1.getWidth(),img2.getWidth()); 
     //create a new buffer and draw two image into the new image 
     BufferedImage newImage = new BufferedImage(width,height, BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g2 = newImage.createGraphics(); 
     Color oldColor = g2.getColor(); 
     //fill background 
     g2.setPaint(Color.WHITE); 
     g2.fillRect(0, 0, width, height); 
     //draw image 
     g2.setColor(oldColor); 
     g2.drawImage(img1, null, 0, 0); 
     g2.drawImage(img2, null, 0, img1.getHeight()); 
     g2.dispose(); 
     return newImage; 
    } 
} 
1

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

Psuedocode (параметры могут также быть немного из того):

Image finalImage; 
Graphics2D g2D = finalImage.createGraphics(); 
for each yTile: 
    for each xTile: 
     Image orig = getImage(path, x, y, xWidth, yWidth); 
     g2D.drawImage(x * scaleFactor, y * scaleFactor, xWidth * scaleFactor, yWidth * scaleFactor, orig); 
return orig; 

Конечно, вы всегда можете сделать это страшный бинарный путь; но это, по-видимому, относится к загрузке только небольших фрагментов изображения: Draw part of image to screen (without loading all to memory)

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

Прошу прощения за несколько рассеянный характер моего ответа; вы на самом деле меня интересуют это сейчас, и я буду исследовать его сегодня вечером. Я попытаюсь обратить внимание на то, с чем я столкнулся. Удачи!

+0

Большое спасибо за ваш ответ! Это было полезно на пути к решению, которое я придумал! – Doena

+0

рад, что я мог бы помочь! –

 Смежные вопросы

  • Нет связанных вопросов^_^