如何在C#中进行即时图像压缩?

5

我有一个Web应用程序,允许用户上传各种格式的图像(PNG,JPEG,GIF)。不幸的是,我的用户不一定具备技术知识,最终上传了比所需质量和大小高得多的图像。

是否有办法在提供服务时压缩这些图像?通过压缩,我指的是有损压缩,降低质量而不一定是大小。如果不同的格式压缩方式不同,那我应该存储文件格式吗?


确实有许多图像处理库-去查一下...另外,尝试从小处开始-编写能够在运行时将图像调整为某个固定大小的代码(与不同质量的压缩相比,已经拥有了.Net中的所有内容),并查看它是否/如何适合您的应用程序。 - Alexei Levenkov
3个回答

9

当然,您可以使用C#和.NET实时压缩图像。

这里有一个函数可以实现此功能。首先,它设置了一个类型为jpg的Encoder对象,并向其添加一个新质量参数。然后将该对象用于将具有新质量的图像写入MemoryStream中。

然后,从该流创建的图像被绘制到具有新尺寸的自身上:

//..
using System.Drawing.Imaging;
using System.IO;
//..

private Image compressImage(string fileName,  int newWidth, int newHeight, 
                            int newQuality)   // set quality to 1-100, eg 50
{
    using (Image image = Image.FromFile(fileName))
    using (Image memImage= new Bitmap(image, newWidth, newHeight))  
    {
        ImageCodecInfo myImageCodecInfo;
        System.Drawing.Imaging.Encoder myEncoder;
        EncoderParameter myEncoderParameter;
        EncoderParameters myEncoderParameters;
        myImageCodecInfo = GetEncoderInfo("image/jpeg"); 
        myEncoder = System.Drawing.Imaging.Encoder.Quality;
        myEncoderParameters = new EncoderParameters(1);
        myEncoderParameter = new EncoderParameter(myEncoder, newQuality);
        myEncoderParameters.Param[0] = myEncoderParameter;

        MemoryStream memStream = new MemoryStream();
        memImage.Save(memStream, myImageCodecInfo, myEncoderParameters);
        Image newImage = Image.FromStream(memStream);
        ImageAttributes imageAttributes = new ImageAttributes();
        using (Graphics g = Graphics.FromImage(newImage))
        {
            g.InterpolationMode = 
              System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;  //**
            g.DrawImage(newImage,  new Rectangle(Point.Empty, newImage.Size), 0, 0, 
              newImage.Width, newImage.Height, GraphicsUnit.Pixel, imageAttributes);
        }
        return newImage;
    }
}

private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
    ImageCodecInfo[] encoders;
    encoders = ImageCodecInfo.GetImageEncoders();
    foreach (ImageCodecInfo ici in encoders)
        if (ici.MimeType == mimeType) return ici;

    return null;
}

设置新的尺寸由您决定。如果您从不想更改尺寸,请删除参数并在代码块中将值设置为旧尺寸。如果您只是偶尔需要更改,则可以将它们传递为0或-1,并在内部进行检查。
质量应该在30-60%左右,具体取决于主题。带文本的屏幕截图无法很好地缩小,需要60-80%才能看起来好看且清晰。
此函数返回文件的jpeg版本。如果您愿意,可以创建不同的编码器,但对于可伸缩质量,jpeg通常是最佳选择。
显然,您也可以传入图像而不是文件名或将newImage保存到磁盘,而不是返回它。(在这种情况下,您应该将其处理掉。)
另外:您可以检查memStream.Length以查看结果是否太大并调整质量...
另外:更正 //**

如果我理解正确,这个程序可以处理PNG、JPEG和GIF格式的图片,但是输出的格式是JPEG,对吗?如果是这样的话,我认为这可能已经满足我的需求了。 - user2320724
确实。它还可以读取TIF文件或BMP:请参见MSD上的Image.FromFile - TaW
你知道昨天你把一个完全不同的问题标记为这个问题的精确副本,对吧? - ProgrammingLlama
不,问题是:如何确保在保存jpeg时它不会变大而是缩小。在评论中,我告诉发帖人他需要添加一个显式编码器,这将允许他控制压缩。以下是如何实现的示例。 - TaW
@TaW 好的,那么你在上面哪里显示了.save()方法的调用?因为这就是另一个人的问题所在。 如果你忘记了发布URL:http://stackoverflow.com/questions/43017831/image-resize-results-in-massively-larger-file-size-than-original-c-sharp#comment73139836_43017831他文件变大的原因是Bitmap的.save()方法默认使用PNG格式。即使您的答案结果为jpg文件,对于大多数人的情况来说,指定Imaging.ImageFormat.Jpeg就足够了。 - ProgrammingLlama
那么您在哪里展示您的.Save()方法呢?您说得有道理。其他部分,也就是“默认保存为png”和“总是用简单的jpg默认方式就足够了”,在我看来都是正确的。但是我已经重新开放了该贴,所以您可以写下您的答案。 - TaW

0

-2

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace export
{
    class Program
    {
        static void Main(string[] args)
        {

 foreach (string file in Directory.EnumerateFiles(@"C:\Temp\Images"))
            {
                 FileInfo file_info = new FileInfo(file);
                

                        var filePathOriginal = Path.Combine(@"C:\Temp\Images",
                            String.Format("{0}", file_info.Name));

                        if (File.Exists(filePathOriginal) && file_info.Name.Contains(".png"))
                        {
                            Task.Factory.StartNew(() => VaryQualityLevel(filePathOriginal));  
                            Console.WriteLine("Compressed {0}. {1}", count, file_info.Name);
                            count++;
                        }

            }            
            Console.WriteLine("End Compressing {0} Images{2}{2}Date: {1}{2}", count, DateTime.Now, Environment.NewLine);
            Console.WriteLine("Done");
            Console.ReadLine();
        }

 static void VaryQualityLevel(string file)
        {
            var img = new Bitmap(file);

            try
            {
                SaveJpeg(img, file); 
                img.Dispose();
                if (File.Exists(file))
                    File.Delete(file);
                File.Move(file + 1, file);
            }
            catch (Exception ex)
            {
                // Keep going

            }

        }

        static void SaveJpeg(Image img, string filename)
        {
            EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, 100L);
            ImageCodecInfo jpegCodec = GetEncoder(ImageFormat.Jpeg);
            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;
            img.Save(filename + 1, jpegCodec, encoderParams);
        }

        static ImageCodecInfo GetEncoder(ImageFormat format)
        {

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
}


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