0
votes

I've seen a ton of stackoverflow articles for reducing image size, but none of them maintain the original image type (or so I've found). They usually have steps to reduce pixel dimensions, reduce image quality, and convert to a specific type of image (usually jpeg).

I have a group of images that I need to resize. They have various image types, and the filenames are all stored in a database, which makes converting from one image type to another somewhat problematic. I can't just change the filename from png to jpg because then the database won't point at a real file.

Doe anyone have an example of how to resize / reduce images to '256 kilobytes' and maintain the original image type?

For examples, here is the code I'm currently fiddling with.

public static byte[] ResizeImageFile(Image oldImage, int targetSize) // Set targetSize to 1024
    {
        Size newSize = CalculateDimensions(oldImage.Size, targetSize);
        using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format24bppRgb))
        {
            using (Graphics canvas = Graphics.FromImage(newImage))
            {
                canvas.SmoothingMode = SmoothingMode.AntiAlias;
                canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
                canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
                canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
                MemoryStream m = new MemoryStream();
                newImage.Save(m, ImageFormat.Jpeg);
                return m.GetBuffer();
            }
        }
    }

Maybe there is a way I can get file fileinfo or mime type first and then switch on the .Save for the type of image?

1
Take one step at a time. I wouldn't focus on a "target file size" just yet - start by finding out what quality metadata you find imporant enough to maintain in your output. You'll most probably end up with separate routines per image type. If I were to perform this task, I would consider to 1) change all filenames in the database and convert all images to one type, and/or 2) use 3rd party tools to resize the existing files "as accurate as possible" and fix the code that creates new database entries/images. But I assume you've thought of those solutions already. - C.Evenhuis
I am not able to adjust the filenames, and its a sync process so my methods are basically asynchronous 'clean up' because 'we can't change core' etc... I am working on a method that does downscaling as well but ... its not efficient. Our end users like to upload 20mb product images because that is what they were provided by the vendor. Its an ongoing problem. - CarComp

1 Answers

0
votes

Here is what I came up with (based on some examples that I found online that weren't 100% complete.

private void EnsureImageRequirements(string filePath)
    {
        try
        {
            if (File.Exists(filePath))
            {
                // If images are larger than 300 kilobytes
                FileInfo fInfo = new FileInfo(filePath);
                if (fInfo.Length > 300000)
                {
                    Image oldImage = Image.FromFile(filePath);

                    ImageFormat originalFormat = oldImage.RawFormat;

                    // manipulate the image / Resize
                    Image tempImage = RefactorImage(oldImage, 1200); ;

                    // Dispose before deleting the file
                    oldImage.Dispose();

                    // Delete the existing file and copy the image to it
                    File.Delete(filePath);

                    // Ensure encoding quality is set to an acceptable level
                    ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();

                    // Set encoder to fifty percent compression
                    EncoderParameters eps = new EncoderParameters
                    {
                        Param = { [0] = new EncoderParameter(Encoder.Quality, 50L) }
                    };

                    ImageCodecInfo ici = (from codec in encoders where codec.FormatID == originalFormat.Guid select codec).FirstOrDefault();

                    // Save the reformatted image and use original file format (jpeg / png / etc) and encoding
                    tempImage.Save(filePath, ici, eps);

                    // Clean up RAM
                    tempImage.Dispose();
                }

            }
        }
        catch (Exception ex)
        {
            this._logger.Error("Could not resize oversized image " + filePath, ex);
        }

    }

    private static Image RefactorImage(Image imgToResize, int maxPixels)
    {
        int sourceWidth = imgToResize.Width;
        int sourceHeight = imgToResize.Height;

        int destWidth = sourceWidth;
        int destHeight = sourceHeight;

        // Resize if needed
        if (sourceWidth > maxPixels || sourceHeight > maxPixels)
        {
            float thePercent = 0;
            float thePercentW = 0;
            float thePercentH = 0;

            thePercentW = maxPixels / (float) sourceWidth;
            thePercentH = maxPixels / (float) sourceHeight;

            if (thePercentH < thePercentW)
            {
                thePercent = thePercentH;
            }
            else
            {
                thePercent = thePercentW;
            }

            destWidth = (int)(sourceWidth * thePercent);
            destHeight = (int)(sourceHeight * thePercent);
        }

        Bitmap tmpImage = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb);

        Graphics g = Graphics.FromImage(tmpImage);
        g.InterpolationMode = InterpolationMode.HighQualityBilinear;

        g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
        g.Dispose();

        return tmpImage;
    }