سؤال

كيفية تغيير حجم الصورة صورة في C# معينة على القرص الثابت الحجم ، مثل 2MiB?هل هناك طريقة أفضل من التجربة والخطأ (حتى لو كانت تقريبية بالطبع).

أي الكلمات الرئيسية في البحث عندما تحاول أن تجد الحل على شبكة الإنترنت ؟

هل كانت مفيدة؟

المحلول

ذلك يعتمد على ما أنت على استعداد للتغيير

  1. جعل حجم الصورة أصغر
  2. تغيير تنسيق الصورة
  3. إذا كان التنسيق يدعم ضغط ضياع، فقم بخفض الجودة
  4. إذا كنت تقوم بتخزين بيانات meta التي لا تحتاج إليها، فقم بإزالتها
  5. تقليل عدد الألوان (والبسات لكل بكسل)
  6. التغيير إلى تنسيق يلجأ
  7. التغيير إلى تنسيق يلجأ وتقليل الألوان

من الصعب تخمين ما سيكون حجم القرص النهائي، ولكن إذا كنت تعرف نقطة انطلاق يمكنك الحصول على تقدير جيد جدا. من المحتمل أن يكون تقليل حجم الحجم متناسبا، فسيكون الحد من البتات لكل بكسل متناسبا.

إذا قمت بتغيير التنسيق أو الضغط أو الجودة، فهذا مجرد تخمين - يعتمد بشكل كبير على محتوى الصورة. ربما يمكنك الحصول على مجموعة جيدة من خلال تجربة ذلك على كائن من الصور التي تتطابق مع ما تعتقد أنك سترى.

نصائح أخرى

يمكنك حساب تقريبي المعلومات مستوى الصورة عن طريق أخذ حجم الصورة الأصلي مقسوما على عدد وحدات البكسل:

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

هذا سوف تحصل على قريبة جدا من المطلوب حجم الملف.إذا كنت ترغب في الحصول على أقرب يمكنك تغيير حجم الصورة إلى احتساب حجم ضغط عليه حساب جديد بايت لكل بكسل قيمة من حجم الملف الذي لديك.

لقد حققت هذا عن طريق تقليل الجودة حتى وصلت إلى الحجم المطلوب.

NB: يتطلب منك إضافة مرجع النظام.

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;
}
}
}

إذا كان برنامج BMP 24bit، أعتقد أنك ستحتاج إلى القيام بشيء مثل هذا:

//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، لذا ستحتاج إلى طرح المزيد من إجمالي حجم الملف قبل البحث الثنائي.

تحويل، تقليل (تكريري، في الذاكرة) وتنزيل (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