我认为没有一种简单(且可靠)的方法来计算图像的压缩大小,而无需对其进行压缩。
虽然 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
,例如,通过像素与字节的比率线性插值文件大小。