Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reduce image size in C#

I want function which will reduce image size.

The function should take the image URL, check is image 4MB size or bigger and if it is then resize it to lover then 4MB and return bytes.

I have next method:

public byte[] ResizeImage(string url)
{
    var uri = new Uri(url);
    var c = new WebClient();
    var oldImgStream = new MemoryStream(c.DownloadData(uri));

    if (oldImgStream.Length <= 4194304)
    {
        return oldImgStream.ToArray();
    }

    using (var oldImage = new Bitmap(oldImgStream))
    using (var newImageStream = new MemoryStream())
    {
        var format = oldImage.RawFormat;
        float resizePercent = (float)4194304 / oldImgStream.Length;
        var newImage =  ResizeImageByPercent(oldImage, resizePercent);

        newImage.Save(newImageStream, format);

       return newImageStream.ToArray();
    }
}

public static Bitmap ResizeImageByPercent(Bitmap image, float resizePercent)
{
    //Set minimum resizePercentage to 80%
    resizePercent = resizePercent > 0.8 ? (float)0.8 : resizePercent;

    int newWidth = (int)(image.Width * resizePercent);
    int newHeight = (int)(image.Height * resizePercent);

    var newImage = new Bitmap(newWidth, newHeight);
    using (var graphics = Graphics.FromImage(newImage))
    {
        graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
        graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
        graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        graphics.FillRectangle(Brushes.Transparent, 0, 0, newWidth, newHeight);
        graphics.DrawImage(image, 0, 0, newWidth, newHeight);
        return newImage;
    }
}

But this doesn't work fine.

I have an jpg image as example.

The image size is a little more then 4MB (4194587 bytes). Resolution of the image is 2272 x 1704.

So when I try to resize this image with my method above. It first calculate "resizePercentage" as:

float resizePercent = (float)4194304 / oldImgStream.Length;
resizePercent = (float)4194304 / 4194587;
resizePercent = 0.9999325 //(99.99325%)

But because I have set minimum of resizePercent it will be set to 0.8 (80%).

resizePercent = 0.8;

Then it will calculate new width and height with this resizePercent.

And new resolution will be: 1817 x 1363 and image is resized to new resolution. But after it is saved to stream and it read bytes it returns even larger image. Site of the returned image is "5146056 bytes" 5MB

So does anyone has idea how to implement this, or what is wrong this my method so it's returning larger image even resolution is reduced.

I should be able to reduce size of images png, jpg and gif

like image 923
carpics Avatar asked Feb 03 '16 16:02

carpics


1 Answers

There's not a one to one correlation between image file size and resolution, when dealing with compressed bitmaps. That correlation does exist for a true bitmap (BMP, for example), since 1 pixel is always equal to a defined number of bytes (based on the colorspace).

However, when talking about compressed bitmaps, like JPEG, the file size is based on efficiency of the compression (how many colors needs to be encoded overall, how many pixels can combine based on being the same color, or how much dithering can be done to create more pixels of the same color that can be combined). That may end up being less with a lower resolution image, but it also could just as easily remain unchanged or even increase, depending on how efficient the original compression was and how what effect a lesser quantity of pixels overall has on the efficiency of the compression.

Long and short, you can't just apply a simple percentage-based reduction in resolution to ensure a certain file size. The best you could really to is gradually decrease the resolution, testing the file size with each iteration, until it's below your threshold, assuming it does actually go below the threshold. With 4MB to play with, there's conceivably a certain resolution that would definitely be below that, but where that point lies is virtually impossible to calculate.

Additionally, this will vary based on a the type of image format. JPEG functions mostly by trying to spread color out as much as possible (dithering). This is why lower quality JPEGs display artifacts. PNG and GIF are mostly indexed, though, where the overall amount of colors is attempted to be reduced (though both can also employ dithering as well). PNG further complicates matters in that it you can also have lossless PNGs, where all colors and pixels are preserved but a simple compression is applied (much like a ZIP archive) to reduce file size. All of these will have different behavior from each other and at various resolutions.

like image 171
Chris Pratt Avatar answered Sep 28 '22 01:09

Chris Pratt