Как изменить размер изображения в C # до определенного размера жесткого диска? - PullRequest
10 голосов
/ 11 августа 2009

Как изменить размер изображения в C # до определенного размера жесткого диска, например, 2 МБ? Есть ли лучший способ, чем метод проб и ошибок (даже если он приблизительный, конечно).

Какие конкретные ключевые слова нужно искать при поиске решения в Интернете?

Ответы [ 5 ]

9 голосов
/ 11 августа 2009

Вы можете рассчитать приблизительный уровень информации для изображения, взяв исходный размер изображения, деленный на количество пикселей:

info = fileSize / (width * height);

У меня есть изображение размером 369636 байт и 1200x800 пикселей, поэтому оно использует ~ 0,385 байт на пиксель.

У меня есть уменьшенная версия, которая составляет 101111 байт и 600x400 пикселей, поэтому она использует ~ 0,4213 байта на пиксель.

Когда вы уменьшите изображение, вы увидите, что оно обычно будет содержать немного больше информации на пиксель, в данном случае на 9% больше. В зависимости от вашего типа изображений и от того, насколько вы их уменьшаете, вы сможете рассчитать среднее значение, на которое увеличивается соотношение информации / пикселей (с), чтобы можно было рассчитать приблизительный размер файла:

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

Из этого вы можете извлечь формулу того, насколько большим вам нужно сделать изображение для достижения определенного размера файла:

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

Это приблизит вас к желаемому размеру файла. Если вы хотите приблизиться, вы можете изменить размер изображения до рассчитанного размера, сжать его и вычислить новое значение в байтах на пиксель из полученного вами размера файла.

4 голосов
/ 21 июля 2014

Я достиг этого, уменьшив качество, пока не достиг желаемого размера.

Примечание: требуется добавить ссылку 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;
}
}
}
1 голос
/ 11 августа 2009

Если это 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, поэтому вам нужно будет вычесть больше из общего размера файла перед двоичным поиск.

0 голосов
/ 09 января 2019

Преобразование, уменьшение (итеративное, в памяти) и загрузка (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;
}
0 голосов
/ 19 августа 2009

Зависит от того, что вы готовы изменить

  1. Уменьшить размер изображения
  2. Изменить формат изображения
  3. Если формат поддерживает сжатие с потерями, уменьшите качество
  4. Если вы храните метаданные, которые вам не нужны, удалите их
  5. Уменьшить количество цветов (и бит на пиксель)
  6. Изменить на формат палитры
  7. Перейдите в формат палитры и уменьшите цвета

Трудно догадаться, каким будет конечный размер диска, но если вы знаете отправную точку, вы можете получить довольно хорошую оценку. Уменьшение размера, вероятно, будет пропорциональным, уменьшение количества бит на пиксель также, вероятно, будет пропорциональным.

Если вы измените формат, сжатие или качество, это всего лишь предположение - сильно зависит от содержимого изображения. Вы могли бы, вероятно, получить хороший диапазон, попробовав его на корпусе изображений, который соответствует тому, что, как вы думаете, вы увидите.

...