Java BuffereImage减小了图像大小

Java BuffereImage减小了图像大小,java,kotlin,bufferedimage,javax.imageio,Java,Kotlin,Bufferedimage,Javax.imageio,我使用JavaImageIO和BuffereImage进行一些图像操作,并希望看到当我将图像输出为jpg时它的行为。在那里,我发现了一些我无法解释的有趣行为。我有以下代码。代码读取图像,然后在同一文件夹中输出与“copy.jpg”相同的图像。代码在Kotlin中,但使用的函数是java函数: val image = File("some/image/path.jpg") val bufImage = ImageIO.read(image.inputStream()) FileOutputStre

我使用JavaImageIO和BuffereImage进行一些图像操作,并希望看到当我将图像输出为jpg时它的行为。在那里,我发现了一些我无法解释的有趣行为。我有以下代码。代码读取图像,然后在同一文件夹中输出与“copy.jpg”相同的图像。代码在Kotlin中,但使用的函数是java函数:

val image = File("some/image/path.jpg")
val bufImage = ImageIO.read(image.inputStream())
FileOutputStream(File(image.parentFile, "copy.jpg")).use { os ->
    ImageIO.write(bufImage, "jpg", os)
}
我希望它输出完全相同的文件,除了可能的元信息。然而,生成的文件几乎是原始文件的十分之一。我怀疑元信息会有那么多。确切的大小差异取决于我使用的图像文件,但是每次输出的图像都会更小。但我看不出旧文件的质量有什么不同。放大时,我会看到相同的像素

为什么文件大小会大幅减小?

JPEG是:它会丢弃大量信息以保持文件小。(未压缩的图像文件可能会大几个数量级。)

当然,这是为了扔掉你不太可能看到或关心的信息;但它仍然会丢失一些图像数据

损失是分代的:如果您有一幅来自JPEG文件的图像,然后将其重新压缩为JPEG文件,它通常会丢失更多的数据,导致比第一个JPEG文件质量更差的结果-即使压缩设置完全相同(尝试近似已压缩图像的效果与尝试近似原始源图像的效果不同。而且无法恢复已丢失的信息!)

这几乎是肯定的。您的代码读取一个JPEG文件并将其扩展为一个BuffereImage(保存未压缩的图像数据),然后将其再次压缩为一个新的JPEG文件,这会进一步降低质量。它可能比使用的第一个文件使用更高的压缩,因此大小更小

如果放大后在图像查看器或编辑器中看不到两个JPEG文件之间的任何差异,我会感到惊讶(JPEG人工制品在锐利的边缘和边界周围最为明显,但如果你知道要寻找什么,你有时可以在其他地方看到它们。如果你能将两幅图像在屏幕上完全相同的区域对齐并直接在它们之间翻转,细微的变化会更容易看到。)

创建JPEG时,您可以控制丢失的信息量,但您使用的方法没有提供这样做的方法。请参阅以了解如何做到这一点。(它是Java语言,但您应该能够遵循。)

显然,您准备丢失的信息越多,最终可以得到的文件越小。但是请注意,如果选择高质量设置,结果可能比第一个JPEG大很多,尽管它可能仍然会丢失稍多的质量

(这就是为什么,如果要对图像进行任何类型的处理,最好将其保存在无损格式中,直到最后,并仅压缩到JPEG等有损格式一次,以避免每次保存和重新加载时丢失质量。)


正如您所指出的,另一个原因可能是非图像数据的丢失-您不太可能注意到元数据(如相机设置)的丢失,但该文件也可能有相当大的缩略图。

JPG是一种有损压缩格式。这可能是原因。您可能希望直接进行字节缓冲,而不是ImageIO.write如果您想要完全相同的文件,请使用另一个文件写入操作进行r copy。从本质上讲,使用ImageIO.write可能会根据扩展名对最终文件应用额外的压缩。JPEG格式有许多选项。例如,如果您将图像导出为JPEG格式,它将要求您提供图像质量设置,这会影响图像被压缩的程度。几乎可以肯定,您保存图像时使用的压缩因子比使用的原始图像更高。我认为,如果您将两幅图像相减,您会发现一些差异。此外,是什么创建了原始jpg?一些相机使用的jpg质量比java的默认值高得多。此外,ev每次使用jpg压缩打开和保存图像时,都会丢失一些信息。JPEG文件的元数据通常大小相同,与图像尺寸/压缩无关。因此,对于小图像,元数据可能占据文件的大部分…但不知道原始JPEG中的内容,因此这当然只是猜测。