【问题标题】:Scale large images with Java使用 Java 缩放大图像
【发布时间】:2015-06-01 15:57:20
【问题描述】:

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

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

性能和质量并不是最重要的因素。主要我只是想确定,所有大小的图像都可以在不耗尽堆空间的情况下按比例缩小。

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

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

感谢和欢呼!

[编辑:] 使用缩略图

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

适用于特定图片,但

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

内存不足。

【问题讨论】:

  • 仅加载原始图像是否会使您的内存不足?还是调整大小导致内存不足?
  • 你看过 ImageProducer / ImageConsumer 吗?不记得是否有生产者以块的形式加载,但这就是您需要的:加载图像的块,缩小它们并在处理下一个块之前写入文件。如果没有默认的生产者,你必须自己解码图像。编辑:如果您找到合适的制作人,您可以使用 PixelGrabber 为您完成这项工作。
  • 具有 24 位色深的该尺寸的原始图像仍应仅消耗 100 兆字节(32 位色深为 133)。
  • 它只是一个 30mb 的数据,在内存 *4 中。
  • @satnam:我刚刚编辑了答案以添加此信息。你的问题让我找到了解决方案,因为它向我展示了我实际上仍然可以使用这种大小的缓冲图像。

标签: java image image-scaling


【解决方案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)

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

对于我的回答有些分散,我深表歉意;你现在真的让我对此感到好奇,今晚我将进一步研究它。我会试着记下我在这里遇到的。祝你好运!

【讨论】:

  • 非常感谢您的回答!这对我想出的解决方案很有帮助!
【解决方案2】:

借助您的提示和问题,我能够编写出真正符合我要求的课程。它可能无法缩放所有尺寸,但适用于非常大的图像。性能非常差(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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-25
    • 2011-05-12
    • 1970-01-01
    • 2012-03-09
    • 2015-10-05
    • 2020-04-27
    • 1970-01-01
    • 2014-07-10
    相关资源
    最近更新 更多