Java - 在不损失质量的情况下调整图像大小视觉比较兰佐斯重采样渐进式扩展关于JDK扩展方法的一句话

我有10,000张照片需要调整大小,所以我有一个Java程序来做到这一点。不幸的是,图像的质量丢失得很差,我无法访问未压缩的图像。

import java.awt.Graphics;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;


import javax.imageio.ImageIO;
/**
 * This class will resize all the images in a given folder
 * @author 
 *
 */
public class JavaImageResizer {

    public static void main(String[] args) throws IOException {

        File folder = new File("/Users/me/Desktop/images/");
        File[] listOfFiles = folder.listFiles();
        System.out.println("Total No of Files:"+listOfFiles.length);
        BufferedImage img = null;
        BufferedImage tempPNG = null;
        BufferedImage tempJPG = null;
        File newFilePNG = null;
        File newFileJPG = null;
        for (int i = 0; i < listOfFiles.length; i++) {
              if (listOfFiles[i].isFile()) {
                System.out.println("File " + listOfFiles[i].getName());
                img = ImageIO.read(new File("/Users/me/Desktop/images/"+listOfFiles[i].getName()));
                tempJPG = resizeImage(img, img.getWidth(), img.getHeight());
                newFileJPG = new File("/Users/me/Desktop/images/"+listOfFiles[i].getName()+"_New");
                ImageIO.write(tempJPG, "jpg", newFileJPG);
              }
        }
        System.out.println("DONE");
    }

    /**
     * This function resize the image file and returns the BufferedImage object that can be saved to file system.
     */
        public static BufferedImage resizeImage(final Image image, int width, int height) {
    int targetw = 0;
    int targeth = 75;

    if (width > height)targetw = 112;
    else targetw = 50;

    do {
        if (width > targetw) {
            width /= 2;
            if (width < targetw) width = targetw;
        }

        if (height > targeth) {
            height /= 2;
            if (height < targeth) height = targeth;
        }
    } while (width != targetw || height != targeth);

    final BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    final Graphics2D graphics2D = bufferedImage.createGraphics();
    graphics2D.setComposite(AlphaComposite.Src);
    graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
    graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    graphics2D.drawImage(image, 0, 0, width, height, null);
    graphics2D.dispose();

    return bufferedImage;
}

我正在处理的图像是这样的:Firwork - original - large

这是我在Microsoft Paint中完成的手动调整大小:

resize - using Paint - small

这是我的程序[双线性]的输出:

resize - using java program - small

更新:使用无显著差异BICUBIC

这是我的程序[双三次]的输出:

enter image description here

是否无论如何都可以提高程序输出的质量,这样我就不必手动调整所有照片的大小?

提前感谢您!


答案 1

遗憾的是,在 Java 中没有推荐的开箱即用的缩放,以提供视觉上良好的结果。其中,以下是我推荐的扩展方法:

  • Lanczos3 重采样(通常视觉上更好,但速度较慢)
  • 渐进式向下缩放(通常视觉上很好,可能相当快)
  • 一步式放大,向上扩展(双三次方快速,效果好,通常不如Lanczos3)Graphics2d

每个方法的示例都可以在此答案中找到。

视觉比较

这是使用不同方法/库缩放到的图像。单击图像以获取完整尺寸:96x140

comparison

comparison zoom

