9

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

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

+2

什么样的形象? – 2009-08-11 06:03:56

回答

0

这取决于你愿意改变

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

很难猜测最终的磁盘大小将是什么,但如果你知道一个起点,你可以得到一个很好的评估。缩小尺寸可能会成比例,减少每像素的位数也可能成比例。

如果您更改格式,压缩或质量,它实际上只是一个猜测 - 高度依赖图像内容。您可以通过在与您认为您会看到的相匹配的图像文集上进行尝试来获得良好的范围。

7

您可以通过将原始图像尺寸的像素数除以计算图像的近似信息化水平:

info = fileSize/(width * height); 

我有一个形象,那就是369636个字节和1200x800像素,因此它使用〜每像素0.385字节。

我有一个更小的版本是101111字节和600x400像素,所以它使用〜0.4213字节/像素。

当你缩小图像时,你会发现它通常会包含更多的每像素信息,在这种情况下,大约增加9%。根据您的图像类型以及缩小它们的程度,您应该可以计算信息/像素比例增加的平均数(c),以便您可以计算大致的文件大小:

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

从这里就可以提取你有多大使图像达到一个特定的文件大小的公式:

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

这将让你很接近所需的文件大小。如果你想更近一点,你可以调整图像大小的计算大小,压缩它,并从你得到的文件大小计算每个像素值的新字节。

1

如果它是一个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,因此您需要从二进制搜索前的总文件大小中减去更多。

2

我通过降低质量,直到我达到我期望的大小来实现这一点。

注:需要您添加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; 
} 
} 
} 
+0

谢谢你,伟大的工作示例。我改变它一次降低质量5个单位,并用它在我的asp.net – 2015-02-06 13:04:58

+1

很高兴我可以帮助别人!我注意到 Image.FromFile(照片)没有正确处理!我更新了我的代码以正确处置。 – 2015-02-06 18:09:21