3

我需要缩小我得到的每张超过 10MB 的图像。文件类型为 png、jpg 和 gif。我看到 ImageSharp 有一个调整图像大小的选项:

Resize(new ResizeOptions
{
    Mode = ResizeMode.Max,
    Size = new Size(maxFileSize)
}

我看到了很多使用带有宽度和高度的 resize 函数的例子,但是没有一个使用这个大小选项,也没有文档准确解释“大小”的含义。

我尝试了以下方法:使用缩小 22.2MB 的图像maxFileSize=1024产生了 527.9MB 的图片。“缩小”相同的图像maxFileSize=1024*2*10产生了 47.4MB 的图片。

如何将图像缩小到大约 10MB 的大小(可以小一点)?我的目标是限制为 10MB,如果超过,将图像减小到 10MB 以下的最大可能大小而不影响比例。

4

2 回答 2

2

我认为没有一种简单(且可靠)的方法来计算图像的压缩大小,而无需对其进行压缩。

虽然 ImageSharp 似乎没有为此提供本机 API,但我们可以使用现有功能来找到满足我们约束的最佳尺寸。

主意:

  1. 我们可以假设较小的图像在磁盘上占用的空间较少,即文件大小与像素数相关。这可能不适用于在非常相似的图像尺寸上的一些复杂的压缩算法;然而,即便如此,我们仍然应该能够找到完美图像大小的良好估计。
  2. 为图像大小选择一些粗略的下限和上限。在最好的情况下,我们完美的图像尺寸介于两者之间。
  3. 通过反复调整和压缩我们的图像,在上述范围之间执行二进制搜索,直到它具有我们想要的文件大小。

对应的C#代码:

// Load original image
using Image image = Image.Load("image.jpg");

// Configure encoder
var imageEncoder = new JpegEncoder
{
    Quality = 95,
    Subsample = JpegSubsample.Ratio420
};

// Resize image, until it fits the desired file size and bounds
int min = ESTIMATED_MINIMUM_WIDTH;
int max = ESTIMATED_MAXIMUM_WIDTH;
int bestWidth = min;
using var tmpStream = new MemoryStream();
while(min <= max)
{
    // Resize image
    int width = (min + max) / 2;
    using var resizedImage = image.Clone(op =>
    {
        op.Resize(new ResizeOptions
        {
            Mode = ResizeMode.Max,
            Size = new Size(width, MAXIMUM_HEIGHT)
        });
    });

    // Compress image
    tmpStream.SetLength(0);
    resizedImage.Save(tmpStream, imageEncoder);

    // Check file size of resized image
    if(tmpStream.Position < MAXIMUM_FILE_SIZE)
    {
        // The current width satisfies the size constraint
        bestWidth = width;

        // Try to make image bigger again
        min = width + 1;
    }
    else
    {
        // Image is still too large, continue shrinking
        max = width - 1;
    }
}

// Resize and store final image
image.Mutate(op =>
{
    op.Resize(new ResizeOptions
    {
        Mode = ResizeMode.Max,
        Size = new Size(bestWidth, MAXIMUM_HEIGHT)
    });
});

// Store resized image
image.Save("image-resized.jpg", imageEncoder);

这将加载图像“image.jpg”并找到bestWidth产生文件大小小于但接近MAXIMUM_FILE_SIZE.

其他常量定义如下:

  • ESTIMATED_MINIMUM_WIDTH:完美图像宽度的估计下限。图像永远不会变得比这更小。至少应该是0
  • ESTIMATED_MAXIMUM_WIDTH:完美图像宽度的估计上限。图像永远不会变得比这更大。最多应该是image.Width
  • MAXIMUM_HEIGHT:最大图像高度。如果存在文件大小以外的其他限制(例如,图像必须适合特定的屏幕大小),这将很有帮助;否则,这可以简单地设置为image.Height

虽然二分搜索提供了很好的算法复杂性,但对于非常大的图像,这仍然会很慢。如果时间很重要(问题没有具体说明),则可以通过对 的上限和下限使用良好的初始估计来提高性能bestWidth,例如,通过像素与字节的比率线性插值文件大小。

于 2020-12-27T17:55:25.887 回答
0

在查看了更多文档之后,我现在看到单个 int 构造函数只需将相同的数字插入宽度和高度。

现在,我使用以下方法将图像缩小太多:

            using (var image = Image.Load(imageFile))
            {
                var proportion = (double)imageFile.Length / maxFileSize;
                var maxWidth = (int)(image.Width / proportion);
                var maxHeight = (int)(image.Height / proportion);
                image.Mutate(x => x
                    .Resize(new ResizeOptions
                    {
                        Mode = ResizeMode.Max,
                        Size = new Size(maxWidth, maxHeight)
                    }));
                // Save the Image
            }

如果有人有更好的想法,我很想听听。

目标是将图像缩小到 10MB 以下(尽可能接近)。

于 2020-02-16T07:13:50.543 回答