.NET 7中System.Drawing.Imaging保存WebP体积过大及编码器缺失问题
问题描述
我正在使用.NET 7开发,通过System.Drawing.Imaging的以下代码将Bitmap保存为WebP文件:
pictureBox1.Image = image; //image is a Bitmap image.Save(folderpath, ImageFormat.Webp);
操作很简单,但保存的640x480图片占用了716KB空间,且画质较差(摄像头图像),和PNG格式占用空间相同。我想知道如何减小WebP文件体积?
更新问题
我参考MSDN代码可调整PNG的质量,但尝试用同样方式设置WebP质量时:
if (capture.IsOpened()) { capture.Read(frame); image = BitmapConverter.ToBitmap(frame); if (pictureBox1.Image != null) { pictureBox1.Image.Dispose(); } pictureBox1.Image = image; ImageCodecInfo myImageCodecInfo; System.Drawing.Imaging.Encoder myEncoder; EncoderParameter myEncoderParameter; EncoderParameters myEncoderParameters; myImageCodecInfo = GetEncoder("image/webp"); myEncoderParameters = new EncoderParameters(1); myEncoder = System.Drawing.Imaging.Encoder.Quality; // Save the bitmap as a JPEG file with quality level 25. myEncoderParameter = new EncoderParameter(myEncoder, 25L); myEncoderParameters.Param[0] = myEncoderParameter; // and now save it to the file image.Save(filename, myImageCodecInfo, myEncoderParameters); }
搭配以下方法:
public static ImageCodecInfo GetEncoder(string mimetype) { return ImageCodecInfo.GetImageEncoders().FirstOrDefault(e => e.MimeType == mimetype); }
却发现ImageCodecInfo.GetImageEncoders()中没有WebP对应的编码器,我无法理解为何Save方法支持保存WebP却找不到该编码器?
解决方案
1. 编码器缺失的原因
.NET 7中System.Drawing.Imaging的ImageCodecInfo.GetImageEncoders()不会返回WebP编码器,因为WebP的支持是依赖Windows 10 1809及以上版本的GDI+扩展实现的,并未被纳入标准编码器枚举列表。Image.Save能保存WebP是底层GDI+原生支持,但未对外暴露编码器信息。
2. 调整WebP质量减小体积的方案
原生System.Drawing无法直接设置WebP编码参数,推荐两种可行方案:
方案一:使用第三方图片处理库(跨平台)
用ImageSharp这类专门的库可以直接控制WebP质量,示例代码:
using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.PixelFormats; // 将Bitmap转换为ImageSharp对象 using var stream = new MemoryStream(); bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); stream.Position = 0; using var image = Image.Load<Rgba32>(stream); // 配置WebP编码参数 var webpEncoder = new WebpEncoder { Quality = 25, // 取值0-100,值越小体积越小 Lossless = false // 非无损压缩才能大幅降体积 }; // 保存为WebP image.Save("output.webp", webpEncoder);
方案二:调用Windows原生WebP API(仅Windows平台)
通过P/Invoke直接调用系统自带的libwebp.dll,控制编码参数:
// 核心P/Invoke声明 [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl)] private static extern int WebPEncodeBGRA(IntPtr rgba, int width, int height, int stride, float quality, out IntPtr output); [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void WebPFree(IntPtr ptr); // 使用示例 var bitmapData = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); IntPtr webpData; int dataSize = WebPEncodeBGRA(bitmapData.Scan0, image.Width, image.Height, bitmapData.Stride, 25f, out webpData); // 将编码后的数据写入文件 byte[] buffer = new byte[dataSize]; System.Runtime.InteropServices.Marshal.Copy(webpData, buffer, 0, dataSize); File.WriteAllBytes("output.webp", buffer); // 释放资源 WebPFree(webpData); image.UnlockBits(bitmapData);
3. 额外优化技巧
- 若不需要全分辨率,可先缩小图像尺寸再编码
- 摄像头图像适合用有损压缩,不要开启无损模式
- 尽量直接从摄像头原始帧数据转换为WebP,减少中间格式转换的损耗
内容的提问来源于stack exchange,提问作者Leandro Bardelli
相关产品推荐
相关产品推荐

