在C#中减小图像尺寸

6

我希望有一个能够缩小图片尺寸的函数。

这个函数应该接受图片的URL,检查图片是否大于4MB,如果是,则将其缩小到小于4MB并返回字节大小。

我有以下方法:

public byte[] ResizeImage(string url)
{
    var uri = new Uri(url);
    var c = new WebClient();
    var oldImgStream = new MemoryStream(c.DownloadData(uri));

    if (oldImgStream.Length <= 4194304)
    {
        return oldImgStream.ToArray();
    }

    using (var oldImage = new Bitmap(oldImgStream))
    using (var newImageStream = new MemoryStream())
    {
        var format = oldImage.RawFormat;
        float resizePercent = (float)4194304 / oldImgStream.Length;
        var newImage =  ResizeImageByPercent(oldImage, resizePercent);

        newImage.Save(newImageStream, format);

       return newImageStream.ToArray();
    }
}

public static Bitmap ResizeImageByPercent(Bitmap image, float resizePercent)
{
    //Set minimum resizePercentage to 80%
    resizePercent = resizePercent > 0.8 ? (float)0.8 : resizePercent;

    int newWidth = (int)(image.Width * resizePercent);
    int newHeight = (int)(image.Height * resizePercent);

    var newImage = new Bitmap(newWidth, newHeight);
    using (var graphics = Graphics.FromImage(newImage))
    {
        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
        graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
        graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        graphics.FillRectangle(Brushes.Transparent, 0, 0, newWidth, newHeight);
        graphics.DrawImage(image, 0, 0, newWidth, newHeight);
        return newImage;
    }
}

但这并不好用。
我有一张jpg图像作为示例。
图片大小略大于4MB(4194587字节)。 图像的分辨率为2272 x 1704。
因此,当我尝试使用上述方法调整此图像的大小时。 它首先计算“resizePercentage”为:
float resizePercent = (float)4194304 / oldImgStream.Length;
resizePercent = (float)4194304 / 4194587;
resizePercent = 0.9999325 //(99.99325%)

但是因为我已经将resizePercent的最小值设置为0.8(80%),所以它会被设置为这个值。

resizePercent = 0.8;

然后它将使用这个resizePercent计算新的宽度和高度。
新的分辨率将是:1817 x 1363,图像被调整为新的分辨率。但是,在将其保存到流中并读取字节后,它返回了更大的图像。返回的图像大小为“5146056字节”即5MB。
那么有人知道如何实现这一点,或者我的方法有什么问题,以至于它返回了更大的图像,即使分辨率降低了。
我应该能够缩小png、jpg和gif图像的大小。

当您接收图像时,您不知道应用的JPG压缩质量。因此,您可能会以比原始图像更高的质量(即较少的压缩)保存图像。当您使用BMP图像时,问题是否仍然存在? - wertzui
如果您的测试图像是JPEG格式,那么它很可能使用了比.NET默认压缩比更高的压缩比。由于JPEG使用有损压缩,因此可以使用各种压缩比来确定最终图像的颗粒状况。对于PNG格式的图像,虽然它们是无损的,但仍然有可能原始图像使用了比.NET更好的算法进行压缩。等等。 - adv12
2个回答

1
处理压缩位图时,图像文件大小和分辨率之间没有一对一的对应关系。对于真正的位图(例如BMP),这种关联确实存在,因为1个像素始终等于一个定义好的字节数(基于颜色空间)。
然而,当涉及到像JPEG这样的压缩位图时,文件大小基于压缩的效率(需要编码的颜色总数、可以组合在一起的像素数量以及可以进行多少半色调以创建更多可以组合的相同颜色像素)。低分辨率的图像可能会导致文件大小减小,但也有可能保持不变或甚至增加,这取决于原始压缩的效率以及整体像素数量减少对压缩效率的影响如何。
长话短说,您不能仅仅应用简单的百分比分辨率降低来确保特定的文件大小。您真正能做的是逐步降低分辨率,在每次迭代中测试文件大小,直到其低于您的阈值,假设它确实低于该阈值。有4MB的可用空间,理论上存在某个分辨率肯定低于此大小,但这一点几乎不可能计算出来。
此外,这将根据图像格式的不同而有所不同。JPEG主要通过尽可能地扩散颜色(抖动)来实现功能。这就是为什么低质量的JPEG显示出伪影的原因。PNG和GIF大多是索引的,其中试图减少颜色的总量(但两者也可以使用抖动)。PNG进一步复杂化了问题,因为它还可以拥有无损的PNG,其中所有颜色和像素都得到保留,但会应用简单的压缩(类似于ZIP存档),以减小文件大小。所有这些格式在不同分辨率下都会表现出不同的行为。

0
尝试更改InterpolationMode、SmoothingMode、CompositingQuality、PixelOffsetMode的质量。您可以看到所有这些都设置为高质量。

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