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

如何将图像中的图像大小调整为C#到某个硬盘大小,如2MiB? 有没有比试验和错误更好的方法(当然,即使它是近似的)。

在尝试在网络上找到解决方案时要搜索的任何特定关键字?

这取决于你愿意改变什么

  1. 使图像的大小更小
  2. 更改图像的格式
  3. 如果格式支持有损压缩,请降低质量
  4. 如果要存储不需要的元数据,请将其删除
  5. 减少颜色数(和每像素位数)
  6. 更改为调色板格式
  7. 更改为调色板格式并减少颜色

很难猜出最终的磁盘大小是多少,但如果你知道一个起点,你可以得到一个很好的估计。 减小尺寸可能是成比例的,减少每像素的比特也可能是成比例的。

如果您更改格式,压缩或质量,它实际上只是猜测 – 在很大程度上取决于图像内容。 你可以通过在符合你认为你会看到的图像的语料库上进行尝试来获得一个很好的范围。

您可以通过将原始图像大小除以像素数来计算图像的近似信息级别:

info = fileSize / (width * height); 

我有一个369636字节和1200×800像素的图像,因此每像素使用~0.385字节。

我有一个较小的版本,101111字节和600×400像素,所以它使用每像素约0.4213字节。

缩小图像时,您会发现每个像素通常会包含更多的信息,在这种情况下大约多9%。 根据您的图像类型和缩小的程度,您应该能够计算信息/像素比例增加的平均值(c),以便您可以计算出近似的文件大小:

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

从中您可以提取一个公式,以确定图像达到特定文件大小所需的大小:

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

这将使您非常接近所需的文件大小。 如果您想要靠近,可以将图像调整为计算大小,压缩它并根据您获得的文件大小计算每像素值的新字节数。

我通过降低质量直到达到我想要的尺寸来实现这一目标。

注意:需要您添加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 { ///  /// Max photo size in bytes ///  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; } } } 

如果它是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 

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