简体   繁体   English

如何将C#中的图像大小调整为某个硬盘大小?

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

How to resize an image an image in C# to a certain hard-disk size, like 2MiB? 如何将图像中的图像大小调整为C#到某个硬盘大小,如2MiB? Is there a better way than trial and error (even if it's approximate, of course). 有没有比试验和错误更好的方法(当然,即使它是近似的)。

Any particular keywords to search for when trying to find the solution on the web? 尝试在网络上找到解决方案时要搜索的任何特定关键字?

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. 我有一个369636字节和1200x800像素的图像,所以它每像素使用~0.385字节。

I have a smaller version that is 101111 bytes and 600x400 pixels, so it uses ~0.4213 bytes per pixel. 我有一个101111字节和600x400像素的较小版本,因此它每像素使用~0.4213字节。

When you shrink an image you will see that it generally will contain slightly more information per pixel, in this case about 9% more. 缩小图像时,您会发现每个像素通常会包含更多的信息,在这种情况下大约多9%。 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: 根据您的图像类型以及缩小它们的数量,您应该能够计算信息/像素比例增加的平均值(c),以便您可以计算出近似的文件大小:

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. 如果您想要靠近,可以将图像调整为计算大小,压缩它并根据您获得的文件大小计算每像素值的新字节数。

I achieved this by reducing the quality until I reached my desired size. 我通过降低质量直到达到我想要的尺寸来实现这一目标。

NB: Requires you to add the System.Drawing reference. 注意:需要您添加System.Drawing引用。

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;
}
}
}

If it's a 24bit BMP i think you would need to do something like this: 如果它是24位BMP我认为你需要做这样的事情:

//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. 如果它在标题部分也是不同的BMP类型,如8位BMP,则会有更多数据指定每个值的实际颜色,从0到255,因此您需要在二进制搜索之前从总文件大小中减去更多。

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. 你可以通过在符合你认为你会看到的图像的语料库上进行尝试来获得一个很好的范围。

Convert, Reduce (Iterative, In Memory) & Download (MVC) 转换,减少(迭代,内存)和下载(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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM