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:
This is the manual resizing I've done in Microsoft Paint:
and this is the output from my program [bilinear]:
UPDATE: No significant difference using BICUBIC
and this is the output from my program [bicubic]:
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:
Graphics2d
bicubic fast and good results, usually not as good as Lanczos3)Examples for every method can be found in this answer.
Here is your image scaled to 96x140
with different methods/libs. Click on the image to get the full size:
Graphics2d
Bicubic interpolationGraphics2d
Nearest Neighbor interpolationUnfortunately a single image is not enough to judge a scaling algorithm, you should test icons with sharp edges, photos with text, etc.
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.
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:
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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With