繁体   English   中英

使用 ImageSharp 缩小图像文件

[英]Shrink image file using ImageSharp

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

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

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

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

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

我不认为有一种简单(可靠)的方法来计算图像的压缩大小,而不是压缩它。

虽然 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的上限和下限使用良好的初始估计来提高性能,例如,通过像素与字节的比率线性插值文件大小。

在查看更多文档后,我现在看到单个 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(尽可能接近)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM