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

在Java中调整图像大小,同时保持纵横比,java,image,resize,Java,Image,Resize,我试图用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 >

我试图用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);

首先,请看第2行。这不应该是
getHeight()

你不需要一个while循环来调整大小,你需要找出调整大小的比率,这是一个简单的数学问题

(width / height) = (new_width / new_height)
如果您知道其中一种“新”尺寸,则可以通过乘法找到另一种尺寸

new_height * (width / height) = new_width
您还可以使用由
buffereImage的
超类映像提供的惰性方法,
getScaledInstance()
-对宽度或高度使用-1将保持纵横比

例如:

scaledPic=picture.getScaledInstance(新的\u宽度-1,Image.SCALE\u快速)

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

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

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);
您可以看一看,这解释了为什么应该避免在某些答案中使用
getScaledInstance()


本文还提供了其他代码。

除了Erik关于getScaledInstance的观点之外,如果您不再使用Java2D中推荐的缩放机制,您可能已经注意到您的图像看起来明显更糟

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

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

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

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非常广泛,涵盖了从超级简单的用例到非常专业的一切。您甚至可以传入自己的BuffereImage操作以应用于映像(库会自动为您修复6年BuffereImage操作JDK错误!)

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

如果这一切听起来很头痛的话,那有点。。。这就是为什么我编写了这个库并将其开源,这样人们就可以调整他们的图像大小,继续他们的生活而不必担心它


希望能有所帮助。

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

是的,它的getHeight输入错误有什么原因我不能将getScaledInstance与BufferdImage一起使用吗?那应该很好,因为BuffereImage扩展了ImageHah,感谢第一个等式。在我混乱的大脑里,有什么东西发出咔哒声!什么代表frontbuffer?这是我的一段代码的摘录,该代码使用交换缓冲渲染动画。frontbuffer只是目前需要显示的BuffereImage。只是看了一下这个库,它帮助了很多;如果可以的话,我会+2这篇文章,事实上你的项目有自己的maven repo托管。谢谢谢谢你的客气话,安德鲁,我很高兴你觉得它很有用@AndrewB我帮你+1他就得到+2。。这当然是值得的。”…将所有这些都汇编到一个“最佳实践”图像缩放库中。”这整句话对我来说是鼓舞人心的。库(v4.2)的问题在于它不能同时尊重高度和宽度。例如,我有一个图像2000x2000。我想把它装在700x200 squire里。我希望输出为200x200,但得到了700x700。是的,我指定了目标高度和目标宽度
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;
    }