Thumbnail image scaling with Java

This tutorial offers example code for a problem we had recently: The automated scaling of images (in our case uploaded via REST Webservice) into thumbnails of different resolutions. Once uploaded, the original image is scaled into different resolutions to provide fitting images for profile, chat or miscellaneous pictures in a web application.

The example code is available as a Maven project on Github.

The scaling is done by the imgscalr library and we added some code in order to scale as many different images or thumbnails as required by the front end web application.

Prerequisites

– Maven
– JDK7

If you never used Maven before, check out this tutorial for installation advice.

Thumbnail generation code step-by-step

Starting off with the simplest class, a normal POJO that contains the width, the height or the final pixel size of the desired thumbnails.

public class ImageResolution {
	private int width = 0;
	private int heigth = 0;
	private int pixel = 0;

	public ImageResolution( int pixel ) {
		this.pixel = pixel;
	}
	
	public ImageResolution( int width, int heigth ) {
		this.width = width;
		this.heigth = heigth;
	}
	
	public ImageResolution( int width, int heigth, int pixel ) {
		this.width = width;
		this.heigth = heigth;
		this.pixel = pixel;
	}

	public int getWidth() {
		return width;
	}
	
	public void setWidth(int width) {
		this.width = width;
	}
	
	public int getHeigth() {
		return heigth;
	}
	
	public void setHeigth(int heigth) {
		this.heigth = heigth;
	}
	
	public int getPixel() {
		return pixel;
	}
	
	public void setPixel(int pixel) {
		this.pixel = pixel;
	}
	
	@Override
	public String toString() {
		return "ImageResolution [width=" + width + ", heigth=" + heigth + ", pixel=" + pixel + "]";
	}
	
}

This POJO (Plain Old Java Object) class is generated by a “Smart” Enum in our code example. In this Enum we define all the thumbnail resolutions to scale the original image to. This is the only place in the code we have to adapt if we want to add or remove different resolutions.

public enum ImageSize {
	// width x height
	WXH_64X64	("WXH_64X64"),
	WXH_128X128	("WXH_128X128"),
	WXH_256X256	("WXH_256X256"),
	WXH_512X512	("WXH_512X512"),
	// pixel
	P_250		("P_250"),
	P_500		("P_500"),
	P_1000		("P_1000"),
	// source
	SOURCE	("SOURCE");
	
    private final String val;       

    private ImageSize( String s ) {
    	val = s;
    }
    
    public String toString() {
       return this.val;
    }
    
   public ImageResolution toImageResolution() {
	   ImageResolution res = null;
	   
	   switch( this ) {
		case WXH_64X64:
			res =  new ImageResolution( 64,64 );
			break;
			
		case WXH_128X128:
			res =  new ImageResolution( 128, 128 );
			break;
			
		case WXH_256X256:
			res =  new ImageResolution( 256, 256 );
			break;

		case WXH_512X512:
			res =  new ImageResolution( 512, 512 );
			break;
			
		case P_250:
			res =  new ImageResolution( 250 );
			break;
			
		case P_500:
			res =  new ImageResolution( 500 );
			break;
			
		case P_1000:
			res =  new ImageResolution( 1000 );
			break;
			
		case SOURCE:
			res =  new ImageResolution( -1, -1, -1 );
			break;
	   }
	   
	   return res;
   }
}

The actual scaling is done by the ImageScaler class which uses exactly one method of the imgscalr library.
We iterate over the values of Enum and retrieve the ImageResolution POJO which contains the defined resolution or pixels for one single Enum member.

public class ImageScaler {
	
	public static void scaleAll( BufferedImage image, String imageName, String imageType ) throws IOException {
		for ( ImageSize size : ImageSize.values() ) { 
		    scale( image, size, imageName, imageType );
		}
	}
	
	public static void scale( BufferedImage image, ImageSize size, String imageName, String imageType ) throws IOException {
		
		ImageResolution res = size.toImageResolution();
		
		BufferedImage scaledImage = null;
		
		// pixel
		if( res.getPixel() > 0 ) {
			scaledImage = Scalr.resize( image, Method.QUALITY, Mode.AUTOMATIC, res.getPixel(), Scalr.OP_ANTIALIAS );
		}
		// width x height
		else if( res.getWidth() > 0 && res.getHeigth() > 0 ) {
			scaledImage = Scalr.resize( image, Method.QUALITY, Mode.AUTOMATIC, res.getWidth(), res.getHeigth(), Scalr.OP_ANTIALIAS );
		}
		// source
		else if( res.getWidth() < 0 && res.getHeigth() < 0 && res.getPixel() < 0 ) {
			scaledImage = image;
		}
		
		File file = new File( size.toString() + "_" + imageName );

		ImageIO.write( scaledImage, imageType, file );
	}
	
}

The Scalr.resize() method has multiple parameters to manipulate the scaling process. You can set different qualities (balanced, ultra quality, speed etc.) and modes to fit the image exactly, fit to parameterized width or height or to match exactly. Furthermore you can set the operation mode (last parameter) to antialising, brighter, darker or greyscale. All these parameters affect the speed and result of the scaling process.

Finally we run and test the code via the main class.

public class App {
	
	// get maven resource path
	private static String getRelativeResourcePath( String resource ) {
		if( resource == null || resource.equals("") ) return null;
		
		return App.class.getClassLoader().getResource( resource ).getPath();
	}
	
	public static void main(String[] args) throws IOException {
		
		String fileName = "example.png";
		
		long startTime = System.currentTimeMillis();
		File f = new File( getRelativeResourcePath( fileName ) );
		
		// for input stream (we required this in the web application (REST - POST))
		//BufferedImage image = ImageIO.read( inputStream );
		
		BufferedImage image = ImageIO.read(f); // load image

		ImageScaler.scaleAll( image, fileName, "png" );

		System.out.println("Process duration : " + ( System.currentTimeMillis() - startTime ) );

	}
}

The current version in the Git repository took about 5 seconds for a 35 MB image on the local development computer. That sounds like a long time, but the image is pretty big. The speed improves a lot with smaller images.

If you have questions or problems, feel free to ask or comment.

Facebooktwitterredditpinterestlinkedinmail

Related posts

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.