Какой уровень качества Image.Save() использует для файлов JPEG?

Я просто удивился, когда загрузил файл jpg, развернулся и сохранил его с качеством 100, а размер был почти в 4 раза больше оригинала. Для дальнейшего изучения я открываю и сохраняю без явной настройки качества и размер файла был точно таким же. Я подумал, что это потому, что ничего не изменилось, поэтому он просто записывает те же самые биты обратно в файл. Чтобы проверить это предположение, я нарисовал большую жирную линию по диагонали по всему изображению и снова сохранил без установки качества (на этот раз я ожидал, что файл подпрыгнет, потому что он будет "грязным"), но он уменьшился на ~10Kb!

На данный момент я действительно не понимаю, что происходит, когда я просто вызываю Image.Save() без указания качества сжатия. Как размер файла настолько близок (после изменения изображения) к исходному размеру, когда качество еще не установлено, когда я установил качество на 100 (в основном, без сжатия), размер файла в несколько раз больше исходного?

Я прочитал документацию по Image.Save(), и в ней нет подробностей о том, что происходит за кулисами. Я гуглил в разные стороны, но не могу найти никакой дополнительной информации, которая бы объясняла, что я вижу. Я работаю в течение 31 часа подряд, поэтому, возможно, я упускаю что-то очевидное;0)

Все это произошло, когда я реализовал некоторые библиотечные методы для сохранения изображений в базе данных. Я перегрузил наш метод "SaveImage", чтобы разрешить явно устанавливать качество, и во время моего тестирования я натолкнулся на странные (для меня) результаты, описанные выше. Любой свет, который вы можете пролить, будет оценен.

Вот код, который проиллюстрирует то, что я испытываю:

string filename = @"C:\temp\image testing\hh.jpg";
string destPath = @"C:\temp\image testing\";

using(Image image = Image.FromFile(filename))
{
    ImageCodecInfo codecInfo = ImageUtils.GetEncoderInfo(ImageFormat.Jpeg);

    //  Set the quality
    EncoderParameters parameters = new EncoderParameters(1);

    // Quality: 10
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 10L);
    image.Save(destPath + "10.jpg", codecInfo, parameters);

    // Quality: 75
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 75L);
    image.Save(destPath + "75.jpg", codecInfo, parameters);

    // Quality: 100
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 100L);
    image.Save(destPath + "100.jpg", codecInfo, parameters);

    //  default
    image.Save(destPath + "default.jpg", ImageFormat.Jpeg);

    //  Big line across image
    using (Graphics g = Graphics.FromImage(image))
    {
        using(Pen pen = new Pen(Color.Red, 50F))
        {
            g.DrawLine(pen, 0, 0, image.Width, image.Height);
        }
    }

    image.Save(destPath + "big red line.jpg", ImageFormat.Jpeg);
}

public static ImageCodecInfo GetEncoderInfo(ImageFormat format)
{
    return ImageCodecInfo.GetImageEncoders().ToList().Find(delegate(ImageCodecInfo codec)
    {
        return codec.FormatID == format.Guid;
    });
}

4 ответа

Решение

Используя отражатель, получается Image.Save() сводится к функции GDI+ GdipSaveImageToFile, с encoderParams НОЛЬ. Поэтому я думаю, что вопрос в том, что делает кодировщик JPEG, когда он получает ноль encoderParams, 75% было предложено здесь, но я не могу найти твердую ссылку.

РЕДАКТИРОВАТЬ Вы, вероятно, могли бы убедиться в этом сами, запустив свою программу выше для значений качества 1..100 и сравнив их с jpg, сохраненным с качеством по умолчанию (используя, скажем, fc.exe /B)

IIRC, это 75%, но я не помню, где я читал это.

Когда вы сохраняете изображение в виде файла JPEG с уровнем качества <100%, вы вносите в сохраненное изображение артефакты, которые являются побочным эффектом процесса сжатия. Вот почему повторное сохранение изображения на 100% фактически увеличивает размер вашего файла по сравнению с оригиналом - по иронии судьбы, в растровом изображении больше информации.

По этой же причине вам всегда следует пытаться сохранить файл в формате без потерь (например, PNG), если вы собираетесь впоследствии вносить какие-либо изменения в свой файл, иначе вы будете влиять на качество вывода с помощью многочисленных преобразований с потерями.

Я не очень разбираюсь в методе Image.Save, но могу вам сказать, что добавление этой жирной линии логически уменьшит размер изображения jpg. Это связано с тем, как jpg сохраняется (и кодируется).

Толстая черная линия обеспечивает очень простое и меньшее кодирование (если я правильно помню, это происходит в основном после дискретного косинусного преобразования), поэтому измененное изображение может быть сохранено с использованием меньшего количества данных (байтов).

этапы кодирования jpg

Что касается изменений в размере (без добавления строки), я не уверен, какое изображение вы снова открыли и сохранили

Для дальнейшего изучения я открываю и сохраняю без явной настройки качества и размер файла был точно такой же

Если вы открыли старое (исходное изображение нормального размера) и сохранили его, то, возможно, сжатие по умолчанию и сжатие исходного изображения совпадают. Если вы открыли новое (в 4 раза больше) изображение и сохранили его, то, возможно, сжатие по умолчанию для метода сохранения будет получено из изображения (как это было при загрузке).

Опять же, я не знаю метод сохранения, поэтому я просто подбрасываю идеи (возможно, они помогут вам).

Другие вопросы по тегам