C#の画像を2MiBのような特定のハードディスクサイズにサイズ変更する方法は? 試行錯誤よりも良い方法はありますか (もちろん、概算であっても)。
Web で解決策を見つけようとするときに検索する特定のキーワードはありますか?
C#の画像を2MiBのような特定のハードディスクサイズにサイズ変更する方法は? 試行錯誤よりも良い方法はありますか (もちろん、概算であっても)。
Web で解決策を見つけようとするときに検索する特定のキーワードはありますか?
元の画像サイズをピクセル数で割ることにより、画像のおおよその情報レベルを計算できます。
info = fileSize / (width * height);
369636 バイトで 1200x800 ピクセルの画像があるため、1 ピクセルあたり ~0.385 バイトを使用します。
私は 101111 バイトと 600x400 ピクセルの小さいバージョンを持っているので、ピクセルあたり ~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
{
/// <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;
}
}
}
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
ヘッダー セクションにも 8 ビット BMP のような異なる BMP タイプがある場合は、0 から 255 までの各値の実際の色を指定するデータが増えるため、バイナリ検索の前に合計ファイル サイズからさらに多くを差し引く必要があります。
何を変えたいかによる
最終的なディスク サイズを推測するのは困難ですが、開始点がわかっている場合は、かなり正確な見積もりを得ることができます。サイズの縮小はおそらく比例し、ピクセルあたりのビット数の縮小も比例する可能性があります。
フォーマット、圧縮、または品質を変更する場合、それは単なる推測であり、画像の内容に大きく依存します。表示されると思われるものと一致する画像のコーパスで試してみると、おそらく適切な範囲が得られるでしょう。