Java Thumbnail generator – ImageScalar vs ImageMagic

Which Java Thumbnail generator is better or which one to use, which is most suitable for my case ?

You will find a lot many queries on stackoverflow website asking the same question. I will try to answer your query with my experience with above two libraries. If you don’t have the patience to read through it go directly to conclusion !

Problem Statement

We need to create thumbnail images of different sizes (200 x 150, 112 x 47, 100 x 100 and many more) from an image uploaded by our users. We don’t know what format, what size of image they will upload. We put a restriction on image size so that we can avoid out of memory problem.  For us it is pretty safe option but soon we realized there are other important parameters that we need to consider whenever we deal with images. Like –

  1. Image Size – Size in bytes
  2. Image file formats – They can of different types (JPEG, PNG TIFF etc.) for long go to this Wikipedia page.
  3. Image resolution – It is measured in different ways pixel, dpi(dots per inch) , cm
  4. Color Modes – Colour modes are the colour spaces you can work in when using Photoshop. These are four types  RGB, CMYK, L*a*b and HSB.

 

I will walk you through steps that I followed and in the end, I will share my conclusion based on my experience.

My First Choice – IMAGESCALR

I searched on internet and gone through various blogs and stackoverflow questions to reach at a conclusion that ImageScalr is the best and most efficient solution available in pure java to solve my problem. This library also implements the optimized incremental scaling algorithm. Only known problem was ImageScalr is that it does not support thumbnails for gif images.  Solution to this problem is to convert thumbnails to other file formats like PNG or JPG.

Here is how I implemented ImageScalr – First step is to include ImageScalr library in my project. Mine is a Maven project so all I have to do is to add maven dependency. You can also download the library directly if you are not using Maven. Here is my POM file

 

<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>

 

ImagScalr code to create Thumbnails

 


public void createThumbnail(File sourceImage, width, height) {
String destImageName = imageFile.getParent ()+width+&quot;_&quot;+height+&quot;_&quot;+ &quot;thunbnail.jpg&quot;;

File destImage = new File(destImageName);
if (!thumbNailFile.exists()) {
BufferedImage img = ImageIO.read(sourceImage);
BufferedImage thumbImg = Scalr.resize(img, Method.QUALITY, Mode.AUTOMATIC, width, height,Scalr.OP_ANTIALIAS);
ImageIO.write(thumbImg, fileFormat, destImage);
}
}

Here I have used Mode.AUTOMATIC. For more information on various available Modes is present here

 

In above code, ImageScalr library requires buffered image object to create thumbnails. Hence I used ImageIO java class present in javax.imageio.ImageIO Package. ImageIO is a utility class which provides lots of utility method related to images processing in Java. Most common of them is reading from image file and writing image to file in java. We can read and write different types images using ImageIO.

Example code for Read Image File –

 


BufferedImage img = ImageIO.read(sourceImage);

 

Write Image File


File destImage = new File(&quot;example.jpg&quot;);
ImageIO.write(renderedImage , &quot;.png&quot;, destImage);

 

First hurdle with ImagScalr

I faced problem with ImageIO, it cannot read images whose color model is CMYK and gives “CMMException” with message “Unsupported Image Type”. Because JPEGImageReader (the inner class that reads your file) reads only RGB color model. When we try to read images whose color model is not “RGB” then I got following exception:

 


java.awt.color.CMMException: Invalid image formatb
at sun.awt.color.CMM.checkStatus(CMM.java:131)
at sun.awt.color.ICC_Transform.(ICC_Transform.java:89)
at java.awt.image.ColorConvertOp.filter(ColorConvertOp.java:516)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.acceptPixels(JPEGImageReader.java:1169)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1137)
- javax.imageio.IIOException: Unsupported Image Type at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:977)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:948)
- java.lang.IllegalArgumentException: Numbers of source Raster bands and source color space components do not match at java.awt.image.ColorConvertOp.filter(ColorConvertOp.java:460)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.acceptPixels(JPEGImageReader.java:1169)

 

And I crossed it with following solution:

I did some research on internet to know that to read CYMK files I have to use JAI (Java Advanced Imaging) API. I solved the above problem by using JAI (Java Advanced Imaging) API.

JAI consists of Java code and native binaries for each supported operating system. JAI used to resolve above errors. It can read images whose color model is CYMK

Steps to implement: – Add following dependencies in pom.xml

<dependency>
<groupId>com.sun.media</groupId>
<artifactId>jai-codec</artifactId>
<version>1.1.3</version>
</dependency>

Example Code with JAI (Java Advanced Imaging) API implementation for reading CYMK files


