Иногда при уменьшении растрового изображения создается файл большего размера. Зачем? - PullRequest
5 голосов
/ 24 марта 2010

Я пытаюсь написать метод для уменьшения размера любого изображения на 50% каждый раз, когда вызывается, но я нашел проблему. Иногда я получаю больший размер файла, тогда как изображение на самом деле составляет лишь половину того, что было. Я забочусь о DPI и PixelFormat. Что еще мне не хватает?

Спасибо за ваше время.

public Bitmap ResizeBitmap(Bitmap origBitmap, int nWidth, int nHeight)
{
    Bitmap newBitmap = new Bitmap(nWidth, nHeight, origBitmap.PixelFormat);

    newBitmap.SetResolution(
       origBitmap.HorizontalResolution, 
       origBitmap.VerticalResolution);

    using (Graphics g = Graphics.FromImage((Image)newBitmap))
    {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;

        g.DrawImage(origBitmap, 0, 0, nWidth, nHeight);
    }

    return newBitmap;
}

http://imgur.com/lY9BN.png

http://imgur.com/KSka0.png

Изменить: Вот отсутствующий код:

int width = (int)(bitmap.Width * 0.5f);
int height = (int)(bitmap.Height * 0.5f);
Bitmap resizedBitmap = ResizeBitmap(bitmap, width, height);
resizedBitmap.Save(newFilename);

Редактировать 2: Исходя из ваших комментариев, это решение, которое я нашел:

private void saveAsJPEG(string savingPath, Bitmap bitmap, long quality)
{
    EncoderParameter parameter = new EncoderParameter(Encoder.Compression, quality);
    ImageCodecInfo encoder = getEncoder(ImageFormat.Jpeg);
    if (encoder != null)
    {
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = parameter;
        bitmap.Save(savingPath, encoder, encoderParams);
    }
}

private ImageCodecInfo getEncoder(ImageFormat format)
{

    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
        if (codec.FormatID == format.Guid)
            return codec;

    return null;
}

Ответы [ 7 ]

6 голосов
/ 24 марта 2010

Скорее всего, вы сохраняете изображение в формате JPEG с низким коэффициентом сжатия (высокое качество).

4 голосов
/ 24 марта 2010

Повторное сжатие изображения JPEG - это то, чего вам следует избегать. Но настоящая проблема здесь - это InterpolationMode, высококачественный, который производит много тонко затененных пикселей, что затрудняет сжатие изображения. Следует избегать использования InterpolationMode.NearestNeighbor, но за счет получения изображения более низкого качества.

2 голосов
/ 24 марта 2010

Проблема не в коде, который вы разместили, а в том, где вы сохраняете JPEG. Какую степень сжатия вы используете?

1 голос
/ 24 марта 2010

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

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

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

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

1 голос
/ 24 марта 2010

Попробуйте этот код, чтобы сохранить имеющийся у вас JPEG. Это позволит вам установить качество сжатия. Поэкспериментируйте и посмотрите, поможет ли вам проблема размера:

private void saveJpeg(string path, Bitmap img, long quality)
{
    EncoderParameter parameter = new EncoderParameter(Encoder.Quality, quality);
    ImageCodecInfo encoder = this.getEncoderInfo("image/jpeg");
    if (encoder != null)
    {
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = parameter;
        img.Save(path, encoder, encoderParams);
    }
}

private ImageCodecInfo getEncoderInfo(string mimeType)
{
    ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders();
    for (int i = 0; i < imageEncoders.Length; i++)
    {
        if (imageEncoders[i].MimeType == mimeType)
        {
            return imageEncoders[i];
        }
    }
    return null;
}
1 голос
/ 24 марта 2010

Кажется, что ваши растровые изображения на самом деле сохраняются в формате jpegs, и, зная, как c # любит делать что-то для вас, он, вероятно, экономит с довольно низким уровнем сжатия по умолчанию. Оригинальный JPEG должен иметь приличный уровень сжатия на нем. Даже если бы это был тот же уровень сжатия, то, как работает формат jpeg, может означать, что вы обнаружили случайное изображение, которое раздувается при более низком разрешении.

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

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

1 голос
/ 24 марта 2010

Я бы сказал, что вы сохраняете его как высококачественный JPEG (меньше сжатия == больше места)

...