在保持Java的纵横比的同时调整图像大小

时间:2009-06-13 19:37:02

标签: java image resize

我试图在java中调整内存中的bufferedImage,但要保持图像的宽高比 我有类似的东西,但这不是很好

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

7 个答案:

答案 0 :(得分:68)

添加到Erik关于getScaledInstance的观点,如果你离开它去使用Java2D中推荐的缩放机制,你可能已经注意到你的图像看起来明显更糟。

原因是当Java2D不鼓励使用getScaledInstance和AreaAveragingScaleFilter时,他们没有用在API中使用的任何 替换它,而是使用我们自己的设备直接使用Java2D API。幸运的是,Chris Campbell(来自J2D团队)继续推荐使用增量缩放技术,该技术可为AreaAveragingScaleFilter提供类似的结果,并且运行速度更快;不幸的是,代码的大小合适,并没有解决你原来的尊重比例的问题。

大约6个月前,我一次又一次地看到关于“用Java缩放图像”的所有这些问题并最终收集了所有的建议,做了我能做的所有挖掘和研究,并将所有这些问题汇编成一个单一的“最佳实践” “image scaling library

API很简单,因为它只有一个类和一堆静态方法。基本用法如下:

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

这是simplest call,其中图书馆将对质量进行最佳猜测,尊重您的图像比例,并将结果放在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图像处理管道将落后于劣质软件管道。

如果这听起来很麻烦,那就是......这就是我编写图书馆并开源的原因,所以人们可以调整图像大小并继续生活,而不必担心它

希望有所帮助。

答案 1 :(得分:8)

对于初学者 - 请看第2行。不应该是getHeight()

你不想要调整大小的while循环,你想要找出调整大小比例,这是一个简单的数学运算。

(width / height) = (new_width / new_height)

如果你知道其中一个'新'尺寸,另一个可以通过乘法找到

new_height * (width / height) = new_width

您还可以使用BufferedImage's超类图像getScaledInstance()提供的惰性方法 - 使用-1表示宽度或高度将保持宽高比

例如: scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);

答案 2 :(得分:8)

如果已知源和目标的宽度,高度,请使用以下函数确定图像的比例。

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

答案 3 :(得分:6)

您可以查看perils-of-image-getscaledinstance.html,了解为什么应避免在某些答案中使用getScaledInstance()

该文章还提供了替代代码。

答案 4 :(得分:3)

我使用这两种方法来缩放图像,其中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;
}

答案 5 :(得分:0)

如果要通过保持纵横比来调整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);

答案 6 :(得分:0)

private static BufferedImage resize(BufferedImage img, int width, int height) {

        double scalex = (double) width / img.getWidth();
        double scaley = (double) height / img.getHeight();
        double scale = Math.min(scalex, scaley);

        int w = (int) (img.getWidth() * scale);
        int h = (int) (img.getHeight() * scale);

        Image tmp = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);

        BufferedImage resized = new BufferedImage(w, h, img.getType());
        Graphics2D g2d = resized.createGraphics();
        g2d.drawImage(tmp, 0, 0, null);
        g2d.dispose();

        return resized;
    }