Pregunta

Cómo cambiar el tamaño de una imagen una imagen en C# para un determinado disco duro de tamaño, como 2MiB?Existe una mejor manera de prueba y error (aunque sea aproximado, por supuesto).

Cualquier particular, las palabras clave a buscar cuando se trata de encontrar la solución en la web?

¿Fue útil?

Solución

Depende de lo que usted está dispuesto a cambiar

  1. Hacer que el tamaño de la imagen más pequeña
  2. Cambiar el formato de la imagen
  3. Si el formato es compatible con una compresión con pérdida, disminución de la calidad
  4. Si usted está almacenando los meta-datos que no necesita, lo quite
  5. Reducir el número de colores (y bits por píxel)
  6. Cambiar a una paleta de formato
  7. Cambiar a una paleta de formato y reducir los colores

Es difícil adivinar lo que el último disco de mayor tamaño, pero si usted sabe un punto de partida que puede obtener una buena estimación.Reducir el tamaño probablemente será proporcional, la reducción de los bits por píxel también es probable que ser proporcional.

Si cambia el formato de la compresión o de la calidad, es realmente sólo una conjetura -- depende mucho del contenido de la imagen.Usted probablemente podría conseguir un buen rango de probarlo en un corpus de imágenes que coincide con lo que creo que vamos a ver.

Otros consejos

Se puede calcular un nivel de información aproximada de la imagen al tomar el tamaño de la imagen original dividida por el número de píxeles:

info = fileSize / (width * height);

Tengo una imagen que es 369636 bytes y 1200x800 píxeles, por lo que utiliza ~ 0.385 bytes por píxel.

Tengo una versión más pequeña que es 101111 bytes y 600x400 píxeles, por lo que utiliza ~ 0.4213 bytes por píxel.

Al reducir una imagen verá que contendrá generalmente un poco más de información por píxel, en este caso alrededor de un 9% más. Dependiendo de su tipo de imágenes y la cantidad que reducir su tamaño, debe ser capaz de calcular un promedio de la cantidad de la ración de la información / píxel aumenta (c), por lo que se puede calcular un tamaño aproximado:

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

A partir de esto se puede extraer una fórmula para el tamaño que tiene que hacer una imagen para llegar a un tamaño de archivo específico:

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

Esto le dará bastante cerca del tamaño de archivo deseado. Si desea obtener más cerca se puede cambiar el tamaño de la imagen al tamaño calculado, comprimirlo y calcular una nueva bytes por valor de píxeles entre el tamaño del archivo que ha llegado.

he logrado esto mediante la reducción de la calidad hasta que llegué a mi tamaño deseado.

Nota: se requiere para agregar la referencia 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;
}
}
}

Si se trata de una de 24 bits BMP creo que tendría que hacer algo como esto:

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

Si se trata de un tipo BMP diferente como 8 bits BMP también en la sección de encabezado habrá más datos que especifican el color real de cada valor de 0 a 255 por lo que tendrá que restar más del tamaño total del archivo antes de que el binario búsqueda.

convertir, reducir (iterativo, en memoria) y Descarga (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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top