使用 Java 缩放大图像
Scale large images with Java
几天来我已经通读了许多相关问题和其他网络资源,但就是找不到解决方案。
- 我想缩小非常大的图像(例如 1300 x 27000 像素)。
- 我不能为 eclipse 使用比 1024 更大的堆 space。
- 我不想使用像 JMagick 这样的外部工具,因为我想将单个可执行 jar 导出到其他设备上的 运行。另外,从我读到的内容来看,我不确定 JMagick 是否可以对非常大的图像进行这种缩放。有人知道吗?
- 到目前为止我尝试的所有结果都在 "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;
}
}
几天来我已经通读了许多相关问题和其他网络资源,但就是找不到解决方案。
- 我想缩小非常大的图像(例如 1300 x 27000 像素)。
- 我不能为 eclipse 使用比 1024 更大的堆 space。
- 我不想使用像 JMagick 这样的外部工具,因为我想将单个可执行 jar 导出到其他设备上的 运行。另外,从我读到的内容来看,我不确定 JMagick 是否可以对非常大的图像进行这种缩放。有人知道吗?
- 到目前为止我尝试的所有结果都在 "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;
}
}