[英]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. 我已经阅读了许多相关问题和其他网络资源,但是我只是找不到解决方案。
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.