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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/991349/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:56:22  来源:igfitidea点击:

Resize image while keeping aspect ratio in Java

javaimageresize

提问by user63898

im trying to resize bufferdImage in memory in java but to keep the aspect ratio of the image im have something like this but this is not good

我试图在 java 中调整内存中的 bufferdImage 的大小,但为了保持图像的纵横比,我有类似的东西,但这并不好

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

采纳答案by Erik

You may have a look at perils-of-image-getscaledinstance.htmlthat explains why getScaledInstance(), used in some of the answers, should be avoided.

您可能会查看perils-of-image-getscaledinstance.html,它解释了为什么getScaledInstance()应该避免在某些答案中使用的 。

The article also provides alternative code.

文章还提供了替代代码。

回答by Max

For starters - take a look at line 2. Shouldnt that be getHeight()?

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

You dont want a while loop for the resizing, you want to find out the resizing ratio, which is a simple bit of math.

您不需要用于调整大小的 while 循环,而是想找出调整大小比率,这是一个简单的数学运算。

(width / height) = (new_width / new_height)

If you know one of the 'new' sizes, the other can be found via multiplication

如果您知道“新”尺寸之一,则可以通过乘法找到另一个

new_height * (width / height) = new_width

You can also use the lazy method provided by BufferedImage'ssuperclass Image, getScaledInstance()- using -1 for either width or height will maintain aspect ratio

您还可以使用BufferedImage's超类 Image提供的惰性方法,getScaledInstance()- 使用 -1 作为宽度或高度将保持纵横比

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

前任:
scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);

回答by akarnokd

If you want to resize a picture of w0 x h0 to w1 x h1 by keeping the aspect ratio, then calculate the vertical and horizontal scale and select the smaller one.

如果你想通过保持纵横比将一张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);

回答by asingh

If width, height of source and target are known, use following function to determine scale of the image.

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

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

}

}

Then use this scale to scale up/down the image using following code

然后使用此比例使用以下代码放大/缩小图像

Image scaledImage = sourceBufferedImage.getScaledInstance((int) (width * scale), (int) (height * scale), Image.SCALE_SMOOTH);

回答by Riyad Kalla

Adding to Erik's point about getScaledInstance, if you moved away from it to using the recommended scaling mechanisms in Java2D, you might have noticed that your images look noticeably worse.

补充 Erik 关于 getScaledInstance 的观点,如果您从它转向使用 Java2D 中推荐的缩放机制,您可能已经注意到您的图像看起来明显更糟。

The reason for that is when the Java2D discouraged use of getScaledInstance and AreaAveragingScaleFilter, they didn't replace it with anything as easyto use in the API, instead we were left to our own devices using Java2D APIs directly. Fortunately, Chris Campbell (from the J2D team) followed up with the recommendation of using an incremental scaling technique that gives similar looking results to AreaAveragingScaleFilter and runs faster; unfortunately the code is of a decent size and doesn't address your original question of honoring proportions.

这样做的原因是当 Java2D 不鼓励使用 getScaledInstance 和 AreaAveragingScaleFilter 时,他们并没有用API 中易于使用的任何东西来替换它,而是让我们直接使用 Java2D API 给我们自己的设备。幸运的是,Chris Campbell(来自 J2D 团队)遵循了使用增量缩放技术的建议,该技术提供与 AreaAveragingScaleFilter 相似的外观结果并且运行速度更快;不幸的是,代码大小合适,并没有解决您最初的尊重比例问题。

About 6 months ago I saw all these questions on SO again and again about "scaling images in Java" and eventually collected all the advice, did all the digging and research I could, and compiled all of into a single "best practices" image scaling library.

大约 6 个月前,我一次又一次地在 SO 上看到所有这些关于“在 Java 中缩放图像”的问题,并最终收集了所有建议,进行了所有我能做的挖掘和研究,并将所有这些编译成一个“最佳实践”图像缩放图书馆

The API is dead simple as it is only 1 class and a bunch of static methods. Basic use looks like this:

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

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

This is the simplest callwhere the library will make a best-guess at the quality, honor your image proportions, and fit the result within a 320x320 bounding box. NOTE, the bounding box is just the maximum W/H used, since your image proportions are honored, the resulting image would still honor that, say 320x200.

这是最简单的调用,库将对质量做出最佳猜测,尊重您的图像比例,并将结果放入 320x320 边界框内。注意,边界框只是使用的最大 W/H,因为您的图像比例受到尊重,结果图像仍然会尊重该比例,例如 320x200。

If you want to override the automatic mode and force it to give you the best-looking result and even apply a very mild anti-alias filter to the result so it looks even better (especially good for thumbnails), that call would look like:

如果您想覆盖自动模式并强制它为您提供最佳效果,甚至对结果应用非常温和的抗锯齿过滤器,使其看起来更好(尤其适用于缩略图),该调用将如下所示:

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

These are all just examples, the API is broad and covers everything from super-simple use cases to very specialized. You can even pass in your own BufferedImageOps to be applied to the image (and the library automatically fixes the 6-year BufferedImageOp JDK bug for you!)

这些都只是示例,API 非常广泛,涵盖了从超级简单的用例到非常专业的所有内容。您甚至可以传入您自己的 BufferedImageOps 以应用于图像(并且该库会自动为您修复 6 年的 BufferedImageOp JDK 错误!)

There is a lot more to scaling images in Java successfully that the library does for you, for example always keeping the image in one of the best supported RGB or ARGB image types while operating on it. Under the covers the Java2D image processing pipeline falls back to an inferior software pipeline if the image type used for any image operations is poorly supported.

该库为您在 Java 中成功缩放图像还有很多其他功能,例如,在对图像进行操作时始终将图像保持在受支持的最佳 RGB 或 ARGB 图像类型之一。如果用于任何图像操作的图像类型没有得到很好的支持,Java2D 图像处理管道会在幕后退回到劣质的软件管道。

If all that sounded like a lot of headache, it sort of is... that's why I wrote the library and open sourced it, so folks could just resize their images and move on with their lives without needing to worry about it.

如果所有这些听起来都令人头疼,那是……这就是我编写库并将其开源的原因,这样人们就可以调整图像大小并继续他们的生活,而无需担心。

Hope that helps.

希望有帮助。

回答by ZZ 5

I use these two methods to scale images, where max is the bigger dimension of your destination image. For 100x100 image it will be 100, for 200x300 image it will be 300.

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

回答by naskar

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