【发布时间】:2021-11-01 13:03:04
【问题描述】:
正如标题所说,我正在尝试调整 PNG 图像的大小以达到目标文件大小(以兆字节为单位)。
我在 SO 和网络上进行了很多搜索,发现了很多代码,但所有代码都没有考虑到最终文件的大小。
我已经安排了一些代码,但试图优化性能。
例子:
- 源图像尺寸 = 30 MB
- 目标文件输出大小 = 5 MB
当前流量:
- 1 - 将PNG图像加载为
BufferedImage - 2 - 递归使用
Scalr.resize(...)来调整图像大小- 2.1 - 对于每个步骤,使用
ImageIO.write将压缩的 PNG 存储在临时文件中 - 2.2 - 使用
File.length检查大小,如果磁盘大小> 5 MB 返回步骤2
- 2.1 - 对于每个步骤,使用
- 3 - 使用
ImageIO.write(...)保存图像
这个方法行得通,微调一些参数(比如比例因子)就可以完成任务了。
我试图了解是否可以通过计算/猜测最终文件大小而不将图像存储在临时文件中来改进所有方面。
有一个byte[] obj 存储在BufferedImage obj 中,我可以使用BufferedImage.getData().getDataBuffer() 获得它,它表示图像的内容,但显然这个数组的大小比文件的最终大小大2 倍或3 倍,因为PNG 压缩算法。
我尝试了一些公式来计算值,例如:
w * h * bitDepth * 8 / 1024 / 1024 但我确信我丢失了很多数据并且帐户没有加起来!
目前我主要使用这段代码:
static void resize(BufferedImage image, String outPath, int scalingFactor) throws Exception {
image = Scalr.resize(image, image.getWidth() - scalingFactor);
// image.getData().getDataBuffer() - the byteArray containing image
File tempFile = File.createTempFile("" + System.currentTimeMillis(), ".png");
ImageIO.write(image, "png", tempFile);
System.out.println("Calculated size in bytes is: " + tempFile.length() + " - factor: " + scalingFactor);
// MAX_SIZE defined in bytes
if (tempFile.length() > MAX_SIZE) {
// recursion starts here
resize(image, outPath, chooseFactor(tempFile, 4));
} else {
// break the recursive cycle
ImageIO.write(image, "png", new File(outPath));
}
}
static int chooseFactor(File image, int scale) {
// MEGABYTE is 1024*1024
double mbSize = (double) image.length() / MEGABYTE;
return (int) ((mbSize / scale) * 100);
}
有没有办法从BufferedImage对象开始计算/猜测最终文件大小?
请告诉我我是否已经说清楚了,或者我可以提供更多信息。
如果您认为问题的解释性不够,也建议为该问题设置一个更合适的标题。
谢谢。
【问题讨论】:
-
为了获得更好的图像质量,请调整原始文件而不是临时文件的大小。
-
哦,你是对的,非常感谢你的建议!
-
更改缩放策略以查看文件大小并预定义一些粗略的缩放。如果这些不起作用,请根据上次重新缩放后的临时文件大小尝试更高的缩放。
-
是的,我已经考虑过对不同尺寸的图像集进行一些大规模测试,以获得基本的比例因子。最终我会将结果整合到线程中:)
标签: java bufferedimage