【问题标题】:I have to compress the PNG file image, without losing the quality我必须压缩PNG文件图像,而不会丢失质量
【发布时间】:2015-12-06 15:46:40
【问题描述】:

我想压缩 PNG 图像,以减小其大小,但质量应保持不变。我试图压缩JPEG图片。图片压缩了大约 90%,质量保持不变,但是当我用它压缩 PNG 图像时。没有结果,没有压缩。大小相同。

这是我的代码。

public const string _StatusLog = "StatusLog.csv";
        static void Main(string[] args)
        {
            Console.WriteLine("                 ###   WELCOME   ###");
            Console.Write("\n\nPlease enter image folder path :");
            string imagePath = Console.ReadLine();
            Program p = new Program();
            p.VaryQualityLevel(imagePath);
            Console.ReadLine();
        }
        private void VaryQualityLevel(string pathOfImage)
        {
            try
            {
                //Console.Write("Target Directory Path :");
                string targetDirectory = pathOfImage;//Console.ReadLine();

                if (targetDirectory != null)
                {
                    string[] allDirectoryInTargetDirectory = Directory.GetDirectories(targetDirectory);
                    //PRODUCT DIRECOTY OPEN
                    Console.Write("Total Folders found = " + allDirectoryInTargetDirectory.Count());
                    Console.Read();
                    if (allDirectoryInTargetDirectory.Any())
                    {
                        foreach (var directory in allDirectoryInTargetDirectory)
                        {
                            string[] subDirectory = Directory.GetDirectories(directory); // ATTRIBUTE DIRECTORY OPEN
                            if (subDirectory.Any())
                            {
                                foreach (var filesInSubDir in subDirectory)
                                {
                                    string[] allFilesInSubDir = Directory.GetFiles(filesInSubDir);
                                    //FILES IN SUB DIR OPEN
                                    if (allFilesInSubDir.Any())
                                    {
                                        foreach (var imageFile in allFilesInSubDir)
                                        {
                                            try
                                            {
                                                Bitmap bmp1 = new Bitmap(imageFile);//pathOfImage);
                                                ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);

                                                // Create an Encoder object based on the GUID 
                                                // for the Quality parameter category.
                                                System.Drawing.Imaging.Encoder myEncoder =
                                                    System.Drawing.Imaging.Encoder.Quality;

                                                // Create an EncoderParameters object. 
                                                // An EncoderParameters object has an array of EncoderParameter 
                                                // objects. In this case, there is only one 
                                                // EncoderParameter object in the array.



                                                #region SAVING THE COMPRESS IMAGE FILE
                                                EncoderParameters myEncoderParameters = new EncoderParameters(1);

                                                EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);
                                                myEncoderParameters.Param[0] = myEncoderParameter;

                                                bmp1.Save(filesInSubDir + "\\" + "Zip" + GettingImageNameForOptimizedImage(imageFile), jpgEncoder, myEncoderParameters);//pathOfImage
                                                Console.WriteLine(filesInSubDir + GettingImageNameForOptimizedImage(imageFile) + "  CREATED");//pathOfImage 
                                                #endregion

                                                #region DELETING THE ORIGNAL FILE
                                                bmp1.Dispose();
                                                System.IO.File.Delete(filesInSubDir + "\\" + GettingImageNameForOptimizedImage(imageFile));//pathOfImage
                                                Console.WriteLine(imageFile.Replace("jpg", "png") + "  DELETED");//pathOfImage 
                                                #endregion
                                                //myEncoderParameter = new EncoderParameter(myEncoder, 100L);
                                                //myEncoderParameters.Param[0] = myEncoderParameter;
                                                //bmp1.Save("D:\\" + RemovingImageFormat[0] + "100L" + ".jpg", jpgEncoder, myEncoderParameters);

                                                #region BACK RENAMING FILE TO ORIGNAL NAME
                                                System.IO.File.Move(filesInSubDir + "\\" + "Zip" + GettingImageNameForOptimizedImage(imageFile), filesInSubDir + "\\" + GettingImageNameForOptimizedImage(imageFile)); 
                                                #endregion
                                            }
                                            catch (Exception ex)
                                            {
                                                Console.Write("\n" + ex.Message + " Press enter to continue :");
                                                Console.ReadLine();

                                                Console.Write("\nWould you like to retry ? [Y/N] :");
                                                string resp = Console.ReadLine();
                                                if (resp == "Y" || resp == "y")
                                                {
                                                    Console.WriteLine("                 -------------------\n\n");
                                                    Main(null);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex);
                Console.Read();
            }

            Console.Write("Press any key to exit...");
            Console.Read();
            // Get a bitmap. ###################################################################


        }
        private ImageCodecInfo GetEncoder(ImageFormat format)
        {

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
        public string GettingImageNameForOptimizedImage(string pathOfImage)
        {
            try
            {
                string[] splitingPathOfImage = pathOfImage.Split('\\');
                string[] RemovingImageFormat = splitingPathOfImage[splitingPathOfImage.Count() - 1].ToString().Split('.');
                return RemovingImageFormat[0] + ".jpg";
            }
            catch (Exception)
            {
                return null;
            }
            return null;
        }
        public static void LoggingOperations(string ImageName, string Status, bool UpdateRequired)
        {
            try
            {
                if (!File.Exists(_StatusLog))
                {
                    using (File.Create(_StatusLog)) { }
                    DirectorySecurity sec = Directory.GetAccessControl(_StatusLog);
                    SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
                    sec.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.Modify | FileSystemRights.Synchronize, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
                    Directory.SetAccessControl(_StatusLog, sec);
                }
                if (UpdateRequired == true)
                {
                    string UpdateStatusText = File.ReadAllText(_StatusLog);
                    UpdateStatusText = UpdateStatusText.Replace(ImageName, ImageName + "," + Status);
                    File.WriteAllText(_StatusLog, UpdateStatusText);
                    UpdateStatusText = "";
                }
                else
                {
                    File.AppendAllText(_StatusLog, Environment.NewLine);
                    File.AppendAllText(_StatusLog, Status);
                }
            }
            catch (Exception)
            {
            }
        }

对于 PNG 压缩,我更改了以下行。

Bitmap bmp1 = new Bitmap(imageFile);//pathOfImage);
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Png);

请有人帮助我。如果有新的方法,我欢迎。如果可以改变这一点,那就更好了。

【问题讨论】:

  • PNG 已经被很好地压缩了,不太可能被改进。你不能在 JPG 中压缩而不损失一些质量。
  • 图像本身不是 PNG 或 JPEG。 PNG 和 JPEG 是 文件格式,代表存储某些图像的方式。两者都已经压缩了。 JPEG 是“有损”的,但其设计方式是优先丢失大多数人(如果不是所有人)仅通过查看图像就不会注意到的数据。 PNG是无损的;这必然会阻止 PNG 格式像 JPEG 那样压缩给定的图像。 PNG 确实有大量的压缩选项,而且其他 PNG 编码器可能会比 .NET 压缩得更好,但你永远不会看到 JPEG 的压缩程度。
  • @Taw,由于评分低,我无法在那里发表评论。在某种程度上,我可以接受损失。如果我有一种方法,它可能会问我,对于图像质量的损失,这将是尺寸,所以我会做出交易。
  • @Peter Duniho JPEG 可以压缩。图像质量明显下降,但在某种程度上是可以接受的。有什么办法可以在一定程度上压缩PNG,在一定程度上损失质量。
  • “有什么办法可以在一定程度上压缩PNG,在一定程度上降低质量”——不,不是。 PNG根据定义是“无损的”。正如我所提到的,格式本身支持各种可用于优化结果的参数,这可能会稍微影响最终的压缩大小(但与您要求的不同),但 .NET 编码器不提供访问这些参数。你需要一个第三方编码器。当然,您始终可以减小图像尺寸;这必然会丢弃信息并且也会减小文件大小。

标签: c# image compression png


【解决方案1】:

PNG 图像默认为 32 位。您可以将它们转换为 8 位:生成的文件将比原始文件小 5 倍。对于大多数图像,质量损失几乎是看不见的。

这就是在线 png 压缩器所做的。

您可以使用 nQuant 自己完成此操作:http://nquant.codeplex.com/(在 Nuget 上可用)

var quantizer = new WuQuantizer();         
using(var quantized = quantizer.QuantizeImage(bmp1))
{
    quantized.Save(targetPath, ImageFormat.Png);
}

该方法的完整解释可以在这篇博文http://www.hurryupandwait.io/blog/convert-32-bit-pngs-to-high-quality-8-bit-pngs-with-c中找到

【讨论】:

  • 感谢您推荐 nQuant。我刚试过。 .NET 创建具有 alpha 透明度的 800kb PNG 时,nQuant 能够以仅 240kb 创建相同的 PNG 图像,而没有明显的质量损失。请记住,它通过将颜色计数减少到 256 来实现这一点,但它做得非常巧妙。
  • 小心这个库。它有一些带有很多颜色的大图像的崩溃问题。 archive.codeplex.com/?p=nquant
【解决方案2】:

我还建议查看ImageSharp,它也适用于 .NET Core

            using (var image = Image.Load(fileData)) // fileData could be file path or byte array etc.
            {
                var h = image.Size().Height / 2;
                var w = image.Size().Width / 2;
                var options = new ResizeOptions
                {
                    Mode = ResizeMode.Stretch,
                    Size = new Size(w, h)
                };
                image.Mutate(_ => _.Resize(options));
                using (var destStream = new MemoryStream())
                {
                    var encoder = new JpegEncoder();
                    image.Save(destStream, encoder);
                    // Do something with output stream
                }     
            }

【讨论】:

    【解决方案3】:

    PNG 压缩的一个主要变量是压缩速度和输出大小之间的权衡。 PNG 压缩实际上可能非常慢,因为它涉及在数据缓冲区中搜索匹配模式。您可以通过限制编码器搜索的缓冲区量来加快压缩速度。

    您的编码器应该有一个设置,允许您指定搜索匹配项的方式。

    如果您的输入 PNG 图像未使用编码器搜索整个缓冲区进行压缩,您可以通过在应用程序中搜索整个缓冲区来获得一些改进的压缩。但是,您不太可能获得重大改进。

    【讨论】:

    • 随着 Photoshop 减小图像的大小,以牺牲质量为代价。同样,我想在 .net 的帮助下做到这一点。
    • PNG 没有大小/质量的权衡。 PNG 可以在压缩时间/大小之间进行权衡。
    • 通过简单地对 PNG 进行索引,您也会看到重大改进
    【解决方案4】:

    在像素大小和颜色深度一致的情况下,PNG 应该是无损的。 如果正在寻找一些基准输出大小的东西。

    https://pnggauntlet.com/

    【讨论】:

      猜你喜欢
      • 2014-03-22
      • 1970-01-01
      • 2014-03-11
      • 2019-10-06
      • 2012-05-27
      • 1970-01-01
      • 2015-03-20
      • 2016-05-18
      • 1970-01-01
      相关资源
      最近更新 更多