Domanda

Come ridimensionare un'immagine di un'immagine in C # per una certa dimensione su disco rigido, come 2MiB? C'è un modo migliore di tentativi ed errori (anche se è approssimativa, naturalmente).

Tutte le parole chiave per la ricerca di particolari quando si cerca di trovare la soluzione sul web?

È stato utile?

Soluzione

Dipende da cosa si è disposti a cambiare

  1. Fare la dimensione dell'immagine più piccola
  2. Modificare il formato dell'immagine
  3. Se il formato supporta una compressione lossy, diminuire la qualità
  4. Se si archiviano meta-dati che non è necessario, rimuove
  5. Ridurre il numero di colori (e bit per pixel)
  6. Cambia in un formato tavolozza
  7. passare a un formato di tavolozza e ridurre i colori

E 'difficile indovinare quale sia la dimensione finale del disco sarà, ma se si conosce un punto di partenza si può ottenere una buona stima. La riduzione delle dimensioni sarà probabilmente proporzionale, riducendo i bit per pixel sarà anche probabilmente proporzionale.

Se si cambia il formato, la compressione o la qualità, in realtà è solo una supposizione - dipende fortemente dal contenuto dell'immagine. Si potrebbe probabilmente ottenere una buona gamma provandolo su un corpus di immagini che corrisponde a ciò che si pensa che vedrete.

Altri suggerimenti

È possibile calcolare un livello di informazioni approssimative per l'immagine prendendo la dimensione dell'immagine originale diviso per il numero di pixel:

info = fileSize / (width * height);

Ho un'immagine che è 369636 byte e 1200x800 pixel, in modo che utilizza ~ 0,385 byte per pixel.

Ho una versione più piccola che è 101111 byte e 600x400 pixel, in modo che utilizza ~ 0.4213 byte per pixel.

Quando si riduce l'immagine si vedrà che generalmente conterrà un po 'più informazioni per pixel, in questo caso circa il 9% in più. A seconda del tipo di immagini e di quanto li si restringono, si dovrebbe essere in grado di calcolare una media per quanto le informazioni / pixel razione aumenta (c), in modo da poter calcolare una dimensione approssimativa del file:

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

Da questo si può estrarre una formula per quanto grande si deve fare un'immagine per raggiungere una dimensione file specifico:

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

Questo ti porterà abbastanza vicino alla dimensione del file desiderato. Se si desidera avvicinarsi è possibile ridimensionare l'immagine alla dimensione calcolato, comprimerla e calcolare un nuovo byte per valore ai pixel la dimensione del file che avete ottenuto.

ho raggiunto questo riducendo la qualità fino a raggiungere le dimensioni desiderate.

NB: richiede per aggiungere il riferimento 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;
}
}
}

Se è un 24bit BMP penso che avrebbe bisogno di fare qualcosa di simile:

//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

Se si tratta di un altro tipo BMP come 8 bit BMP anche nella sezione di intestazione ci saranno più i dati che specificano il colore reale di ogni valore da 0 a 255 quindi sarà necessario sottrarre più dalla dimensione totale del file prima che il binario ricerca.

Converti, Ridurre (iterativo, in memoria) e Download (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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top