Изменение размера изображения - иногда очень низкого качества?

StackOverflow https://stackoverflow.com/questions/803987

Вопрос

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

protected void ConvertToBitmap(string filename)
    {
        var origImg = System.Drawing.Image.FromFile(filename);
        var widthDivisor = (double)origImg.Width / (double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
        var heightDivisor = (double)origImg.Height / (double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
        int newWidth, newHeight;

        if (widthDivisor < heightDivisor)
        {
            newWidth = (int)((double)origImg.Width / widthDivisor);
            newHeight = (int)((double)origImg.Height / widthDivisor);
        }
        else
        {
            newWidth = (int)((double)origImg.Width / heightDivisor);
            newHeight = (int)((double)origImg.Height / heightDivisor);
        }

         var newImg = origImg.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
        newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
    }

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

Пример исходного изображения:альтернативный текст http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Пример изображения с измененным размером:alt text

Примечание:У меня есть приложение WPF, но я использую функцию WinForms для изменения размера, потому что это проще и потому что мне уже нужна ссылка на System.Windows.Формы для значка в трее.

Это было полезно?

Решение

Измените последние две строки вашего метода на это:

var newImg = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage(newImg);
g.DrawImage(origImg, new Rectangle(0,0,newWidth,newHeight));
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
g.Dispose();

Другие советы

На данный момент я не могу заглянуть в .ЧИСТЫЙ исходный код, но, скорее всего, проблема в Image.GetThumbnailImage способ.Даже MSDN говорит, что "это хорошо работает, когда запрошенное уменьшенное изображение имеет размер около 120 x 120 пикселей, но если вы запрашиваете большое уменьшенное изображение (например, 300 x 300) из изображения, которое имеет встроенную миниатюру, в уменьшенном изображении может произойти заметная потеря качества".Для истинного изменения размера (т. е.не создание миниатюр), вы должны использовать Graphics.DrawImage способ.Вам также может понадобиться поиграть с Graphics.InterpolationMode чтобы при необходимости получить лучшее качество.

Если вы не создаете миниатюру, используя метод, называемый GetThumbnailImage вероятно, это не очень хорошая идея...

Для получения других вариантов, взгляните на эта статья CodeProject.В частности, это создает новый образ, создает Graphics для этого и устанавливает режим интерполяции в HighQualityBicubic и наносит исходное изображение на графику.По крайней мере, стоит попробовать.

Как указано на MSDN, GetThumbnailImage() не предназначен для произвольного масштабирования изображения.Все, что превышает 120x120, должно быть масштабировано вручную.Попробуйте это вместо этого:

using(var newImg = new Bitmap(origImg, newWidth, newHeight))
{
    newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
}

Редактировать

В качестве пояснения, эта перегрузка Bitmap вызовы конструктора Graphics.DrawImage, хотя у вас нет никакого контроля над интерполяцией.

вместо этого кода:

newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);

используй этот :

System.Drawing.Imaging.ImageCodecInfo[] info = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
System.Drawing.Imaging.EncoderParameters param = new System.Drawing.Imaging.EncoderParameters(1);
param.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
newImg.Save(dest_img, info[1], param);

Например, исходное изображение - JPG, а измененное изображение - PNG.Вы специально конвертируете между форматами?Переключение между различными схемами сжатия с потерями может привести к потере качества.

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

Изображения определенно ухудшатся, если вы их увеличите.

Некоторые камеры помещают уменьшенный эскиз в сам файл, предположительно, для предварительного просмотра на самом устройстве.

Метод GetThumbnail фактически получает это уменьшенное изображение, которое встроено в файл изображения, вместо получения метода с более высоким разрешением.

Простое решение - обмануть .Net, заставив его удалить эту информацию о миниатюре перед выполнением изменения размера или другой операции.вот так....

img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX); 
//removes thumbnails from digital camera shots
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);

Если вы пытаетесь изменить размер, ограничивающий пропорции, я написал метод расширения в системе.Рисование.Изображение, которое может вам пригодиться.

/// <summary>
/// 
/// </summary>
/// <param name="img"></param>
/// <param name="size">Size of the constraining proportion</param>
/// <param name="constrainOnWidth"></param>
/// <returns></returns>
public static System.Drawing.Image ResizeConstrainProportions(this System.Drawing.Image img,
    int size, bool constrainOnWidth, bool dontResizeIfSmaller)
{
    if (dontResizeIfSmaller && (img.Width < size))
        return img;
    img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX); 
    img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
    float ratio = 0;
    ratio = (float)img.Width / (float)img.Height;

    int height, width = 0;
    if (constrainOnWidth)
    {
        height = (int)(size / ratio);
        width = size;
    }
    else
    {
        width = (int)(size * ratio);
        height = size;
    }
    return img.GetThumbnailImage(width, height, null, (new System.IntPtr(0)));
}

Это будет сильно варьироваться в зависимости от следующих факторов:

  1. Насколько близко целевое разрешение соответствует "естественному" масштабу исходного разрешения
  2. Глубина цвета исходного изображения
  3. Тип (ы) изображения - некоторые из них с большими потерями, чем другие
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top