Image.Save()在保存jpeg文件时使用什么质量级别?

37
我刚用100%的品质保存了一张JPG图片,结果惊奇地发现文件大小几乎是原文件的4倍。为了进一步调查,我试着打开这张图片并在没有明确设置品质的情况下保存它,结果文件大小和原文件完全相同。我猜这是因为什么都没变,所以直接将原来的位元组写回到了文件中。为了测试我的假设,我在图片上画了一条粗大的对角线,然后再次保存了图片,这次也没有设置品质(我预计文件会变大因为它变“脏”了),但它的大小却减小了约10KB!
目前我真的不懂当我简单地调用Image.Save()方法时,如果未指定压缩品质,到底发生了什么。为什么在修改图像后,即使没有设置品质,文件大小与原始大小如此接近,而当我将品质设置为100(基本不压缩)时,文件大小却比原始文件大好几倍?
我已经阅读了关于Image.Save()方法的文档,但没有任何关于其背后发生的事情的详细说明。我已经尽力通过各种方式进行谷歌搜索,但找不到可以解释我所看到的情况的其他信息。我已经连续工作了31个小时,可能错过了一些显而易见的东西;0)
所有这些都是在我实现一些库方法以将图像保存到数据库时发生的。我已经重载了我们的“SaveImage”方法,以允许明确设置品质,在测试期间,我遇到了上述奇怪的结果。如果你能提供任何帮助,我将不胜感激。
以下是一些代码,可以说明我的经验:
string filename = @"C:\temp\image testing\hh.jpg";
string destPath = @"C:\temp\image testing\";

using(Image image = Image.FromFile(filename))
{
    ImageCodecInfo codecInfo = ImageUtils.GetEncoderInfo(ImageFormat.Jpeg);

    //  Set the quality
    EncoderParameters parameters = new EncoderParameters(1);

    // Quality: 10
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 10L);
    image.Save(destPath + "10.jpg", codecInfo, parameters);

    // Quality: 75
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 75L);
    image.Save(destPath + "75.jpg", codecInfo, parameters);

    // Quality: 100
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 100L);
    image.Save(destPath + "100.jpg", codecInfo, parameters);

    //  default
    image.Save(destPath + "default.jpg", ImageFormat.Jpeg);

    //  Big line across image
    using (Graphics g = Graphics.FromImage(image))
    {
        using(Pen pen = new Pen(Color.Red, 50F))
        {
            g.DrawLine(pen, 0, 0, image.Width, image.Height);
        }
    }

    image.Save(destPath + "big red line.jpg", ImageFormat.Jpeg);
}

public static ImageCodecInfo GetEncoderInfo(ImageFormat format)
{
    return ImageCodecInfo.GetImageEncoders().ToList().Find(delegate(ImageCodecInfo codec)
    {
        return codec.FormatID == format.Guid;
    });
}

2
我在.NET中进行了一些有关JPG压缩及其相应误差的实验,详情请见http://www.c4real.biz/jpgCompression.aspx。 - user1236263
根据一些快速测试,似乎您最初的直觉是正确的:如果您有一个最初是jpg格式的图像,并调用Save而不指定质量,则它将只是将原始位输出到文件中。如果您在内存中修改了图像,或者如果您请求特定的压缩级别,则会重新编码图像。如果您为来自低质量源的图像请求高质量级别,则尝试非常准确地描述原始压缩引入的所有伪影会浪费很多空间。 - sethobrien
4个回答

23
使用反射,结果显示Image.Save()实际上归结为 GDI+ 函数GdipSaveImageToFile,其中encoderParams为 NULL。因此,我认为问题是当JPEG编码器获得null的encoderParams时会发生什么。这里建议使用75%,但我找不到任何实质性的参考资料。

编辑 您可以通过运行以上程序来比较1到100的质量值并将它们与默认质量保存的jpg进行比较(例如使用fc.exe /B)。


9
我进行了一个快速测试,打开了一个*.bmp文件,然后使用默认设置保存为jpg格式,另外再用显式设置为75的质量设置保存。两个文件大小完全相同。因此,编码器内部默认为75。我认为这应该在文档中说明,请问你们怎么看? - scubasteve
2
在找到这个问题之前,我在这里查看了http://msdn.microsoft.com/en-us/library/system.drawing.imaging.imageformat.jpeg.aspx。因此,我本来期望它要么有答案,要么有指向其他文档的链接。 - Liam
我相信它使用quality=100作为默认值,这是可怕的...你需要使用quality=90来获得良好的行为。 - Lilith River
这是关于 http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoder.quality.aspx 的文档。 - Luca
1
@ComputerLinguist:你的评论毫无意义。质量100比质量90更好。 - Doug S
显示剩余2条评论

6
据我所知,这个比例是75%,但我不记得在哪里看到的。

3
我使用了他的方法来将一个文件另存为75L,另一个文件使用默认设置保存。结果是:75L = 36.7 KB,而default = 36.8 KB。如果不是正好75%,那么可能是75.1%... - BrunoLM

0

当您将图像保存为JPEG文件并且质量级别小于100%时,您会引入伪影到保存的图像中,这是压缩过程的副作用。这就是为什么以100%重新保存图像实际上会增加文件大小超出原始大小 - 具有讽刺意味的是位图中存在更多信息。

这也是为什么如果您打算在之后对文件进行任何编辑,则应始终尝试以非损失格式(例如PNG)保存,否则您将通过多个有损转换影响输出质量。


顺便提一下,保存高色深度也很重要。在对图像进行某些操作时,特别是渐变图像时,可能会出现分色效果。使用高色深度源图像有助于使颜色渐变保持平滑。 - sourcenouveau
感谢提供信息。我在Photoshop中进行了快速测试,看看将图像保存为100质量时会发生什么,结果确实将其大小增加到与我在100上进行的测试大致相同。 - scubasteve

0

我对Image.Save方法不是很了解,但我可以告诉你,添加那条粗线逻辑上会减小jpg图像的大小。这是由于jpg保存(和编码)的方式。

粗黑线使得编码变得非常简单且更小(如果我没记错,这主要与离散余弦变换有关),因此修改后的图像可以使用更少的数据(字节)存储。

jpg编码步骤

关于尺寸的变化(没有添加线条),我不确定您重新打开和保存的是哪个图像

为了进一步调查,我在未明确设置质量的情况下打开并保存,文件大小完全相同

如果您打开了旧的(原始正常大小)图像并重新保存,则可能默认压缩和原始图像压缩相同。 如果您打开了新的(4倍大)图像并重新保存,则可能保存方法的默认压缩是从加载时的图像派生出来的。

再次说明,我不知道保存方法,所以我只是提供一些想法(也许它们会给您带来启示)。


你说得对,一个大的、坚实的区域会高效压缩,这很有道理。我本来想尝试并且应该解释得更清楚一些的是强制重新编码数据。我试图确定是否之所以看到相同的文件大小是因为Save()直接将位数据转储到磁盘中。我想如果我跨越图像然后保存它,就会触发文件重新编码。感谢提供的链接和额外信息。 - scubasteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接