2009-04-29 5 views
6

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

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

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

Example Orig изображение: alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Пример изменения размера изображения: alt text http://img523.imageshack.us/img523/2531/naturaerowoods.png

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

+0

Спасибо за вопрос это! Эта проблема все еще присутствует в настоящее время ... – Andrew

ответ

7

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

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(); 
+2

Вам также нужно установить параметры качества - они по умолчанию низкие. См. Http://nathanaeljones.com/163/20-image-resizing-pitfalls/ –

+0

Приветствия как Bfree, так и Computer Linguist, теперь решили мои проблемы ... –

+0

Если вы делаете это на сервере, например в приложении ASP.NET лучше всего использовать [обертку] (http://imageresizing.net), которая исправляет memleaks System.Drawing. –

7

В настоящий момент я не могу заглянуть в источник .NET, но, скорее всего, проблема в методе Image.GetThumbnailImage. Даже MSDN говорит, что «он работает хорошо, когда требуемое уменьшенное изображение имеет размер около 120 x 120 пикселей, но вы запрашиваете большое изображение миниатюр (например, 300 x 300) из изображения с встроенной миниатюрой, быть заметной потерей качества в миниатюрном изображении ». Для истинного изменения размера (то есть не для миниатюр), вы должны использовать метод Graphics.DrawImage. Вам также может потребоваться сыграть с Graphics.InterpolationMode, чтобы получить лучшее качество, если необходимо.

2

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

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

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

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

0

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

+0

Я хочу использовать картинку в качестве обоев, и для этого мне нужно преобразовать ее в BMP, потому что Win XP не принимает никаких других форматов для обоев. – eWolf

3

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

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

+3

Downvoters: пожалуйста, укажите причины ... –

+0

они просто ревнуют ваших эпических знаний jon игнорировать их –

-1

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

  1. Насколько близко разрешение назначения совпадает с «естественной» масштаб исходного разрешения
  2. Глубина источника цветного изображения
  3. Изображение тип (ы) - некоторые из них более потерь, чем другие
0

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

+0

Его два изображения показывают, что оригинал больше. –

0

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

0

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

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

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

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

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

/// <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))); 
} 
2

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

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); 
Смежные вопросы