使用 Java 缩放大图像

Scale large images with Java

几天来我已经通读了许多相关问题和其他网络资源,但就是找不到解决方案。

  1. 我想缩小非常大的图像(例如 1300 x 27000 像素)。
  2. 我不能为 eclipse 使用比 1024 更大的堆 space。
  3. 我不想使用像 JMagick 这样的外部工具,因为我想将单个可执行 jar 导出到其他设备上的 运行。另外,从我读到的内容来看,我不确定 JMagick 是否可以对非常大的图像进行这种缩放。有人知道吗?
  4. 到目前为止我尝试的所有结果都在 "OutOfMemoryError: Java heap space" 我试过例如coobird.thumbnailator 或 awt.Graphics2D,...

性能和质量不是最重要的因素。主要是我只是想确定,所有尺寸的图像都可以按比例缩小,而不会 运行ning 出堆 space。

那么,有没有办法缩放图像?可能是小块以便不需要加载完整图像?或者有其他方法吗?

作为解决方法,如果我可以只制作图像较小部分的缩略图就足够了。但是我想裁剪大图会和缩放大图一样有问题吗?

感谢和欢呼!

[编辑:] 使用缩略图

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

适用于特定图片,但

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

运行内存不足。

我从来没有这样做过;但我建议以平铺块的形式加载图像,将它们缩小,在新的 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)

似乎已经有大量预构建的实用程序可以只加载文件的一部分。

对于我的回答有点零散,我深表歉意;你现在真的让我对此感到好奇,今晚我会进一步研究它。我会尝试记下我 运行 到这里的内容。祝你好运!

根据您的提示和问题,我能够写出一个 class 实际上可以满足我的要求。它可能无法缩放所有尺寸,但适用于非常大的图像。性能非常糟糕(1300 x 27000 png 需要 10-15 秒),但它对我的目的有效。

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