java scalr,Java - 调整图像大小而不会降低质量

本文探讨了Java中批量图片缩放导致的质量损失问题,并推荐了几种优化方法,包括Lanczos3重采样、渐进式下采样和基于Graphics2D的一次性缩放。这些方法在保持速度与质量之间取得平衡,为Java图像处理提供了更好的选择。
摘要由CSDN通过智能技术生成

I have 10,000 photos that need to be resized so I have a Java program to do that. Unfortunately, the quality of the image is poorly lost and I don't have access to the uncompressed images.

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;

}

An image I am working with is this:

X0aPT.jpg

This is the manual resizing I've done in Microsoft Paint:

cb56fb336c48595d08ac71648168621d.png

and this is the output from my program [bilinear]:

Z7tD3.jpg

UPDATE: No significant difference using BICUBIC

and this is the output from my program [bicubic]:

oMU52.jpg

is there anyway to increase the quality of the program output so I don't have to manually resize all photos?

Thank you in advance!

解决方案

Unfortunately, there is no recommended out-of-the-box scaling in Java that provides visually good results. Among others, here are the methods I recommend for scaling:

Lanczos3 Resampling (usually visually better, but slower)

Progressive Down Scaling (usually visually fine, can be quite fast)

One-Step scaling for up scaling (with Graphics2d bicubic fast and good results, usually not as good as Lanczos3)

Examples for every method can be found in this answer.

Visual Comparison

Here is your image scaled to 96x140 with different methods/libs. Click on the image to get the full size:

sH1tO.png

JhRQT.png

Morten Nobel's lib Lanczos3

Thumbnailator Bilinear Progressive Scaling

Imgscalr ULTRA_QUALTY (1/7 step Bicubic Progressive Scaling)

Imgscalr QUALTY (1/2 step Bicubic Progressive Scaling)

Morten Nobel's lib Bilinear Progressive Scaling

Graphics2d Bicubic interpolation

Graphics2d Nearest Neighbor interpolation

Photoshop CS5 bicubic as reference

Unfortunately a single image is not enough to judge a scaling algorithm, you should test icons with sharp edges, photos with text, etc.

Lanczos Resampling

Is said to be good for up- and especially downscaling. Unfortunately there is no native implementation in current JDK so you either implement it yourself and use a lib like Morten Nobel's lib. A simple example using said lib:

ResampleOp resizeOp = new ResampleOp(dWidth, dHeight);

resizeOp.setFilter(ResampleFilters.getLanczos3Filter());

BufferedImage scaledImage = resizeOp.filter(imageToScale, null);

The lib is published on maven-central which is not mentioned unfortunately. The downside is that it usually is very slow without any highly optimized or hardware accelerated implementations known to me. Nobel's implementation is about 8 times slower than a 1/2 step progressive scaling algorithm with Graphics2d. Read more about this lib on his blog.

Progressive Scaling

Mentioned in Chris Campbell's blog about scaling in Java, progressive scaling is basically incrementally scaling an image in smaller steps until the final dimensions are reached. Campbell describes it as halving width/height until you reach target. This produces good results and can be used with Graphics2D which can be hardware accelerated, therefore usually having very good performance with acceptable results in most cases. The major downside of this is if downscaled less than half using Graphics2D provides the same mediocre results since it is only scaled once.

Here is a simple example on how it works:

xGtdc.png

The following libs incorporate forms of progressive scaling based on Graphics2d:

Uses the progressive bilinear algorithm if the target is at least half of every dimension, otherwise it uses simple Graphics2d bilinear scaling and bicubic for upscaling.

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

It is as fast or slightly faster than one-step scaling with Graphics2d scoring an average of 6.9 sec in my benchmark.

Uses progressive bicubic scaling. In the QUALITY setting it uses Campbell style algorithm with halving the dimensions every step while the ULTRA_QUALITY has finer steps, reducing the size every increment by 1/7 which generates generally softer images but minimizes the instances where only 1 iteration is used.

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

The major downside is performance. ULTRA_QUALITY is considerably slower than the other libs. Even QUALITY a bit slower than Thumbnailator's implementation. My simple benchmark resulted in 26.2 sec and 11.1 sec average respectively.

Has also implementations for progressive scaling for all basic Graphics2d (bilinear, bicubic & nearest neighbor)

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

A word on JDK Scaling Methods

Current jdk way to scale an image would be something like this

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

but most are very disappointed with the result of downscaling no matter what interpolation or other RenderHints are used. On the other hand upscaling seems to produce acceptable images (best would be bicubic). In previous JDK version (we talking 90s v1.1) Image.getScaledInstance() was introduced which provided good visual results with parameter SCALE_AREA_AVERAGING but you are discouraged to use it - read the full explanation here.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值