I have to compress the PNG file image, without losing the quality

21,916

Solution 1

PNG Images are 32 bits by default. You can convert them to 8 bits : resulting file will be about 5 times smaller than the original one. With most images, the loss of quality is almost invisible.

This is what online png compressors do.

You can do this yourself by using nQuant : http://nquant.codeplex.com/ (available on Nuget)

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

Full explanation of the method is available on this blog post http://www.hurryupandwait.io/blog/convert-32-bit-pngs-to-high-quality-8-bit-pngs-with-c

Solution 2

I also suggest looking at ImageSharp which also works with .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
                }     
            }

Solution 3

The one major variable in PNG compression is the tradeoff between compression speed and output size. PNG compression can actually be quite slow because it involves searching a data buffer for matching patterns. You can speed up the compression by limiting how much of the buffer the encoder searches.

Your encoder should have a setting that allows you to specify how searching for matches it will do.

IF your input PNG image was not compressed with the encoder searching the entire buffer, you may get some improved compression by searching the full buffer in your application. However, you are unlikely to get a major improvement.

Share:
21,916
Muhammad Bashir
Author by

Muhammad Bashir

Updated on December 31, 2021

Comments

  • Muhammad Bashir
    Muhammad Bashir over 2 years

    I want to compress a PNG image, to reduce its size but the quality should remain the same. I have tried to compress JPEG picture. Picture compressed about 90% and quality remain the same but when i compress a PNG image with it. No result, no compression. Same size.

    Here is my code.

    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)
                {
                }
            }
    

    For PNG compression i changed the following line.

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

    Kindly some one help me out. If there is a new method, I welcome it. If this could be altered, it would a lot better.

  • Muhammad Bashir
    Muhammad Bashir over 8 years
    As Photoshop decrease the size of image with some trade of with quality. Same i want to do it with the help of .net.
  • user3344003
    user3344003 over 8 years
    There is no size/quality tradeoff in PNG. There is a compression time/size tradeoff that is possible with PNG.
  • Doug S
    Doug S almost 8 years
    Thanks for suggesting nQuant. I just tried it. Where .NET was creating a 800kb PNG with alpha transparency, nQuant was able to create the same PNG image at only 240kb, with no discernible quality loss. Keep in mind it does this by reducing the color count to 256, but it does it very smartly.
  • Pierre
    Pierre about 6 years
    By simply indexing the PNG you will also see a major improvement
  • Donny V.
    Donny V. over 4 years
    Careful with this library. It has crash issues with somewhat large images with a lot of colors. archive.codeplex.com/?p=nquant