Pergunta

Como redimensionar uma imagem de uma imagem em C # para um determinado tamanho de disco rígido, como 2MiB? Existe uma maneira melhor do que tentativa e erro (mesmo que seja aproximada, é claro).

As palavras-chave específicas para procurar ao tentar encontrar a solução na web?

Foi útil?

Solução

Depende do que você está disposto a mudança

  1. Faça o tamanho da imagem menor
  2. Alterar o formato da imagem
  3. Se o formato suporta uma compressão com perdas, diminuir a qualidade
  4. Se você estiver armazenando meta-dados que você não precisa, removê-lo
  5. Reduzir o número de cores (e bits por pixel)
  6. Mudar para um formato paletted
  7. mude para um formato paletted e reduzir as cores

É difícil adivinhar o que o tamanho final do disco vai ser, mas se você conhece um ponto de partida você pode obter uma boa estimativa bonita. Reduzir o tamanho provavelmente será proporcional, reduzindo os bits por pixel também irá provavelmente ser proporcional.

Se você alterar o formato, compressão ou qualidade, é realmente apenas uma suposição - depende muito do conteúdo da imagem. Você provavelmente poderia ter uma boa gama, tentando-o em um corpus de imagens que corresponde ao que você acha que vai estar vendo.

Outras dicas

Você pode calcular um nível de informações aproximadas para a imagem, tomando o tamanho da imagem original, dividido pelo número de pixels:

info = fileSize / (width * height);

Eu tenho uma imagem que é 369636 bytes e 1200x800 pixels, para que ele usa ~ 0,385 bytes por pixel.

Eu tenho uma versão menor que é 101111 bytes e 600x400 pixels, por isso utiliza ~ 0.4213 bytes por pixel.

Quando você encolher uma imagem que você vai ver que ele conterão geralmente ligeiramente mais informações por pixel, neste caso cerca de 9% mais. Dependendo do seu tipo de imagens e quanto você reduzi-los, você deve ser capaz de calcular uma média de quanto as informações / pixel de racionamento aumenta (C), de modo que você pode calcular um tamanho aproximado do arquivo:

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

A partir desta você pode extrair uma fórmula para o quão grande você tem que fazer uma imagem para atingir um tamanho de arquivo específico:

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

Isto fará você muito perto do tamanho do arquivo desejado. Se você quiser obter mais perto que você pode redimensionar a imagem para o tamanho calculado, comprimi-lo e calcular um novo bytes por valor de pixels entre o tamanho do arquivo que você tem.

Eu consegui isso, reduzindo a qualidade até chegar o meu tamanho desejado.

NB:. Requer que você adicionar a referência 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 é um 24bit BMP eu acho que você precisa fazer algo como isto:

//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 é um tipo diferente BMP como 8 bit BMP também na seção de cabeçalho haverá mais dados que especificam a cor real de cada valor de 0 a 255 para que você vai precisar para subtrair mais do tamanho total do arquivo antes do binário pesquisar.

converter, reduzir (iterativo, In Memory) 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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top