Pregunta

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 se ve así:

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 tiene una calidad extremadamente deficiente. Parece que se habría redimensionado a algo muy pequeño (tamaño de miniatura) y ampliado nuevamente. Pero la resolución de la imagen es correcta. ¿Qué puedo hacer?

Imagen original de ejemplo: texto alternativo http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Ejemplo de imagen redimensionada: texto alternativo ??

Nota: Tengo una aplicación WPF pero utilizo la función WinForms 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.

¿Fue útil?

Solución

Cambie 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();

Otros consejos

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 solicita una imagen en miniatura grande (por ejemplo, 300 x 300) de una imagen que tiene una miniatura incrustada, allí podría ser una notable pérdida de calidad en la imagen en miniatura ''. Para un cambio de tamaño verdadero (es decir, no miniaturas), debe usar el método Graphics.DrawImage . También es posible que deba jugar con Graphics.InterpolationMode para obtener una mejor calidad si es necesario.

Si no está creando una miniatura, usar un método llamado GetThumbnailImage probablemente no sea una buena idea ...

Para ver otras opciones, consulte este artículo de CodeProject . En particular, crea una nueva imagen, crea un Graphics y establece el modo de interpolación en HighQualityBicubic y dibuja la imagen original en los gráficos. Vale la pena intentarlo, al menos.

Como se indica en MSDN , GetThumbnailImage () no está diseñado para escalar imágenes arbitrariamente. Todo lo que supere los 120x120 debe escalarse manualmente. Intente esto en su 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 Bitmap llama a Graphics.DrawImage , aunque no tiene ningún control sobre la interpolación.

en lugar de este código:

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

usa este:

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

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

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

Las imágenes definitivamente se degradarán si las amplía.

Algunas cámaras colocan una miniatura redimensionada en el archivo presumiblemente para propósitos de vista previa en el propio dispositivo.

El método GetThumbnail en realidad obtiene esta imagen en miniatura que está incrustada dentro del 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 que elimine esa información en miniatura antes de realizar el cambio de tamaño u otra operación. así ...

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 restrictivas, escribí un método de extensión en System.Drawing.Image que podría encontrar ú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)));
}

Esto variará ampliamente en función de los siguientes factores:

  1. Qué tan cerca la resolución de destino coincide con un "natural" escala de la resolución original
  2. La profundidad de color de la imagen de origen
  3. Los tipos de imagen: algunos tienen más pérdidas que otros
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top