简体   繁体   English

使用Java缩放大图像

[英]Scale large images with Java

I have read through many related questions and other web resources for days, but I just can't find a solution. 我已经阅读了许多相关问题和其他网络资源,但是我只是找不到解决方案。

  1. I want to scale down very large images (eg 1300 x 27000 Pixel). 我想缩小非常大的图像(例如1300 x 27000像素)。
  2. I cannot use a larger heap space for eclipse than 1024. 我不能使用大于1024的Eclipse堆空间。
  3. I rather don't want to use an external tool like JMagick since I want to export a single executable jar to run on other devices. 我不想使用像JMagick这样的外部工具,因为我想导出一个可执行jar以便在其他设备上运行。 Also from what I read I am not sure if even JMagick could do this scaling of very large images. 从我阅读的内容中,我也不确定甚至JMagick是否可以对非常大的图像进行缩放。 Does anyone know? 有人知道吗?
  4. Everything I tried so far results in "OutOfMemoryError: Java heap space" I trieg eg coobird.thumbnailator or awt.Graphics2D, ... 到目前为止,我尝试的所有操作都会导致“ OutOfMemoryError:Java堆空间”,例如,coobird.thumbnailator或awt.Graphics2D,...

Performance and quality are not the most important factors. 性能和质量不是最重要的因素。 Mainly I just want to be sure, that all sizes of images can be scaled down without running out of heap space. 我主要是想确保所有尺寸的图像都可以按比例缩小,而不会耗尽堆空间。

So, is there a way to scale images? 那么,有没有办法缩放图像? may be in small chunks so that the full image doesn't need to be loaded? 可能是小的块,因此不需要加载完整图像? Or any other way to do this? 或任何其他方式做到这一点?

As a workaround it would also be sufficient if I could just make a thumbnail of a smaller part of the image. 作为一种解决方法,如果我只制作图像一小部分的缩略图就足够了。 But I guess cropping an large image will have the same problems as if scaling a large image? 但是我想裁剪大图像会和缩放大图像一样有同样的问题吗?

Thanks and cheers! 谢谢和欢呼!

[EDIT:] With the Thumbnailator [编辑:]使用Thumbnailator

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

works for the particular picture, but 适用于特定图片,但是

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

runs out of memory. 内存不足。

I've never had to do that; 我从来没有这样做; but I would suggest loading the image in tiled pieces, scaling them down, printing the scaled-down version on the new BufferedImage, and then loading the next tile over the first. 但是我建议以分块的形式加载图像,将其缩小,在新的BufferedImage上打印缩小的版本,然后在第一个上加载下一个分块。

Psuedocode (parameters may also be a little out of order): 伪代码(参数也可能有点混乱):

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;

Of course you could always do it the dreaded binary way; 当然,您始终可以使用可怕的二进制方式进行操作; but this apparently addresses how to load only small chunks of an image: Draw part of image to screen (without loading all to memory) 但这显然解决了如何仅加载图像的小块的问题:将图像的一部分绘制到屏幕上(而不将所有图像加载到内存中)

It seems that there are already a large number of prebuilt utilities for loading only part of a file. 似乎已经有大量的预构建实用程序仅用于加载文件的一部分。

I apologize for the somewhat scattered nature of my answer; 对于回答的分散性,我深表歉意。 you actually have me curious about this now and I'll be researching it further tonight. 您实际上让我现在对此感到好奇,我将在今晚进一步研究它。 I'll try and make note of what I run into here. 我会尝试记下我在这里遇到的问题。 Good luck! 祝好运!

With your hints and questions I was able to write a class that actually does what I want. 在您的提示和问题的帮助下,我编写了一个可以真正满足我需求的类。 It might not scale all sizes, but works for very large images. 它可能无法缩放所有尺寸,但适用于非常大的图像。 The performance is very bad (10-15 Sec for an 1300 x 27000 png), but it works for my purposes. 性能非常差(对于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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM