【问题标题】:Image compression is not working图像压缩不起作用
【发布时间】:2014-02-06 00:50:17
【问题描述】:

我在网站上进行了一项裁剪图像的操作,但是最终的裁剪图像在文件大小方面明显变大(原始图像为 24k,裁剪图像为 650k)。所以我发现我需要在保存之前对图像进行一些压缩。我想出了以下几点:

public static System.Drawing.Image CropImage(System.Drawing.Image image, Rectangle cropRectangle, ImageFormat format)
{
    var croppedImage = new Bitmap(cropRectangle.Width, cropRectangle.Height);
    using (var g = Graphics.FromImage(croppedImage))
    {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(
            image, 
            new Rectangle(new Point(0,0), new Size(cropRectangle.Width, cropRectangle.Height)), 
            cropRectangle, 
            GraphicsUnit.Pixel); 
        return CompressImage(croppedImage, format);
    }
}

public static System.Drawing.Image CompressImage(System.Drawing.Image image, ImageFormat imageFormat)
{
    var bmp = new Bitmap(image);
    var codecInfo = EncoderFactory.GetEncoderInfo(imageFormat);
    var encoder = System.Drawing.Imaging.Encoder.Quality;
    var parameters = new EncoderParameters(1);
    var parameter = new EncoderParameter(encoder, 10L);
    parameters.Param[0] = parameter;

    using (var ms = new MemoryStream())
    {
        bmp.Save(ms, codecInfo, parameters);
        var resultImage = System.Drawing.Image.FromStream(ms);
        return resultImage;
    }
}

我将质量设置为低只是为了看看是否有任何变化。没有。作物在外观上被正确保存,但压缩并不令人愉快。如果我完全绕过 CompressImage(),文件大小和图像质量似乎都没有任何不同。

所以,有 2 个问题。为什么什么都没有发生?有没有一种更简单的方法可以将生成的图像压缩为“网络优化”,类似于 Photoshop 保存网络图像的方式(我认为它只是从中剥离了很多信息以减小尺寸)。

【问题讨论】:

  • 您正在读取和保存什么图像格式? BMP 几乎不压缩。 PNG 或 JPG 压缩得更好。我可以轻松地将 giong 从 24k PNG 成像到 650k BMP。
  • 我正在使用 png/jpg 格式阅读。 Bitmap 只是一个中间对象
  • 问题不在于你在读什么……而是你在保存什么。请检查 condecInfo 和 paramets 对象的正确设置...也许您保存的图像质量比原始图像更高...

标签: c# image-compression


【解决方案1】:

您的问题是您必须在保存图像时“压缩”(真正编码)图像,而不是在保存之前。程序中的 Image 对象始终是未压缩的。

通过保存到 MemoryStream 并从流中读回,将对图像进行编码,然后再次将其解码回相同的大小(如果您使用 JPEG,则会在此过程中损失一些质量)。但是,如果将其保存到带有压缩参数的文件中,则会得到一个压缩的图像文件。

在 153 KB 的源图像上使用 JPEG 质量级别 90 的此例程可得到 102 KB 的输出图像。如果您想要更小的文件大小(具有更多编码工件),请将编码器参数更改为小于 90。

public static void SaveJpegImage(System.Drawing.Image image, string fileName)
{
    ImageCodecInfo codecInfo = ImageCodecInfo.GetImageEncoders()
        .Where(r => r.CodecName.ToUpperInvariant().Contains("JPEG"))
        .Select(r => r).FirstOrDefault();

    var encoder = System.Drawing.Imaging.Encoder.Quality;
    var parameters = new EncoderParameters(1);
    var parameter = new EncoderParameter(encoder, 90L);
    parameters.Param[0] = parameter;

    using (FileStream fs = new FileStream(fileName, FileMode.Create))
    {
        image.Save(fs, codecInfo, parameters);
    }
}

【讨论】:

  • 因此,在我的特定实现中,图像永远不会保存到磁盘。图像作为二进制信息保存在数据库中,然后直接读回网页(MVC FileResult)。如何压缩图像并保存字节?如果我根据您所说的将数据库中的压缩字节读入图像(中间),它将重新膨胀大小。
  • 这与我目前的几乎相同,唯一的区别是我在返回之前将其读回Image。我尝试省略该部分并从 MemoryStream 中返回字节数组,但结果相同 - 当原始文件为 24k 时,文件为 600k。
  • 不确定这有多优化,但我有一个生产中的应用程序可以将图像保存到 Oracle Blob。在您的示例中,在将位图保存到内存流之后,立即从流中获取原始字节:'byte[] imageBytes = ms.ToArray();` 并将字节数组存储在数据库中。您可以使用不同的编码 - 当您从数据库中读取它时,它就像您对字节数组执行 File.Read 一样 - 请注意它是 jpg 或 png,而不是 Image 对象。您需要从 byte[] 创建一个流并加载它以获取图像。
  • 重申一下,将内存流读回图像会将图像从其他格式(jpg、png、tiff)解码回位图 - 您将图像膨胀回原始大小。
  • 好的,我搞定了。起初,看起来我在所有正确的地方都正确地保存了字节,但我尝试使用 Image 作为中间对象,我认为有几个地方我是 zigged 而不是 zagged。我尽可能地消除了Image,并尝试单独使用byte[],它似乎已经清除了它。谢谢你的耐心:)
【解决方案2】:

我认为,当您使用使用 Image.FromStream 创建的引用流的图像时,您不应该处置 MemoryStream。直接从流中创建位图也不起作用。

试试这个:

private static Image CropAndCompressImage(Image image, Rectangle rectangle, ImageFormat imageFormat)
{
    using(Bitmap bitmap = new Bitmap(image))
    {
        using(Bitmap cropped = bitmap.Clone(rectangle, bitmap.PixelFormat))
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                cropped.Save(memoryStream, imageFormat);
                return new Bitmap(Image.FromStream(memoryStream));
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-28
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    • 2019-08-06
    • 2012-05-09
    相关资源
    最近更新 更多