Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
83 views
in Technique[技术] by (71.8m points)

c# - Shrink image file using ImageSharp

I need to shrink every image I get that is more than 10MB. File types are png, jpg and gif. I saw that ImageSharp has an option to resize an image:

Resize(new ResizeOptions
{
    Mode = ResizeMode.Max,
    Size = new Size(maxFileSize)
}

I saw a lot of examples using the resize function with width and height, but none using this one size option, and no documentation explain exactly what "size" means.

I've tried the following: Shrinking an image of 22.2MB using maxFileSize=1024 yielded a picture of 527.9MB. "Shrinking" the same image with maxFileSize=1024*2*10 yielded a 47.4MB picture.

How can I shrink an image to a size of roughly 10MB (can be a bit less)? My goal here is to limit to 10MB, and, if exceeded, reduce the image to the maximal possible size under 10MB without affecting the ratio.

question from:https://stackoverflow.com/questions/65896679/compress-image-in-net-core-mvc

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I don't think that there is an easy (and reliable) way to compute the compressed size of an image, without, well, compressing it.

While ImageSharp doesn't seem to offer a native API for this, we can use existing functionality to find an optimal size which satisfies our constraints.

Idea:

  1. We can assume that smaller images take less space on disk, i.e., the file size is correlated with the number of pixels. This may not hold for some sophisticated compression algorithms on very similar image sizes; however, even then we should still able to find a good estimation for the perfect image size.
  2. Pick some rough lower and upper bounds for the image size. In the best case, our perfect image size lays somewhere in between.
  3. Perform a binary search between the aforementioned bounds, by repeatedly resizing and compressing our image, until it has our desired file size.

Corresponding C# code:

// Load original image
using Image image = Image.Load("image.jpg");

// Configure encoder
var imageEncoder = new JpegEncoder
{
    Quality = 95,
    Subsample = JpegSubsample.Ratio420
};

// Resize image, until it fits the desired file size and bounds
int min = ESTIMATED_MINIMUM_WIDTH;
int max = ESTIMATED_MAXIMUM_WIDTH;
int bestWidth = min;
using var tmpStream = new MemoryStream();
while(min <= max)
{
    // Resize image
    int width = (min + max) / 2;
    using var resizedImage = image.Clone(op =>
    {
        op.Resize(new ResizeOptions
        {
            Mode = ResizeMode.Max,
            Size = new Size(width, MAXIMUM_HEIGHT)
        });
    });

    // Compress image
    tmpStream.SetLength(0);
    resizedImage.Save(tmpStream, imageEncoder);

    // Check file size of resized image
    if(tmpStream.Position < MAXIMUM_FILE_SIZE)
    {
        // The current width satisfies the size constraint
        bestWidth = width;

        // Try to make image bigger again
        min = width + 1;
    }
    else
    {
        // Image is still too large, continue shrinking
        max = width - 1;
    }
}

// Resize and store final image
image.Mutate(op =>
{
    op.Resize(new ResizeOptions
    {
        Mode = ResizeMode.Max,
        Size = new Size(bestWidth, MAXIMUM_HEIGHT)
    });
});

// Store resized image
image.Save("image-resized.jpg", imageEncoder);

This loads the image "image.jpg" and finds the width bestWidth which yields a file size smaller than, but near to MAXIMUM_FILE_SIZE.

The other constants are defined as follows:

  • ESTIMATED_MINIMUM_WIDTH: The estimated lower bound for the perfect image width. The image will never become smaller than this. Should be at least 0.
  • ESTIMATED_MAXIMUM_WIDTH: The estimated upper bound for the perfect image width. The image will never become larger than this. Should be at most image.Width.
  • MAXIMUM_HEIGHT: The maximum image height. This is helpful if there are other constraints than the file size (e.g., the image must fit a certain screen size); else, this can be simply set to image.Height.

While binary search offers good algorithmic complexity, this can still be quite slow for very large images. If time is important (the question doesn't specify this), the performance can be improved by using good initial estimations of the upper and lower bounds for bestWidth, e.g., by linearly interpolating the file size through the ratio of pixels to bytes.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...