2015-06-01 74 views
0

我已经阅读了很多相关的问题和其他网络资源,但是我只是找不到解决方案。使用Java缩放大图像

  1. 我想缩小非常大的图像(例如1300 x 27000像素)。
  2. 我不能使用比1024更大的eap日志堆积空间。
  3. 我宁愿不使用像JMagick这样的外部工具,因为我想导出一个可执行的jar在其他设备上运行。另外从我读的内容来看,我不确定是否即使JMagick可以对超大图像进行缩放。有人知道吗?
  4. 我到目前为止所做的一切都会导致“OutOfMemoryError:Java heap space” I trieg例如, coobird.thumbnailator或awt.Graphics2D,...

性能和质量不是最重要的因素。主要我只是想确定,所有尺寸的图像都可以缩小而不会耗尽堆空间。

那么,有没有办法缩放图像?可能在小块中,这样完整的图像不需要加载?或者任何其他方式来做到这一点?

作为一种解决方法,如果我只能制作图像的较小部分的缩略图,也是足够的。但我认为裁剪大图像会产生相同的问题,就像缩放大图像一样?

谢谢,欢呼!

[编辑:] 随着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上打印缩小的版本,然后在第一个图像上加载下一个图像。

伪码(参数也可以不按顺序一点点):

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

很高兴我能帮忙! –