public void createThumbnail(File sourceImage, width, height) {
String destImageName = imageFile.getParent()+width+&quot;_&quot;+height+&quot;_&quot;+ &quot;thunbnail.jpg&quot;;
File destImage = new File(destImageName);

BufferedImage img = null;
try{
img = ImageIO.read(destination);
} catch(CMMException ce) {
SeekableStream seekableStream = new FileSeekableStream(destination);
ParameterBlock pb = new ParameterBlock();
pb.add(seekableStream);
img = JAI.create(&quot;jpeg&quot;, pb).getAsBufferedImage();
}

if (!thumbNailFile.exists()) {
BufferedImage thumbImg = Scalr.resize(img, Method.QUALITY, Mode.AUTOMATIC, width, height,Scalr.OP_ANTIALIAS);
ImageIO.write(thumbImg, fileFormat, destImage);
}

Changes are shown between line no 5 to 13. In above code we are catching CMMException and then trying to read file using JAI Api.  SeekableStream is used to create a ParameterBlock, to be passed to the JAI create() call.

 

Yet another hurdle – Out of Memory exception

When everything seems to be perfect and I was ready to go live with my code I bumped into unusual  out of memory Exception for some images.  I did more research to narrow down the problem and found that I have one CYMK image with 23376X 14260 high resolution and actual file size is below 4 MB. But when I try to convert such image into BufferedImage, its size become more than 512 MB and when I pass this image to my ImagScalr and try to create thumbnails then I run out of JVM memory. 

 

I was using Caching heavily so my JVM process was already under memory crunch and to process such large image from one user consumes all of the available memory and In case multiple user try to perform such task I will be at receiving end and cannot predict how much RAM I need.

First solution that comes to my mind is to increase JVM memory. I tried that but I still faced the problem as my application is already using memory heavily.

Then ImageMagic comes into picture which gives me option to delegate the thumbnail creation task to another process outside my JVM.

 

Image Magic Comes to Rescue

ImageMagick is an open source software suite for displaying, converting, and editing raster image files. It can read and write over 200 image file formats. ImageMagick is licensed under the Apache 2.0 license.

File format conversion – One of the basic and thoroughly implemented features of ImageMagick is its ability to efficiently and accurately convert images between different file formats (it uses the command “convert” to achieve this). ImageMagik provides high quality image conversion and operate on large images. But then there is lot of inter-process communication stuff that I have to deal with. I wanted something that I can do in Java coding. Then I come to know about the Im4java.

 

Im4java

Im4java is a pure-java interface to the ImageMagick commandline. Im4java is second java interface to ImageMagick. JMagick is the first interface. Im4java is not replacement of JMagick. JMagick is thin JNI(java native interface) layer above the imageMagick. But im4java generates the commandline for the ImageMagick commands and passes the generated line to the selected IM-command.

Implementation

To start the actual process you need to download and install image magick setup. For more information click on the below link http://www.imagemagick.org/script/binary-releases.php#windows

After installing you need to set the search path. To setup your search path for the tools you have three options available. Choose any one of them:

  1. Set the environment variable IM4JAVA_TOOLPATH. This variable should contain a list of directories to search for your tools separated by your platform path delemiter (on *NIX typically “:”, on Windows “;”).
  2. Globally set the search path from within your java program

String myPath=&quot;C:\\Programs\\ImageMagick;C:\\Programs\\exiftool&quot;;
ProcessStarter.setGlobalSearchPath(myPath);

This will override any values set with IM4JAVA_TOOLPATH.

3. Set the search path for an individual command

String imPath=&quot;C:\\Programs\\ImageMagick&quot;;
ConvertCmd cmd = new ConvertCmd();
cmd.setSearchPath(imPath);

After setting the search path you need to add the POM dependency in your project and also write the Java code.

 

Steps to implement

Add POM dependency

<dependency>
<groupId>org.im4java</groupId>
<artifactId>im4java</artifactId>
<version>1.4.0</version>
</dependency>

 

Example Code

  • Create object of ConvertCmd class for run the operation .
  • Create object of IMOperationclass and add source image on which you want to perform operation like create a thumbnail of image. Give the name of destination image where thumbnail of image stored. You can set dimensions of destination image using op.thumbnail(width,height);
  • At end run the operation by using run() method of ConvertCmd class and pass the operation(op) as parameter.

ConvertCmd cmd = new ConvertCmd();
String destinationFileName = sourceImage.getParent() +&quot;/&quot;+ width + &quot;_&quot; + height + &quot;_&quot; + imageName;
File thumbNailFile = new File(destinationFileName);
if (!thumbNailFile.exists()) {
IMOperation op = new IMOperation();
op.addImage(sourceFileName);
op.thumbnail(width);
op.addImage(destinationFileName);
cmd.run(op);
}

In above code, first I have created an object of class ConvertCmd. Run method of this class will perform the operation. Then I created  object of IMOperation class and added source image on which you want to perform operation like create a thumbnail of image. Then give the name of destination image where thumbnail of image is to be stored.

You can set dimensions of destination image using op.thumbnail(width,height). And in the end run the operation by using run() method of ConvertCmd class and pass the operation(op) as parameter.

Finally I am able to achieve what I wanted. I am able to process input image files of any format, color mode without any problem and my thumbnails are created by ImageMagick with outofmemory exception.

 

Few Noticeable Features of IM4Java

  1. It supports most of ImageMagick’s commands
  2. It supports BufferedImage, i.e we can give BufferedImage object as input to IM commands and pipe output of IM commands in BufferedImage.
  3. We can run commands asynchronously.
  4. Provides high quality of images.

Advantages of im4java

The interface of the IM commandline is quite stable, so our java program (and the im4java-library) will work across many versions of IM. im4java also provides a better OO interface. And most important is that you can use im4java everywhere JMagick interface can’t be used because of the JNI hazard

Disadvantages of im4java

We are limited to the capabilities of the IM commands. But With JMagick, we have access to the low-level interface of IM and therefore we have a very detailed control of the processing of images with better performance.

 

After the long story here is my Conclusion

Conclusion

  1. For simple RGB files use ImageScalr . ImageIO class is used for reading files and ImageScalr to create thumbnails
  2. For supporting RGB + CYMK, use ImageIO and JAI (Java Advanced Imaging) API for reading files and ImageScalr to create thumbnail.
  3. In case you don’t know what file formats, color mode you are going to deal with, safest option is to use ImageMagick.

References:

http://im4java.sourceforge.net/
http://im4java.sourceforge.net/docs/dev-guide
http://im4java.sourceforge.net/docs/dev-guide.html
For download Demo application, please click on this link ThumbnailDemo

 

Leave a Reply

Your email address will not be published. Required fields are marked *


2 × two =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>