Изменение размера изображения с сохранением соотношения сторон в Java - PullRequest
17 голосов
/ 13 июня 2009

Я пытаюсь изменить размер bufferdImage в памяти в Java, но сохранить соотношение сторон изображения У меня есть что-то вроде этого, но это не хорошо

int w = picture.getWidth();
int h = picture.getWidth();
int neww=w;
int newh=h;
int wfactor = w;
int hfactor = h;
if(w > DEFULT_PICTURE_WIDTH || h > DEFULT_PICTURE_HIGHT)
{
    while(neww > DEFULT_PICTURE_WIDTH)
    {
        neww = wfactor /2;
        newh = hfactor /2;
        wfactor = neww;
        hfactor = newh;
    }
}

picture = Utils.resizePicture(picture,neww,newh);

Ответы [ 6 ]

69 голосов
/ 05 июля 2011

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

Причина этого в том, что когда Java2D не рекомендовал использовать getScaledInstance и AreaAveragingScaleFilter, они не заменили его на что-либо столь простое для использования в API, вместо этого мы были оставлены на наших собственных устройствах с использованием Java2D API напрямую. К счастью, Крис Кэмпбелл (из команды J2D) выполнил рекомендацию об использовании метода инкрементального масштабирования, который дает результаты, аналогичные AreaAveragingScaleFilter, и работает быстрее; к сожалению, код имеет приличный размер и не отвечает вашему первоначальному вопросу соблюдения пропорций.

Около 6 месяцев назад я снова и снова видел все эти вопросы на SO о «масштабировании изображений в Java» и в конечном итоге собрал все советы, сделал все исследования и исследования, которые мог, и собрал все в одну «лучшую практику». " библиотека масштабирования изображений .

API очень прост, так как это всего лишь 1 класс и несколько статических методов. Основное использование выглядит так:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);

Это самый простой вызов , когда библиотека сделает правильное предположение о качестве, соблюдает пропорции вашего изображения и поместит результат в ограничивающий прямоугольник 320x320. ПРИМЕЧАНИЕ: ограничивающий прямоугольник - это только максимальное используемое значение W / H, так как пропорции вашего изображения соблюдаются, результирующее изображение все равно будет учитывать это, например, 320x200.

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

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 
                                       150, 100, Scalr.OP_ANTIALIAS);

Это всего лишь примеры, API является широким и охватывает все: от очень простых вариантов использования до очень специализированных. Вы даже можете передать свои собственные BufferedImageOps, которые будут применены к изображению (и библиотека автоматически исправит 6-летнюю ошибку BufferedImageOp JDK для вас!)

Существует гораздо больше возможностей для успешного масштабирования изображений в Java, чем библиотека делает для вас, например, всегда поддерживая изображение в одном из лучших поддерживаемых типов изображений RGB или ARGB при работе с ним. Под прикрытием конвейер обработки изображений Java2D возвращается к подчиненному программному конвейеру, если тип изображения, используемый для любых операций с изображениями, плохо поддерживается.

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

Надеюсь, это поможет.

8 голосов
/ 11 декабря 2009

Если ширина, высота источника и цели известны, используйте следующую функцию для определения масштаба изображения.

private double determineImageScale(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) {

double scalex = (double) targetWidth / sourceWidth;
double scaley = (double) targetHeight / sourceHeight;
return Math.min(scalex, scaley);

}

Затем используйте эту шкалу для увеличения / уменьшения изображения, используя следующий код

Image scaledImage = sourceBufferedImage.getScaledInstance((int) (width * scale), (int) (height * scale), Image.SCALE_SMOOTH);
8 голосов
/ 13 июня 2009

Для начала - взгляните на строку 2. Разве это не должно быть getHeight()?

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

(width / height) = (new_width / new_height)

Если вам известен один из «новых» размеров, другой можно найти с помощью умножения

new_height * (width / height) = new_width

Вы также можете использовать ленивый метод, предоставленный BufferedImage's superclass Image, getScaledInstance() - использование -1 для ширины или высоты будет поддерживать соотношение сторон

напр .:
scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);

6 голосов
/ 10 декабря 2010

Вы можете взглянуть на perils-of-getscaledinstance.html , который объясняет, почему getScaledInstance(), использованный в некоторых ответах, следует избегать.

В статье также приведен альтернативный код.

3 голосов
/ 28 ноября 2013

Я использую эти два метода для масштабирования изображений, где max - это большее измерение целевого изображения. Для изображения 100x100 это будет 100, для изображения 200x300 это будет 300.

    public static BufferedImage scale(InputStream is, int max) {
    Image image = null;
    try {
        image = ImageIO.read(is);
    } catch (IOException e) {
        e.printStackTrace();
    }
    int width = image.getWidth(null);
    int height = image.getHeight(null);
    double dWidth = 0;
    double dHeight = 0;
    if (width == height) {
        dWidth = max;
        dHeight = max;
    } 
    else if (width > height) {
        dWidth = max;
        dHeight = ((double) height / (double) width) * max;
    }
    else {
        dHeight = max;
        dWidth = ((double) width / (double) height) * max;
    }
    image = image.getScaledInstance((int) dWidth, (int) dHeight, Image.SCALE_SMOOTH);
    BufferedImage bImage = toBufferedImage(image);
    return bImage;

}

public static BufferedImage toBufferedImage(Image img)
{
    if (img instanceof BufferedImage)
    {
        return (BufferedImage) img;
    }

    BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);

    Graphics2D bGr = bimage.createGraphics();
    bGr.drawImage(img, 0, 0, null);
    bGr.dispose();

    return bimage;
}
0 голосов
/ 14 июня 2009

Если вы хотите изменить размер изображения от w0 x h0 до w1 x h1, сохранив соотношение сторон, вычислите вертикальный и горизонтальный масштаб и выберите меньший.

double scalex = 1;
double scaley = 1;
if (scalingMode == ScalingMode.WINDOW_SIZE) {
  scalex = (double)getWidth() / frontbuffer.getWidth();
  scaley = (double)getHeight() / frontbuffer.getHeight();
} else
if (scalingMode == ScalingMode.KEEP_ASPECT) {
  double sx = (double)getWidth() / frontbuffer.getWidth();
  double sy = (double)getHeight() / frontbuffer.getHeight();
  scalex = Math.min(sx, sy);
  scaley = scalex;
  // center the image
  g2.translate((getWidth() - (frontbuffer.getWidth() * scalex)) / 2,
    (getHeight() - (frontbuffer.getHeight() * scaley)) / 2);
}
g2.scale(scalex, scaley);
if (interpolation != ImageInterpolation.NONE) {
  g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation.hint);
}
g2.drawImage(frontbuffer, 0, 0, null);
...