Cambiar el tamaño de la imagen proporcionalmente con las restricciones MaxHeight y MaxWidth

124

Utilizando System.Drawing.Image.

Si el ancho o la altura de una imagen exceden el máximo, debe cambiar su tamaño proporcionalmente. Después de cambiar el tamaño, debe asegurarse de que ni el ancho ni la altura excedan el límite.

El ancho y la altura se cambiarán de tamaño hasta que no se exceda al máximo y al mínimo automáticamente (el mayor tamaño posible) y también se mantendrá la relación.

Sarawut Positwinyu
fuente
@Sarawut Positwinyu - ¿Pero qué relación de aspecto quieres?
Bibhu
¿Qué desea que suceda si una imagen no puede redimensionarse a un máximo y mínimo de la altura y el ancho y la relación de aspecto se mantiene?
Conrad Frix
@Bibhu ¿Hay muchos tipos de relación de aspecto? No sé sobre eso. Solo quiero que la relación de imagen sea similar a la posible relación de imagen original.
Sarawut Positwinyu
@Sarawut Positwinyu: mira este enlace wiki para obtener más información sobre la relación de aspecto. en.wikipedia.org/wiki/Aspect_ratio_%28image%29
Bibhu
1
@Sarawut Positwinyu No usaste mal el término relación de aspecto. O si lo
hicieras,

Respuestas:

300

¿Me gusta esto?

public static void Test()
{
    using (var image = Image.FromFile(@"c:\logo.png"))
    using (var newImage = ScaleImage(image, 300, 400))
    {
        newImage.Save(@"c:\test.png", ImageFormat.Png);
    }
}

public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
    var ratioX = (double)maxWidth / image.Width;
    var ratioY = (double)maxHeight / image.Height;
    var ratio = Math.Min(ratioX, ratioY);

    var newWidth = (int)(image.Width * ratio);
    var newHeight = (int)(image.Height * ratio);

    var newImage = new Bitmap(newWidth, newHeight);

    using (var graphics = Graphics.FromImage(newImage))
        graphics.DrawImage(image, 0, 0, newWidth, newHeight);

    return newImage;
}
Alex Aza
fuente
77
@Alex buen uso de Math.Min (siempre me olvido de eso)
Conrad Frix
55
Te sugiero que uses una declaración de uso en el objeto Graphics al menos para ahorrar algunos recursos :)
Schalk
Solo estoy pensando en un caso, no estoy seguro de si es posible o no que después de multiplicar con la relación, el ancho o la altura aún sea mayor que el ancho máximo o la altura máxima.
Sarawut Positwinyu
44
También asegúrese de estar usando System.Drawing.Image si usa asp.net.
Induster
1
@Smith: no ejecute el método Guardar si no necesita guardar la imagen. Esto es exactamente lo que hace mi método ScaleImage: devuelve la imagen sin guardarla.
Alex Aza
5

Solución de trabajo:

Para cambiar el tamaño de la imagen con un tamaño inferior a 100 Kb

WriteableBitmap bitmap = new WriteableBitmap(140,140);
bitmap.SetSource(dlg.File.OpenRead());
image1.Source = bitmap;

Image img = new Image();
img.Source = bitmap;
WriteableBitmap i;

do
{
    ScaleTransform st = new ScaleTransform();
    st.ScaleX = 0.3;
    st.ScaleY = 0.3;
    i = new WriteableBitmap(img, st);
    img.Source = i;
} while (i.Pixels.Length / 1024 > 100);

Más referencia en http://net4attack.blogspot.com/

usuario806084
fuente
5

Solución mucho más larga, pero da cuenta de los siguientes escenarios:

  1. ¿La imagen es más pequeña que el cuadro delimitador?
  2. ¿La imagen y el cuadro delimitador son cuadrados?
  3. ¿La imagen es cuadrada y el cuadro delimitador no?
  4. ¿La imagen es más ancha y más alta que el cuadro delimitador?
  5. ¿La imagen es más ancha que el cuadro delimitador?
  6. ¿La imagen es más alta que el cuadro delimitador?

    private Image ResizePhoto(FileInfo sourceImage, int desiredWidth, int desiredHeight)
    {
        //throw error if bouning box is to small
        if (desiredWidth < 4 || desiredHeight < 4)
            throw new InvalidOperationException("Bounding Box of Resize Photo must be larger than 4X4 pixels.");            
        var original = Bitmap.FromFile(sourceImage.FullName);
    
        //store image widths in variable for easier use
        var oW = (decimal)original.Width;
        var oH = (decimal)original.Height;
        var dW = (decimal)desiredWidth;
        var dH = (decimal)desiredHeight;
    
        //check if image already fits
        if (oW < dW && oH < dH)
            return original; //image fits in bounding box, keep size (center with css) If we made it bigger it would stretch the image resulting in loss of quality.
    
        //check for double squares
        if (oW == oH && dW == dH)
        {
            //image and bounding box are square, no need to calculate aspects, just downsize it with the bounding box
            Bitmap square = new Bitmap(original, (int)dW, (int)dH);
            original.Dispose();
            return square;
        }
    
        //check original image is square
        if (oW == oH)
        {
            //image is square, bounding box isn't.  Get smallest side of bounding box and resize to a square of that center the image vertically and horizontally with Css there will be space on one side.
            int smallSide = (int)Math.Min(dW, dH);
            Bitmap square = new Bitmap(original, smallSide, smallSide);
            original.Dispose();
            return square;
        }
    
        //not dealing with squares, figure out resizing within aspect ratios            
        if (oW > dW && oH > dH) //image is wider and taller than bounding box
        {
            var r = Math.Min(dW, dH) / Math.Min(oW, oH); //two dimensions so figure out which bounding box dimension is the smallest and which original image dimension is the smallest, already know original image is larger than bounding box
            var nH = oH * r; //will downscale the original image by an aspect ratio to fit in the bounding box at the maximum size within aspect ratio.
            var nW = oW * r;
            var resized = new Bitmap(original, (int)nW, (int)nH);
            original.Dispose();
            return resized;
        }
        else
        {
            if (oW > dW) //image is wider than bounding box
            {
                var r = dW / oW; //one dimension (width) so calculate the aspect ratio between the bounding box width and original image width
                var nW = oW * r; //downscale image by r to fit in the bounding box...
                var nH = oH * r;
                var resized = new Bitmap(original, (int)nW, (int)nH);
                original.Dispose();
                return resized;
            }
            else
            {
                //original image is taller than bounding box
                var r = dH / oH;
                var nH = oH * r;
                var nW = oW * r;
                var resized = new Bitmap(original, (int)nW, (int)nH);
                original.Dispose();
                return resized;
            }
        }
    }
    
Ryan Mann
fuente
1
Creo que hay un par de errores tipográficos en los que está utilizando la relación para calcular la nueva altura de la imagen redimensionada. Corregir var nH = oH * r; Incorrecto: var nH = oW * r;
wloescher
Solucionado, simplemente nunca comentado.
Ryan Mann