I have an application where users are able to upload pictures in albums but naturally the uploaded images need to be resized so there are also thumbs available and the shown
You can use ImageMagick to create thumbnails.
convert -define jpeg:size=500x180 hatching_orig.jpg -auto-orient \
-thumbnail 250x90 -unsharp 0x.5 thumbnail.gif
To use it from Java you can try JMagick which provides a Java (JNI) interface to ImageMagick. Or you can simply invoke the ImageMagick commands directly using Runtime.exec
or ProcessBuilder
.
Do you really need the quality that is provided by using Image.SCALE_SMOOTH? If you don't, you can try using Image.SCALE_FAST. You might find this article helpful if you want to stick with something provided by Java.
I used im4java with GraphicsMagick in order to have really faster results (faster than ImageIO).
Used that sort of code :
public static void createFilePreview(final File originalFile, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
runThumbnail(new ConvertCmd(), originalFile.getAbsolutePath(), originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
}
public static void createFilePreview(final InputStream originalFileInputStream, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
final ConvertCmd cmd = new ConvertCmd();
cmd.setInputProvider(new Pipe(originalFileInputStream, null));
runThumbnail(cmd, "-", originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
}
private static void runThumbnail(final ConvertCmd cmd, final String originalFile, final String originalFileMimeType, final String destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
final IMOperation operation = new IMOperation();
// if it is a PDF, will add some optional parameters to get nicer results
if (originalFileMimeType.startsWith("application/pdf")) {
operation.define("pdf:use-trimbox=true"); // as it is said here http://www.prepressure.com/pdf/basics/page_boxes "The imposition programs and workflows that I know all use the TrimBox as the basis for positioning pages on a press sheet."
operation.density(300, 300); // augment the rendering from 75 (screen size) to 300 dpi in order to create big preview with good quality
}
operation.addImage("[0]"); // if it is a PDF or other multiple image source, will extract the first page / image, else it is ignored
operation.autoOrient(); // Auto-orient the image if it contains some orientation information (typically JPEG with EXIF header)
operation.thumbnail(maxWidth, maxHeight);
operation.addImage();
cmd.run(operation, originalFile, destinationPreviewFile);
}
I'm using code similar to the following to scale images, I removed the part that deals with preserving the aspect ratio. The performance was definitely better than 10s per image, but I don't remember any exact numbers. To archive better quality when downscaling you should scale in several steps if the original image is more than twice the size of the wanted thumbnail, each step should scale the previous image to about half its size.
public static BufferedImage getScaledImage(BufferedImage image, int width, int height) throws IOException {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double scaleX = (double)width/imageWidth;
double scaleY = (double)height/imageHeight;
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY);
AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR);
return bilinearScaleOp.filter(
image,
new BufferedImage(width, height, image.getType()));
}
The fastest way to scale an image in java without loosing image quality is to use Bilinear scaling. Bilinear is only good if you scale the image by 50% at a time because of the way it works. The following code is from 'Filthy rich clients' by Chet Haase. He explains multiple techniques in the book, but this one has the highest performance to quality trade-off.
It supports all types of BufferedImages so don't worry about compatability. It also lets java2D hardware accelerate your image because the calculations are done by Java2D. Don't worry if you don't understand that last part. The most important thing is that this is the fastest way to do it.
public static BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, boolean progressiveBilinear)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
BufferedImage scratchImage = null;
Graphics2D g2 = null;
int w, h;
int prevW = ret.getWidth();
int prevH = ret.getHeight();
if(progressiveBilinear) {
w = img.getWidth();
h = img.getHeight();
}else{
w = targetWidth;
h = targetHeight;
}
do {
if (progressiveBilinear && w > targetWidth) {
w /= 2;
if(w < targetWidth) {
w = targetWidth;
}
}
if (progressiveBilinear && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
if(scratchImage == null) {
scratchImage = new BufferedImage(w, h, type);
g2 = scratchImage.createGraphics();
}
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
prevW = w;
prevH = h;
ret = scratchImage;
} while (w != targetWidth || h != targetHeight);
if (g2 != null) {
g2.dispose();
}
if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
scratchImage = new BufferedImage(targetWidth, targetHeight, type);
g2 = scratchImage.createGraphics();
g2.drawImage(ret, 0, 0, null);
g2.dispose();
ret = scratchImage;
}
System.out.println("ret is "+ret);
return ret;
}
You will ever have a trade off between the speed of the resizing and the quality of the resulting picture. You might try another scaling algorithm of the JDK.
The best and most flexible tool for image editing AFAIK is ImageMagick.
There are two interfaces for the Java Language:
You should prefer im4java before using the command line directly to call ImageMagick.