Cambiar el tamaño de la imagen, mantener la relación de aspecto

6 minutos de lectura

Avatar de usuario de Fofole
Fofolé

Tengo una imagen que cambio de tamaño:

if((width != null) || (height != null))
{
    try{
        // Scale image on disk
        BufferedImage originalImage = ImageIO.read(file);
        int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB
                                                : originalImage.getType();

        BufferedImage resizedImageJpg = resizeImage(originalImage, type, 200, 200);
        ImageIO.write(resizedImageJpg, "jpg", file); 

       } catch(IOException e) {
           System.out.println(e.getMessage());
       }
}

Así es como cambio el tamaño de la imagen:

private static BufferedImage resizeImage(BufferedImage originalImage, int type,
                                         Integer imgWidth, Integer imgHeight)
{
    var resizedImage = new BufferedImage(imgWidth, imgHeight, type);
    Graphics2D g = resizedImage.createGraphics();
    g.drawImage(originalImage, 0, 0, imgWidth, imgHeight, null);
    g.dispose();

    return resizedImage;
}

Ahora el problema es que también necesito mantener la relación de aspecto. Es decir, necesito que la nueva imagen 200/200 contenga la nueva imagen escalada. Algo como esto:
ingrese la descripción de la imagen aquí

Intenté algunas cosas pero no funcionaron como esperaba. Cualquier ayuda es apreciada.

Avatar de usuario de Ozzy
Ozzy

Aquí vamos:

Dimension imgSize = new Dimension(500, 100);
Dimension boundary = new Dimension(200, 200);

Función para devolver el nuevo tamaño dependiendo del límite:

public static Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {

    int original_width = imgSize.width;
    int original_height = imgSize.height;
    int bound_width = boundary.width;
    int bound_height = boundary.height;
    int new_width = original_width;
    int new_height = original_height;

    // first check if we need to scale width
    if (original_width > bound_width) {
        //scale width to fit
        new_width = bound_width;
        //scale height to maintain aspect ratio
        new_height = (new_width * original_height) / original_width;
    }

    // then check if we need to scale even with the new height
    if (new_height > bound_height) {
        //scale height to fit instead
        new_height = bound_height;
        //scale width to maintain aspect ratio
        new_width = (new_height * original_width) / original_height;
    }

    return new Dimension(new_width, new_height);
}

En caso de que alguien también necesite el código de cambio de tamaño de la imagen, aquí hay una solución decente.

Si no está seguro acerca de la solución anterior, hay diferentes formas de lograr el mismo resultado.

  • Usé la misma lógica para cambiar el tamaño de los mapas de bits en Android 🙂

    –James andresakis

    27 de junio de 2012 a las 0:15

  • La respuesta de @Ozzy es correcta, excepto cuando no se requiere escala (o más específicamente cuando el ancho original es más pequeño que el límite). Inicializando new_width y new_height a a original_width y original_height corrige el problema.

    – Ryan Morlock

    29 de enero de 2013 a las 22:57


  • Sé que esto es para Java, pero lo convertí a js en caso de que alguien más necesite la función getDimensions(originalWidth,originalHeight,newWidth,newHeight){ var dimension = {}; dimensiones.ancho = anchooriginal; dimensiones.altura = alturaoriginal; if(originalWidth > newWidth){ dimensiones.width = newWidth; dimensiones.alto = (dimensiones.ancho * altooriginal)/anchooriginal; } if(dimensiones.altura > nuevaAltura){ dimensiones.altura = nuevaAltura; dimensiones.ancho = (dimensiones.altura * anchooriginal) / alturaoriginal; } devolver dimensiones; }

    – Rocío de la mañana

    17 de diciembre de 2015 a las 4:39

  • Esto no funciona si la dimensión de destino es mayor. por lo tanto, ‘escala’ debe cambiarse a ‘reducir’ para este método.

    – yilmaz

    20 de junio de 2018 a las 6:11

  • Esta solución funcionó para mí, hasta que necesité cambiar el tamaño de una imagen a un límite más grande que la imagen misma. Tal vez este tiene más sentido.

    – Sistema Cardinal

    23 de enero de 2019 a las 20:55

Avatar de usuario de Matthias Braun
Matías Braun

Traducido de aquí:

Dimension getScaledDimension(Dimension imageSize, Dimension boundary) {

    double widthRatio = boundary.getWidth() / imageSize.getWidth();
    double heightRatio = boundary.getHeight() / imageSize.getHeight();
    double ratio = Math.min(widthRatio, heightRatio);

    return new Dimension((int) (imageSize.width  * ratio),
                         (int) (imageSize.height * ratio));
}

También puedes usar imgscalr para cambiar el tamaño de las imágenes manteniendo la relación de aspecto:

