Подтвердить что ты не робот

Изменение размера изображения в Java, сохранение пропорций

У меня есть изображение, которое я изменяю размер:

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 resizeImageJpg = resizeImage(originalImage, type, 200, 200);
        ImageIO.write(resizeImageJpg, "jpg", file); 

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

Вот как я изменяю размер изображения:

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

    return resizedImage;
}

Теперь проблема в том, что мне также нужно поддерживать соотношение сторон. То есть мне нужно, чтобы новое изображение 200/200 содержало новое масштабированное изображение. Примерно так: enter image description here

Я попробовал некоторые вещи, но они не сработали, как ожидалось. Любая помощь приветствуется. Большое спасибо.

4b9b3361

Ответ 1

Здесь мы идем:

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

Функция для возврата нового размера в зависимости от границы

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

В случае, если кому-то также нужен код изменения размера изображения, вот достойное решение.

Если вы не уверены в вышеприведенном решении есть разные способы достичь того же результата.

Ответ 2

В переводе с здесь:

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

Вы также можете использовать imgscalr для изменения размера изображений, сохраняя соотношение сторон:

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

Ответ 4

попробуйте это

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

Ответ 5

Загрузить изображение:

BufferedImage bufferedImage = ImageIO.read(file);   

Измените его размер:

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

Использование библиотеки Scalr:

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

Ответ 6

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

}

}

Ответ 7

Это мое решение:

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

Ответ 8

Все остальные ответы показывают, как вычислить новую высоту изображения в зависимости от ширины нового изображения или наоборот и как изменить размер изображения с помощью Java Image API. Для тех людей, которые ищут прямое решение, я рекомендую любую инфраструктуру обработки изображений java, которая может сделать это в одной строке.

Ниже приведен пример Marvin Framework:

// 300 is the new width. The height is calculated to maintain aspect.
scale(image.clone(), image, 300);

Необходимый импорт:

import static marvin.MarvinPluginCollection.*

Ответ 9

Я нашел выбранный ответ, чтобы иметь проблемы с масштабированием, и поэтому я сделал (еще) еще одну версию (которую я тестировал):

public static Point scaleFit(Point src, Point bounds) {
  int newWidth = src.x;
  int newHeight = src.y;
  double boundsAspectRatio = bounds.y / (double) bounds.x;
  double srcAspectRatio = src.y / (double) src.x;

  // first check if we need to scale width
  if (boundsAspectRatio < srcAspectRatio) {
    // scale width to fit
    newWidth = bounds.x;
    //scale height to maintain aspect ratio
    newHeight = (newWidth * src.y) / src.x;
  } else {
    //scale height to fit instead
    newHeight = bounds.y;
    //scale width to maintain aspect ratio
    newWidth = (newHeight * src.x) / src.y;
  }

  return new Point(newWidth, newHeight);
}

Написано в Android-терминологии: -)

как для тестов:

@Test public void scaleFit() throws Exception {
  final Point displaySize = new Point(1080, 1920);
  assertEquals(displaySize, Util.scaleFit(displaySize, displaySize));
  assertEquals(displaySize, Util.scaleFit(new Point(displaySize.x / 2, displaySize.y / 2), displaySize));
  assertEquals(displaySize, Util.scaleFit(new Point(displaySize.x * 2, displaySize.y * 2), displaySize));
  assertEquals(new Point(displaySize.x, displaySize.y * 2), Util.scaleFit(new Point(displaySize.x / 2, displaySize.y), displaySize));
  assertEquals(new Point(displaySize.x * 2, displaySize.y), Util.scaleFit(new Point(displaySize.x, displaySize.y / 2), displaySize));
  assertEquals(new Point(displaySize.x, displaySize.y * 3 / 2), Util.scaleFit(new Point(displaySize.x / 3, displaySize.y / 2), displaySize));
}

Ответ 10

Вот небольшой фрагмент кода, который я написал, он изменяет размер изображения, чтобы соответствовать контейнеру, сохраняя при этом исходное соотношение сторон изображения. Он принимает в качестве параметров ширину контейнера, высоту и изображение. Вы можете изменить его в соответствии с вашими потребностями. Это просто и отлично работает для моих приложений.

private Image scaleimage(int wid, int hei, BufferedImage img){
    Image im = img;
    double scale;
    double imw = img.getWidth();
    double imh = img.getHeight();
    if (wid > imw && hei > imh){
        im = img;
    }else if(wid/imw < hei/imh){
        scale = wid/imw;
        im = img.getScaledInstance((int) (scale*imw), (int) (scale*imh), Image.SCALE_SMOOTH);
    }else if (wid/imw > hei/imh){
        scale = hei/imh;
        im = img.getScaledInstance((int) (scale*imw), (int) (scale*imh), Image.SCALE_SMOOTH);
    }else if (wid/imw == hei/imh){
        scale = wid/imw;
        im = img.getScaledInstance((int) (scale*imw), (int) (scale*imh), Image.SCALE_SMOOTH);
    } 
    return im;
}

Ответ 11

Просто добавьте еще один блок в код Ozzy, чтобы все выглядело так:

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;
    }
    // upscale if original is smaller
    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;
    }

    return new Dimension(new_width, new_height);
}