  1. 莫滕·诺贝尔的lib Lanczos3
  2. 缩略图器双线性渐进式缩放
  3. Imgscalr ULTRA_QUALTY(1/7 步双三次进行性缩放)
  4. Imgscalr QUALTY(1/2步双三次进行性缩放)
  5. Morten Nobel的lib BilineAr Progressive Scaling
  6. Graphics2d双三次插值
  7. Graphics2d最近邻插值
  8. Photoshop CS5 双三次型作为参考

不幸的是,单个图像不足以判断缩放算法,您应该测试具有锋利边缘的图标,带有文本的照片等。

兰佐斯重采样

据说有利于升压,特别是降尺度。不幸的是,当前的JDK中没有本机实现,因此您可以自己实现它并使用像Morten Nobel的lib这样的lib。使用所述 lib 的简单示例:

ResampleOp resizeOp = new ResampleOp(dWidth, dHeight);
resizeOp.setFilter(ResampleFilters.getLanczos3Filter());
BufferedImage scaledImage = resizeOp.filter(imageToScale, null);

该库发布在maven-central上,不幸的是没有提到。缺点是它通常非常慢,没有任何高度优化或硬件加速的实现。Nobel 的实现速度比 .在他的博客上阅读更多关于这个lib的信息Graphics2d

渐进式扩展

在 Chris Campbell 关于在 Java 中扩展的博客中提到,渐进式缩放基本上是以较小的步骤增量缩放图像,直到达到最终尺寸。坎贝尔将其描述为将宽度/高度减半,直到达到目标。这产生了良好的结果,并且可以与硬件加速一起使用,因此通常具有非常好的性能,在大多数情况下具有可接受的结果。这样做的主要缺点是,如果缩小到不到一半的使用,则提供相同的平庸结果,因为它只缩放一次。Graphics2DGraphics2D

以下是它如何工作的简单示例:

progressive scaling

以下库包含基于 的渐进式缩放形式:Graphics2d

缩略图器 v0.4.8

如果目标至少是每个维度的一半,则使用渐进式双线性算法,否则它将使用简单的双线性缩放和双三次进行升级。Graphics2d

Resizer resizer = DefaultResizerFactory.getInstance().getResizer(
  new Dimension(imageToScale.getWidth(), imageToScale.getHeight()), 
  new Dimension(dWidth, dHeight))
BufferedImage scaledImage = new FixedSizeThumbnailMaker(
  dWidth, dHeight, false, true).resizer(resizer).make(imageToScale);

它的速度与一步缩放一样快或略快,在我的基准测试中平均得分为6.9秒。Graphics2d

Imgscalr v4.2

使用渐进式双三次鳞屑。在设置中,它使用坎贝尔风格的算法,每步将尺寸减半,而具有更精细的步骤,每次增量将大小减小1/7,这通常生成更柔和的图像,但最大限度地减少仅使用1次迭代的情况。QUALITYULTRA_QUALITY

BufferedImage scaledImage = Scalr.resize(imageToScale, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_EXACT, dWidth, dHeight, bufferedImageOpArray);

主要的缺点是性能。 比其他库慢得多。甚至比缩略图器的实现还要慢一点。我的简单基准测试分别产生了26.2秒和11.1秒的平均值。ULTRA_QUALITYQUALITY

莫滕·诺贝尔的 lib v0.8.6

还实现了所有基本(双线性,双三次和最近邻)的渐进式缩放Graphics2d

BufferedImage scaledImage = new MultiStepRescaleOp(dWidth, dHeight, RenderingHints.VALUE_INTERPOLATION_BILINEAR).filter(imageToScale, null);

关于JDK扩展方法的一句话

当前缩放图像的jdk方式是这样的

scaledImage = new BufferedImage(dWidth, dHeight, imageType);
Graphics2D graphics2D = scaledImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(imageToScale, 0, 0, dWidth, dHeight, null);
graphics2D.dispose();

但大多数人对缩小比例的结果感到非常失望,无论使用什么插值或其他。另一方面,放大似乎会产生可接受的图像(最好是双三次)。在以前的JDK版本(我们谈论的是90年代v1.1)中,引入了带有参数的良好视觉效果,但您不鼓励使用它 - 阅读此处的完整说明RenderHintsImage.getScaledInstance()SCALE_AREA_AVERAGING


答案 2

缩略图器是一个库,旨在以简单的方式创建高质量的缩略图,对现有图像进行批量转换是其用例之一。

执行批量调整大小

例如,要使用缩略图器调整示例,您应该能够使用以下代码获得类似的结果:

File folder = new File("/Users/me/Desktop/images/");
Thumbnails.of(folder.listFiles())
    .size(112, 75)
    .outputFormat("jpg")
    .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

这将继续并获取目录中的所有文件,然后逐个处理它们,尝试调整它们的大小以适合112 x 75的尺寸,并且它将尝试保留原始图像的宽高比以防止图像的“翘曲”。images

无论图像类型如何,缩略图器将继续读取所有文件(只要Java图像IO支持格式,缩略图器就会处理它),执行调整大小操作并将缩略图输出为JPEG文件,同时将a附加到文件名的开头。thumbnail.

下图说明了如果执行上述代码,原始文件的文件名将如何在缩略图的文件名中使用。

images/fireworks.jpg     ->  images/thumbnail.fireworks.jpg
images/illustration.png  ->  images/thumbnail.illustration.png
images/mountains.jpg     ->  images/thumbnail.mountains.jpg

生成高质量缩略图

在图像质量方面,正如Marco13的答案中提到的,Chris Campbell在他的The Perils of Image.getScaledInstance()中描述的技术是在Branotleator中实现的,从而产生高质量的缩略图,而无需任何复杂的处理。

以下是使用缩略图器调整原始问题中显示的烟花图像大小时生成的缩略图:

Thumbnail of image in original question

上面的图像是使用以下代码创建的:

BufferedImage thumbnail = 
    Thumbnails.of(new URL("http://i.stack.imgur.com/X0aPT.jpg"))
        .height(75)
        .asBufferedImage();

ImageIO.write(thumbnail, "png", new File("24745147.png"));

该代码表明它也可以接受URL作为输入,并且缩略图器也能够创建s。BufferedImage


免责声明:我是缩略图库的维护者。


推荐