2009-04-29 11 views
6

Estoy cambiando el tamaño de algunas imágenes a la resolución de pantalla del usuario; si la relación de aspecto es incorrecta, la imagen debe cortarse. Mi código es el siguiente:Cambio de tamaño de la imagen, a veces de muy mala calidad?

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

En la mayoría de los casos, esto funciona bien. Pero para algunas imágenes, el resultado es extremadamente de mala calidad. Parece que se habría redimensionado a algo muy pequeño (tamaño de miniatura) y ampliado de nuevo ... Pero la resolución de la imagen es correcta. ¿Que puedo hacer?

Ejemplo orig imagen: alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Ejemplo imagen redimensionada: alt text http://img523.imageshack.us/img523/2531/naturaerowoods.png

Nota: Tengo una aplicación WPF pero yo uso la función de Windows Forms para cambiar el tamaño, porque es más fácil y porque ya necesito una referencia a System.Windows.Forms para un icono de bandeja.

+0

Gracias por preguntar esto! Este problema todavía está presente hoy en día ... – Andrew

Respuesta

7

Cambiar las dos últimas líneas de su método a esto:

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

También debe configurar los ajustes de calidad, por defecto son bajos. Ver http://nathanaeljones.com/163/20-image-resizing-pitfalls/ –

+0

Saludos a Bfree y Computer Linguist, ahora resuelve mis problemas ... –

+0

Si está haciendo esto en el servidor, como en una aplicación ASP.NET, lo mejor es usar [un contenedor] (http://imageresizing.net) que corrige el sistema. Dibujar memleaks. –

7

No puedo echar un vistazo a la fuente .NET en este momento, pero lo más probable es que el problema esté en el método Image.GetThumbnailImage. Incluso MSDN dice que "funciona bien cuando la imagen en miniatura solicitada tiene un tamaño de aproximadamente 120 x 120 píxeles, pero si solicita una imagen en miniatura grande (por ejemplo, 300 x 300) de una imagen que tiene una miniatura incrustada, podría ser una pérdida notable de calidad en la imagen en miniatura ". Para el cambio de tamaño real (es decir, no miniaturas), debe usar el método Graphics.DrawImage. Es posible que también necesite jugar con el Graphics.InterpolationMode para obtener una mejor calidad si es necesario.

2

Como se indica en MSDN, GetThumbnailImage() no está diseñado para realizar escalas de imagen arbitrarias. Cualquier cosa de más de 120x120 debe escalarse manualmente. Prueba este lugar:

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

Editar

Como punto de aclaración, esta sobrecarga del constructor llama BitmapGraphics.DrawImage, aunque usted no tiene ningún control sobre la interpolación.

0

Por ejemplo, la imagen original es JPG y la imagen redimensionada es PNG. ¿Está convirtiendo entre formatos a propósito? El cambio entre diferentes esquemas de compresión de lossey puede causar pérdida de calidad.

+0

Quiero usar la imagen como fondo de pantalla, y para ello tengo que convertirla a BMP porque Win XP no acepta ningún otro formato para fondos de pantalla. – eWolf

3

Si no está creando una imagen en miniatura, utilizando un método llamado GetThumbnailImage probablemente no es una buena idea ...

Para otras opciones, echar un vistazo a this CodeProject article. En particular, crea una nueva imagen, crea un Graphics para él y establece el modo de interpolación en HighQualityBicubic y dibuja la imagen original en los gráficos. Vale la pena intentarlo, al menos.

+3

Downvoters: por favor, den razones ... –

+0

están celosos de su conocimiento épico jon ignórenlos –

-1

Esto va a variar ampliamente en base a los siguientes factores:

  1. ¿En qué medida coincide con la resolución de destino una escala "natural" de una resolución original
  2. La profundidad origen de la imagen de color
  3. La imagen tipo (s) - algunos son más pérdidas que otros
0

¿Está aumentando o disminuyendo el tamaño de la imagen cuando la cambia de tamaño? Si está creando una imagen más grande de una más pequeña, este tipo de degradación es de esperar.

+0

Sus dos imágenes parecen indicar que el original es más grande. –

0

Las imágenes definitivamente se degradarán si las agranda.

0

Algunas cámaras colocan una miniatura redimensionada en el archivo presumiblemente para previsualizar en el dispositivo.

El método GetThumbnail en realidad obtiene esta imagen en miniatura que está incrustada en el archivo de imagen en lugar de obtener el método de mayor resolución.

La solución fácil es engañar a .Net para descartar esa información en miniatura antes de hacer su cambio de tamaño u otra operación. al igual que ....

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

Si está intentando cambiar el tamaño de las proporciones de restringir i escribió un método de extensión en System.Drawing.Image que le puede resultar útil.

/// <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

en lugar de este código:

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

uso éste:

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); 
Cuestiones relacionadas