我需要缩小我得到的每个超过 10MB 的图像。 文件类型是png,jpg和gif。 我看到ImageSharp有一个调整图像大小的选项:
Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(maxFileSize)
}
我看到很多使用宽度和高度调整大小函数的示例,但没有一个使用这个大小选项,也没有文档确切解释"大小"的含义。
我尝试了以下方法:使用maxFileSize=1024
缩小 22.2MB 的图像会产生 527.9MB 的图像。 用maxFileSize=1024*2*10
"缩小"相同的图像会产生47.4MB的图片。
如何将图像缩小到大约 10MB 的大小(可以少一点(? 我的目标是限制在 10MB,如果超过,请将图像减小到最大可能的大小,小于 10MB,而不会影响比例。
我认为没有一种简单(且可靠(的方法来计算图像的压缩大小,而无需压缩它。
虽然 ImageSharp 似乎没有为此提供原生 API,但我们可以使用现有功能来找到满足我们约束的最佳大小。
想法:
- 我们可以假设较小的图像在磁盘上占用的空间更少,即文件大小与像素数相关。对于图像大小非常相似的一些复杂压缩算法,这可能不成立;然而,即便如此,我们仍然应该能够找到完美图像大小的良好估计。
- 为图像大小选择一些粗略的下限和上限。在最好的情况下,我们完美的图像尺寸介于两者之间。
- 通过反复调整和压缩图像的大小,在上述边界之间执行二叉搜索,直到它具有我们想要的文件大小。
相应的 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(尽可能接近(。