BufferedImage resizeMe = ImageIO.read(new File("orig.jpg"));
Dimension newMaxSize = new Dimension(255, 255);
BufferedImage resizedImg = Scalr.resize(resizeMe, Method.QUALITY,
                                        newMaxSize.width, newMaxSize.height);

  • Esta debería ser la respuesta aceptada, ¡el código es más pequeño y también funciona para imágenes más pequeñas! ¡Gracias!

    – Simón Ludwig

    24 de marzo de 2019 a las 10:45

  • Dejé Scalr.java en mi proyecto y este ejemplo funcionó perfectamente, sin ningún drama.

    – Sean Anderson

    18 de abril a las 1:57

avatar de usuario de daveb
David

Querrás echarle un vistazo Imagen.getScaledInstance()y más en esta respuesta: Cómo mejorar el rendimiento del método g.drawImage() para cambiar el tamaño de las imágenes

Cargar imagen:

BufferedImage bufferedImage = ImageIO.read(file);   

Cambiar el tamaño:

private BufferedImage resizeAndCrop(BufferedImage bufferedImage, Integer width, Integer height) {

    Mode mode = (double) width / (double) height >= (double) bufferedImage.getWidth() / (double) bufferedImage.getHeight() ? Scalr.Mode.FIT_TO_WIDTH
            : Scalr.Mode.FIT_TO_HEIGHT;

    bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.ULTRA_QUALITY, mode, width, height);

    int x = 0;
    int y = 0;

    if (mode == Scalr.Mode.FIT_TO_WIDTH) {
        y = (bufferedImage.getHeight() - height) / 2;
    } else if (mode == Scalr.Mode.FIT_TO_HEIGHT) {
        x = (bufferedImage.getWidth() - width) / 2;
    }

    bufferedImage = Scalr.crop(bufferedImage, x, y, width, height);

    return bufferedImage;
}

Usando la biblioteca Scalr:

<dependency>
    <groupId>org.imgscalr</groupId>
    <artifactId>imgscalr-lib</artifactId>
    <version>4.2</version>
</dependency>

prueba esto

float rateX =  (float)jpDisplayImagen.getWidth()/(float)img.getWidth();
float rateY = (float)jpDisplayImagen.getHeight()/(float)img.getHeight();
if (rateX>rateY){
    int W=(int)(img.getWidth()*rateY);
    int H=(int)(img.getHeight()*rateY);
    jpDisplayImagen.getGraphics().drawImage(img, 0, 0,W,H, null);
}
else{
    int W=(int)(img.getWidth()*rateX);
    int H=(int)(img.getHeight()*rateX);
    jpDisplayImagen.getGraphics().drawImage(img, 0, 0,W,H, null);
}

Avatar de usuario de Hansjörg Hofer
Hansjorg Hofer

public class ImageTransformation {

public static final String PNG = "png";

public static byte[] resize(FileItem fileItem, int width, int height) {

    try {
        ResampleOp resampleOp = new ResampleOp(width, height);
        BufferedImage scaledImage = resampleOp.filter(ImageIO.read(fileItem.getInputStream()), null);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(scaledImage, PNG, baos);

        return baos.toByteArray();
    } catch (Exception ex) {
        throw new MapsException("An error occured during image resizing.", ex);
    }

}

public static byte[] resizeAdjustMax(FileItem fileItem, int maxWidth, int maxHeight) {

    try {
        BufferedInputStream bis = new BufferedInputStream(fileItem.getInputStream());  
        BufferedImage bufimg = ImageIO.read(bis);   

        //check size of image 
        int img_width = bufimg.getWidth();
        int img_height = bufimg.getHeight();
        if(img_width > maxWidth || img_height > maxHeight) {
            float factx = (float) img_width / maxWidth;
            float facty = (float) img_height / maxHeight;
            float fact = (factx>facty) ? factx : facty;
            img_width = (int) ((int) img_width / fact);
            img_height = (int) ((int) img_height / fact);
        }

        return resize(fileItem,img_width, img_height);

    } catch (Exception ex) {
        throw new MapsException("An error occured during image resizing.", ex);
    }

}

}

Avatar de usuario de Khadzko Aliaksander
Khadzko Aliaksander

Esta es mi solución:

/*
Change dimension of Image
*/
public static Image resizeImage(Image image, int scaledWidth, int scaledHeight, boolean preserveRatio) {  

    if (preserveRatio) { 
        double imageHeight = image.getHeight();
        double imageWidth = image.getWidth();

        if (imageHeight/scaledHeight > imageWidth/scaledWidth) {
            scaledWidth = (int) (scaledHeight * imageWidth / imageHeight);
        } else {
            scaledHeight = (int) (scaledWidth * imageHeight / imageWidth);
        }        
    }                   
    BufferedImage inputBufImage = SwingFXUtils.fromFXImage(image, null);     
    // creates output image
    BufferedImage outputBufImage = new BufferedImage(scaledWidth, scaledHeight, inputBufImage.getType());       
    // scales the input image to the output image
    Graphics2D g2d = outputBufImage.createGraphics();
    g2d.drawImage(inputBufImage, 0, 0, scaledWidth, scaledHeight, null);
    g2d.dispose();      
    return SwingFXUtils.toFXImage(outputBufImage, null);
}

¿Ha sido útil esta solución?