How to resize an image in C# to a certain hard-disk size?

前端 未结 5 1523
野性不改
野性不改 2020-12-16 19:46

How to resize an image an image in C# to a certain hard-disk size, like 2MiB? Is there a better way than trial and error (even if it\'s approximate, of course).

Any

相关标签:
5条回答
  • 2020-12-16 20:13

    You can calculate an approximate information level for the image by taking the original image size divided by the number of pixels:

    info = fileSize / (width * height);
    

    I have an image that is 369636 bytes and 1200x800 pixels, so it uses ~0.385 bytes per pixel.

    I have a smaller version that is 101111 bytes and 600x400 pixels, so it uses ~0.4213 bytes per pixel.

    When you shrink an image you will see that it generally will contain slightly more information per pixel, in this case about 9% more. Depending on your type of images and how much you shrink them, you should be able to calculate an average for how much the information/pixel ration increases (c), so that you can calculate an approximate file size:

    newFileSize = (fileSize / (width * height)) * (newWidth * newHeight) * c
    

    From this you can extract a formula for how large you have to make an image to reach a specific file size:

    newWidth * newHeight = (newFileSize / fileSize) * (width * height) / c
    

    This will get you pretty close to the desired file size. If you want to get closer you can resize the image to the calculated size, compress it and calculate a new bytes per pixel value from the file size that you got.

    0 讨论(0)
  • 2020-12-16 20:13

    If it's a 24bit BMP i think you would need to do something like this:

    //initial size =  WxH
    long bitsperpixel = 24; //for 24 bit BMP
    double ratio;
    long size = 2 * 1 << 20;//2MB = 2 * 2^20
    size -= 0x35;//subtract the BMP header size from it
    long newH, newW, left, right, middle,BMProwsize;
    left = 1;
    right = size;//binary search for new width and height
    while (left < right)
    {
        middle = (left + right + 1) / 2;
        newW = middle;
        ratio = Convert.ToDouble(newW) / Convert.ToDouble(W);
        newH = Convert.ToInt64(ratio * Convert.ToDouble(H));
        BMProwsize = 4 * ((newW * bitsperpixel + 31) / 32);
        //row size must be multiple of 4
        if (BMProwsize * newH <= size)
            left = middle;
        else
            right = middle-1;                
    }
    
    newW = left;
    ratio = Convert.ToDouble(newW) / Convert.ToDouble(W);
    newH = Convert.ToInt64(ratio * Convert.ToDouble(H));
    //resize image to newW x newH and it should fit in <= 2 MB
    

    If it is a different BMP type like 8 bit BMP also in the header section there will be more data specifying the actual color of each value from 0 to 255 so you will need to subtract more from the total file size before the binary search.

    0 讨论(0)
  • 2020-12-16 20:25

    It depends on what you are willing to change

    1. Make the size of the image smaller
    2. Change the format of the image
    3. If the format supports a lossy compression, decrease the quality
    4. If you are storing meta-data that you don't need, remove it
    5. Reduce the number of colors (and bits per pixel)
    6. Change to a paletted format
    7. Change to a paletted format and reduce the colors

    It's hard to guess what the final disk size will be, but if you know a starting point you can get a pretty good estimate. Reducing the size will probably be proportional, reducing the bits per pixel will also likely be proportional.

    If you change the format, compression or quality, it's really just a guess -- depends highly on the image content. You could probably get a good range by trying it on a corpus of images that matches what you think you'll be seeing.

    0 讨论(0)
  • 2020-12-16 20:25

    Convert, Reduce (Iterative, In Memory) & Download (MVC)

    public ActionResult ReduceFileSize(string ImageURL, long MAX_PHOTO_SIZE) //KB
    {
        var photo = Server.MapPath("~/" + ImageURL); //Files/somefiles/2018/DOC_82401583cb534b95a10252d29a1eb4ee_1.jpg
    
        var photoName = Path.GetFileNameWithoutExtension(photo);
    
        var fi = new FileInfo(photo);
    
        //const long MAX_PHOTO_SIZE = 100; //KB //109600;
    
        var MAX_PHOTO_SIZE_BYTES = (MAX_PHOTO_SIZE * 1000);
    
        if (fi.Length > MAX_PHOTO_SIZE_BYTES)
        {
            using (var image = Image.FromFile(photo))
            {
                using (var mstream = DownscaleImage(image, MAX_PHOTO_SIZE_BYTES))
                {
    
                    //Convert the memorystream to an array of bytes.
                    byte[] byteArray = mstream.ToArray();
                    //Clean up the memory stream
                    mstream.Flush();
                    mstream.Close();
                    // Clear all content output from the buffer stream
                    Response.Clear();
                    // Add a HTTP header to the output stream that specifies the default filename
                    // for the browser's download dialog
                    Response.AddHeader("Content-Disposition", "attachment; filename=" + fi.Name);
                    // Add a HTTP header to the output stream that contains the 
                    // content length(File Size). This lets the browser know how much data is being transfered
                    Response.AddHeader("Content-Length", byteArray.Length.ToString());
                    // Set the HTTP MIME type of the output stream
                    Response.ContentType = "application/octet-stream";
                    // Write the data out to the client.
                    Response.BinaryWrite(byteArray);
    
                }
            }
        }
        else
        {
            return null;
        }
    
        return null;
    }
    
    
    
    private static MemoryStream DownscaleImage(Image photo, long MAX_PHOTO_SIZE_BYTES)
    {
        MemoryStream resizedPhotoStream = new MemoryStream();
    
        long resizedSize = 0;
        var quality = 93;
        //long lastSizeDifference = 0;
        do
        {
            resizedPhotoStream.SetLength(0);
    
            EncoderParameters eps = new EncoderParameters(1);
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)quality);
            ImageCodecInfo ici = GetEncoderInfo("image/jpeg");
    
            photo.Save(resizedPhotoStream, ici, eps);
            resizedSize = resizedPhotoStream.Length;
    
            //long sizeDifference = resizedSize - MAX_PHOTO_SIZE;
            //Console.WriteLine(resizedSize + "(" + sizeDifference + " " + (lastSizeDifference - sizeDifference) + ")");
            //lastSizeDifference = sizeDifference;
            quality--;
    
        } while (resizedSize > MAX_PHOTO_SIZE_BYTES);
    
        resizedPhotoStream.Seek(0, SeekOrigin.Begin);
    
        return resizedPhotoStream;
    }
    
    private static ImageCodecInfo GetEncoderInfo(String mimeType)
    {
        int j;
        ImageCodecInfo[] encoders;
        encoders = ImageCodecInfo.GetImageEncoders();
        for (j = 0; j < encoders.Length; ++j)
        {
            if (encoders[j].MimeType == mimeType)
                return encoders[j];
        }
        return null;
    }
    
    0 讨论(0)
  • 2020-12-16 20:30

    I achieved this by reducing the quality until I reached my desired size.

    NB: Requires you to add the System.Drawing reference.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Drawing.Drawing2D;
    
    namespace PhotoShrinker
    {
    class Program
    {
    /// <summary>
    /// Max photo size in bytes
    /// </summary>
    const long MAX_PHOTO_SIZE = 409600;
    
    static void Main(string[] args)
    {
        var photos = Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.jpg");
    
        foreach (var photo in photos)
        {
            var photoName = Path.GetFileNameWithoutExtension(photo);
    
            var fi = new FileInfo(photo);
            Console.WriteLine("Photo: " + photo);
            Console.WriteLine(fi.Length);
    
            if (fi.Length > MAX_PHOTO_SIZE)
            {
                using (var image = Image.FromFile(photo)) 
                {
                      using (var stream = DownscaleImage(image))
                      {
                            using (var file = File.Create(photoName + "-smaller.jpg"))
                            {
                                stream.CopyTo(file);
                            }
                      }
                }
                Console.WriteLine("File resized.");
            }
            Console.WriteLine("Done.")
            Console.ReadLine();
        }
    
    }
    
    private static MemoryStream DownscaleImage(Image photo)
    {
        MemoryStream resizedPhotoStream = new MemoryStream();
    
        long resizedSize = 0;
        var quality = 93;
        //long lastSizeDifference = 0;
        do
        {
            resizedPhotoStream.SetLength(0);
    
            EncoderParameters eps = new EncoderParameters(1);
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)quality);
            ImageCodecInfo ici = GetEncoderInfo("image/jpeg");
    
            photo.Save(resizedPhotoStream, ici, eps);
            resizedSize = resizedPhotoStream.Length;
    
            //long sizeDifference = resizedSize - MAX_PHOTO_SIZE;
            //Console.WriteLine(resizedSize + "(" + sizeDifference + " " + (lastSizeDifference - sizeDifference) + ")");
            //lastSizeDifference = sizeDifference;
            quality--;
    
        } while (resizedSize > MAX_PHOTO_SIZE);
    
        resizedPhotoStream.Seek(0, SeekOrigin.Begin);
    
        return resizedPhotoStream;
    }
    
    private static ImageCodecInfo GetEncoderInfo(String mimeType)
    {
        int j;
        ImageCodecInfo[] encoders;
        encoders = ImageCodecInfo.GetImageEncoders();
        for (j = 0; j < encoders.Length; ++j)
        {
            if (encoders[j].MimeType == mimeType)
                return encoders[j];
        }
        return null;
    }
    }
    }
    
    0 讨论(0)
提交回复
热议问题