C# 图像处理:缩放和文件大小约束 (.NET Framework 4.8)

问题描述 投票:0回答:1

问题

我正在升级旧版软件(.NET Framework 4.8)以与 API 集成,该 API 以 Base64 编码字符串为所有用户提供图片。我可以获取这个字符串,将其提取到字节数组中,然后创建一个将用于缩略图的图像。当我尝试缩放和压缩图像以确保其大小不超过 10kb 时,问题就出现了。

目前的解决方案

缩放是通过根据图像大小和最大允许字节 10,000 估计缩放因子来完成的。这不是一个完美的缩放函数,但它是一个足够的估计。 在保存图像之前获取文件的大小很困难,因为使用 bmp.Save() 方法时内存流的大小“不”等于位图的大小。但是,如果我采用相同的字节数组并使用 File.SaveAllBytes() 保存它,则生成的 jpg 的大小与源字节数组的大小完全匹配。 使用 File.SaveAllBytes 方法的缺点是无法控制压缩级别。因此,这个可怕的循环解决方案似乎是保证文件小于 10000 字节的唯一方法。该循环以不同程度的压缩保存临时文件,直到临时文件的大小低于 10000 字节。 当前问题

通过检查文件大小,我

仍然

得到一些大约 10,200 字节大的文件,尽管临时文件表明它应该属于这个大小。如果有人有任何提示,我很想看看如何改进此代码,而不必在 10000 字节限制上添加一些额外的“填充”。 然而,当我尝试将文件限制为 10kb 时,问题就出现了。

这是我当前的解决方案。

using (MemoryStream ms = new MemoryStream(imageByteArray)) { Bitmap bmp = new Bitmap(ms); if (bmp != null) { long quality = 82; //SaveImageToFile(bmp, filePath, quality); long size = ObtainFileSize(bmp, imageName, quality); double scaleFactor = Math.Sqrt((double)allowedFileSizeInByte / (double)ms.Length); int newWidth = (int)(bmp.Width * scaleFactor); int newHeight = (int)(bmp.Height * scaleFactor); bmp = new Bitmap(bmp, new Size(newWidth, newHeight)); while (size > allowedFileSizeInByte) { quality = quality - 5; size = ObtainFileSize(bmp, imageName, quality); } SaveImageToFile(bmp, filePath, quality); bmp.Dispose(); //result.Dispose(); } }

此外,我还有两个用于实际保存的辅助方法。

private void SaveImageToFile(Bitmap bmp, string imagePath, long quality) { try { //All images will be encoded as ajpg with varyig quality ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg); System.Drawing.Imaging.Encoder imageEncoder = System.Drawing.Imaging.Encoder.Quality; EncoderParameters myEncoderParameters = new EncoderParameters(1); myEncoderParameters.Param[0] = new EncoderParameter(imageEncoder, quality); bmp.Save(imagePath, jgpEncoder, myEncoderParameters); } catch { throw; } }

        private long ObtainFileSize(Bitmap bmp, string imageName, long quality)
        {
            using (Bitmap newBmp = new Bitmap(bmp))
            {
                // Get the temporary folder path
                string tempPath = Path.GetTempPath();
                // Combine the path with your file name
                string tempFile = Path.Combine(tempPath, imageName + ".jpeg");
                // Save the image to the temporary file
                SaveImageToFile(newBmp, tempFile, quality);
                long size = new FileInfo(tempFile).Length;
                File.Delete(tempFile);
                return size;
            }

        }

        // for now we only use JPG but this can change...
        private ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }

    }

c# .net image-processing graphics bitmap
1个回答
0
投票

1.重试压缩直到尺寸合适

编写一个循环,重试压缩,直到大小可接受。您可以改变图像大小、压缩级别或两者的某种组合。这本质上是二分搜索的某种变体,用于查找导致足够接近的文件大小的参数。某些压缩库

可能

有一个 API 可以为您执行此操作,或者提供一些其他帮助程序来帮助估计参数。但我不认为任何内置编码器可以做到这一点。您可以对内存流进行编码,以避免访问磁盘,直到找到一些合适的参数。 2.不要使用压缩

如果没有压缩,结果大小是确定的,仅取决于图像宽度/高度和位深度。请注意,由于图像标题和对齐要求,这种关系可能会有些复杂。我想这就是您所说的

File.SaveAllBytes()

的意思。对于任何给定的文件大小,这很可能会导致图像质量变差。

3.使用固定速率压缩

用于 GPU 纹理的图像格式通常使用固定速率压缩格式,这应该使文件大小可预测,请参阅

纹理块压缩

。但这些类型的格式在常规应用程序中并不常用,而且据我所知,没有内置的 C# API 来保存/加载此类图像。

© www.soinside.com 2019 - 2024. All rights reserved.