문제

C#의 이미지를 2mib와 같은 특정 하드 디스크 크기로 크기를 조정하는 방법은 무엇입니까? 시행 착오보다 더 나은 방법이 있습니까 (물론 근사 가더라도).

웹에서 솔루션을 찾을 때 검색 할 특정 키워드가 있습니까?

도움이 되었습니까?

해결책

그것은 당신이 기꺼이 바꾸려는 것에 달려 있습니다

  1. 이미지의 크기를 더 작게 만듭니다
  2. 이미지의 형식을 변경하십시오
  3. 형식이 손실 압축을 지원하면 품질을 줄입니다.
  4. 필요하지 않은 메타 데이터를 저장하는 경우 제거하십시오.
  5. 색상 수 (및 픽셀 당 비트) 감소
  6. 팔레트 형식으로 변경하십시오
  7. 팔레트 형식으로 변경하고 색상을 줄입니다

최종 디스크 크기가 무엇인지 추측하기는 어렵지만 출발점을 알고 있다면 꽤 좋은 견적을 얻을 수 있습니다. 크기를 줄이는 것은 아마도 비례 할 수 있으므로 픽셀 당 비트를 줄이는 것도 비례 할 것입니다.

형식, 압축 또는 품질을 변경하면 실제로 추측 일뿐입니다. 이미지 내용에 크게 의존합니다. 당신은 아마 당신이보고있는 것과 일치하는 이미지의 코퍼스에서 시도함으로써 좋은 범위를 얻을 수 있습니다.

다른 팁

원본 이미지 크기를 픽셀 수로 나눈 후 이미지의 대략적인 정보 수준을 계산할 수 있습니다.

info = fileSize / (width * height);

369636 바이트와 1200x800 픽셀의 이미지가 있으므로 픽셀 당 ~ 0.385 바이트를 사용합니다.

나는 101111 바이트와 600x400 픽셀의 작은 버전을 가지고 있으므로 픽셀 당 ~ 0.4213 바이트를 사용합니다.

이미지를 축소하면 이미지가 일반적으로 픽셀 당 약간 더 많은 정보가 포함되어 있음을 알 수 있습니다.이 경우 약 9% 더. 이미지 유형과 수축량에 따라 정보/픽셀 배급량이 얼마나 증가하는지 (c)에 대해 평균을 계산하여 대략적인 파일 크기를 계산할 수 있어야합니다.

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

이를 통해 특정 파일 크기에 도달하기 위해 이미지를 만드는 데 얼마나 큰 공식을 추출 할 수 있습니다.

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

이렇게하면 원하는 파일 크기에 거의 가깝습니다. 가까이 가려면 이미지를 계산 된 크기로 크기로 조정하고 압축하고 얻은 파일 크기에서 픽셀 값 당 새 바이트를 계산할 수 있습니다.

원하는 크기에 도달 할 때까지 품질을 줄임으로써 이것을 달성했습니다.

NB : System.Drawing Reference를 추가해야합니다.

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 사이의 더 많은 데이터가 있으므로 이진 검색 전에 총 파일 크기에서 더 많은 것을 빼야합니다.

변환, 축소 (반복, 메모리) 및 다운로드 (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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top