Site Network: Personal | Professional | Photography

Technical Blog

This blog will contain content related to Java, Seam, Security, my sites and projects, as well as other technical subjects I am interested in.

Comments and questions are welcome!

How To Resize Uploaded Images Using Java – Better Way

Based on helpful comments from Matt on this previous post:

How To Resize Uploaded Images Using Java

I have upgraded the image resizing code. The results are noticeably better in quality, even at thumbnail sizes. I wanted to share the completed new code, in case anyone needs it.

Again, thanks to Matt S. for his pointers.

    /**
     * The JAI.create action name for handling a stream.
     */
    private static final String JAI_STREAM_ACTION = "stream";

    /**
     * The JAI.create action name for handling a resizing using a subsample averaging technique.
     */
    private static final String JAI_SUBSAMPLE_AVERAGE_ACTION = "SubsampleAverage";

    /**
     * The JAI.create encoding format name for JPEG.
     */
    private static final String JAI_ENCODE_FORMAT_JPEG = "JPEG";

    /**
     * The JAI.create action name for encoding image data.
     */
    private static final String JAI_ENCODE_ACTION = "encode";

    /**
     * The http content type/mime-type for JPEG images.
     */
    private static final String JPEG_CONTENT_TYPE = "image/jpeg";

    private int mMaxWidth = 800;

    private int mMaxWidthThumbnail = 150;

.....

    /**
     * This method takes in an image as a byte array (currently supports GIF, JPG, PNG and
     * possibly other formats) and
     * resizes it to have a width no greater than the pMaxWidth parameter in pixels.
     * It converts the image to a standard
     * quality JPG and returns the byte array of that JPG image.
     *
     * @param pImageData
     *                the image data.
     * @param pMaxWidth
     *                the max width in pixels, 0 means do not scale.
     * @return the resized JPG image.
     * @throws IOException
     *                 if the image could not be manipulated correctly.
     */
    public byte[] resizeImageAsJPG(byte[] pImageData, int pMaxWidth) throws IOException {
	InputStream imageInputStream = new ByteArrayInputStream(pImageData);
	// read in the original image from an input stream
	SeekableStream seekableImageStream = SeekableStream.wrapInputStream(imageInputStream, true);
	RenderedOp originalImage = JAI.create(JAI_STREAM_ACTION, seekableImageStream);
	((OpImage) originalImage.getRendering()).setTileCache(null);
	int origImageWidth = originalImage.getWidth();
	// now resize the image
	double scale = 1.0;
	if (pMaxWidth > 0 && origImageWidth > pMaxWidth) {
	    scale = (double) pMaxWidth / originalImage.getWidth();
	}
	ParameterBlock paramBlock = new ParameterBlock();
	paramBlock.addSource(originalImage); // The source image
	paramBlock.add(scale); // The xScale
	paramBlock.add(scale); // The yScale
	paramBlock.add(0.0); // The x translation
	paramBlock.add(0.0); // The y translation

	RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING,
		RenderingHints.VALUE_RENDER_QUALITY);

	RenderedOp resizedImage = JAI.create(JAI_SUBSAMPLE_AVERAGE_ACTION, paramBlock, qualityHints);

	// lastly, write the newly-resized image to an output stream, in a specific encoding
	ByteArrayOutputStream encoderOutputStream = new ByteArrayOutputStream();
	JAI.create(JAI_ENCODE_ACTION, resizedImage, encoderOutputStream, JAI_ENCODE_FORMAT_JPEG, null);
	// Export to Byte Array
	byte[] resizedImageByteArray = encoderOutputStream.toByteArray();
	return resizedImageByteArray;
    }

36 Responses to “How To Resize Uploaded Images Using Java – Better Way”

  1. Maeve says:

    Hi!
    Thanks for this code! I tried many different code-pieces, but yours was the only one which worked.
    The result looks good too. =)
    Greetings, Maeve

  2. Mike McP says:

    I’m trying to test out this improved version in an existing app I have, and I’m getting this error when I run it:

    Exception in thread “AgentThread: JavaAgent” java.lang.NoClassDefFoundError: com.sun.media.jai.codec.SeekableStream
    at com.lekkimworld.lotusphere2007.picture_importer.PictureImporter.resizeImageAsJPG(PictureImporter.java:111)
    at com.lekkimworld.lotusphere2007.picture_importer.PictureImporter.importPicture(PictureImporter.java:326)
    at JavaAgent.NotesMain(JavaAgent.java:19)
    at lotus.domino.AgentBase.runNotes(Unknown Source)
    at lotus.domino.NotesThread.run(Unknown Source)

    In Eclipse, I’ve referenced jai_codec.jar and jai_core.jar, and I verified that they exist where their paths point on my file system. Any ideas what the cause of this could be? I’m anxious to test the quality of this improved version over my homebrew code (very similar to Devon’s first posting on the topic).

    take care, Mike

  3. Devon says:

    Mike:

    it compiles in Eclipse I assume, and just throws this error during runtime? You should check the classpath for the running app and ensure the jai_codec.jar is in the classpath of the app (outside of eclipse).

    Just my initial thoughts…

  4. Mike McP says:

    Ahh, you’re completely correct…this was my dumb mistake! I’m running this app in Lotus Notes, and I forgot to move the jai JAR files over to my running app! Thanks Devon…works like a charm, and much better quality than my earlier code!

    take care,
    MIke

  5. Devon says:

    Mike: Glad to hear it!! I was very impressed with the image quality of this updated code. I’m glad it’s worked out for you as well.

  6. Matthias says:

    JPEG_CONTENT_TYPE should be just “JPG” instead of “image/jpeg”. The spec does not expect a MIME type, but one of:

    BMP
    JPEG
    PNG
    PNM
    TIFF
    See http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/Encode.doc.html#56610

    I noticed because using “image/png” instead threw an exception, but “png” worked. That is for JAI 1.1.3.

  7. Devon says:

    @Matthias: The encoder call is actually using the JAI_ENCODE_FORMAT_JPEG constant, which has a value of “JPEG”. The JPEG_CONTENT_TYPE constant isn’t used in that code snippet, but it used elsewhere in the class I copied the code from to set mime type headers into the database record for the file metadata.

  8. Newbie says:

    Iam new to JAI and i tried using the code snipped provided here to resize an image.I get the following exception:
    java.lang.RuntimeException: – Unable to render RenderedOp for this operation.
    Pls let me know wht could be the reason for the same

  9. Devon says:

    @Newbie: perhaps the image format isn’t supported by the codecs in JAI? What is the image format you are working with? Can you share the full stack trace of the error?

    Thanks!

  10. Andy Westley says:

    I just wanted to say that having tried some really horrible messy code to integrate apache commons fileupload and some awt-based image resizing code, I stumbled across your artice, and the code dropped into my project seamlessly. Brilliant.

  11. Kris says:

    I’ve found that when I need to scale a large image down to less than 50% of the original size, this method works GREAT! However, when (0.5d <= scale < 1.0d) this really didn’t produce very good images for me. I had to modify the code to resize using up to three different methods depending on the value of scale. See the code I’ve provided here:
    http://pastebin.com/f13fbbfb3

    I welcome any feedback!

  12. Adrian says:

    Devon,

    I like your work here. One question (and maybe I just missed reading up on it on your article(s)), any idea on what I can do to deal with images that have transparencies? GIF in particular.

    Thanks in advance,

    Adrian

  13. NewBie2JAI says:

    Hi Devon,

    I am another Newbie to JAI and I too get the same exception as Newbie. My stack trace as below:

    Exception in thread “main” java.lang.RuntimeException: – Unable to render RenderedOp for this operation.
    at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:826)
    at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:866)
    at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:887)
    at testfiles.ResizeImage.resizeImageAsJPG(ResizeImage.java:89)
    at testfiles.ResizeImage.main(ResizeImage.java:30)

    I call your method as below in main method of a standalone java class:

    try{
    File file = new File(“C:\\Documents and Settings\\All Users\\Documents\\My Pictures\\Sample Pictures\\Blue hills.jpg”);
    long length = file.length();
    byte[] bytes = new byte[(int)length];
    new ResizeImage().resizeImageAsJPG(bytes, 20);
    }catch (IOException e) {
    e.printStackTrace();
    }

    Please let me know where I am going wrong.

  14. NewBie2JAI says:

    Well on loading data into byte array as in the example link I get the following error:

    Error: Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode.
    Occurs in: com.sun.media.jai.mlib.MediaLibAccessor
    java.lang.NoClassDefFoundError: com/sun/medialib/mlib/Image
    at com.sun.media.jai.mlib.MediaLibAccessor$1.run(MediaLibAccessor.java:248)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.media.jai.mlib.MediaLibAccessor.setUseMlib(MediaLibAccessor.java:245)
    at com.sun.media.jai.mlib.MediaLibAccessor.useMlib(MediaLibAccessor.java:177)
    at com.sun.media.jai.mlib.MediaLibAccessor.isMediaLibCompatible(MediaLibAccessor.java:357)
    at com.sun.media.jai.mlib.MediaLibAccessor.isMediaLibCompatible(MediaLibAccessor.java:315)
    at com.sun.media.jai.mlib.MlibSubsampleAverageRIF.create(MlibSubsampleAverageRIF.java:56)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at javax.media.jai.FactoryCache.invoke(FactoryCache.java:122)
    at javax.media.jai.OperationRegistry.invokeFactory(OperationRegistry.java:1674)
    at javax.media.jai.ThreadSafeOperationRegistry.invokeFactory(ThreadSafeOperationRegistry.java:473)
    at javax.media.jai.registry.RIFRegistry.create(RIFRegistry.java:332)
    at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:818)
    at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:866)
    at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:887)
    at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:798)
    at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:866)
    at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:887)
    at javax.media.jai.JAI.createNS(JAI.java:1099)
    at javax.media.jai.JAI.create(JAI.java:973)
    at javax.media.jai.JAI.create(JAI.java:1668)
    at testfiles.ResizeImage.resizeImageAsJPG(ResizeImage.java:146)
    at testfiles.ResizeImage.main(ResizeImage.java:38)
    Caused by: java.lang.ClassNotFoundException: com.sun.medialib.mlib.Image
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClassInternal(Unknown Source)
    … 26 more

    Its breaking at this point in the resizeImageAsJPG which is line 146

    JAI.create(JAI_ENCODE_ACTION, resizedImage, encoderOutputStream, JAI_ENCODE_FORMAT_JPEG, null);

    Anything I missed?

  15. NewBie2JAI says:

    Hey Devon…. I got it working… found the missing library file “mlibwrapper_jai”.

  16. [...] Edit: please read the better way here: Better way to Resize Images Using Java! [...]

  17. NewBie2JAI says:

    Hi Devon,

    Is there a better way to crop images vertically to a specific height for specific widths if the height is beyond certain limit. For e.g.
    if the resized image’s width is 160 px and its height is beyond 200px than it should be cropped vertically upto 200 px in height. The cropping should be proportionate from top as well as bottom and retain the center portion of the resized image as is. Appreciate your help.

    • Devon says:

      NewBie2JAI: a quick googling for “JAI crop image” gave some good results. This code snippet looks promising, but I’d definitely google for it and read the docs on the JAI “crop” action.

      RenderedImage ri = JAI.create(“fileload”,pathandfilename);

      public void crop()
      {
      pb = new ParameterBlock();
      pb.addSource(ri);
      pb.add((float)topLeftmx);
      pb.add((float)topLeftmy);
      pb.add((float)roiWidth);
      pb.add((float)roiHeight);
      ri = JAI.create(“crop”,pb);
      }

  18. NewBie2JAI says:

    Thanks Devon. I too googled it out and impemented it but I am getting this error:”Crop The rectangular crop area must not be outside the image.” Working on it.. Thanks though.

  19. NewBie2JAI says:

    FYI..

    I am using “ParameterBlockJAI” and setting the parameters for minX, minY and width, height.

  20. Jean-Marie Tinghir says:

    I had very bad quality resized images, and it appeared that the solution was to specify the interpolation.
    The default is the InterpolationNearest, but the InterpolationBilinear or InterpolationBicubic give much better quality.
    But then there was an other problem with those interpolations : black borders sometimes appear after scale operation.
    So I had to scale it with 2 more pixels, and then crop it with x=1 y=1 and the size I want.

  21. shiva says:

    This code looks great. But i am facing one small (small??? or big???) issue. I am getting this exception. can you please show me the way on this

    java.lang.IllegalArgumentException: SubsampleAverage: No OperationDescriptor is registered in the current operation registry under this name.
    at javax.media.jai.JAI.createNS(Ljava/lang/String;Ljava/awt/image/renderable/ParameterBlock;Ljava/awt/RenderingHints;)Ljavax/media/jai/RenderedOp;(JAI.java:883)
    at javax.media.jai.JAI.create(Ljava/lang/String;Ljava/awt/image/renderable/ParameterBlock;Ljava/awt/RenderingHints;)Ljavax/media/jai/RenderedOp;(JAI.java:786)

  22. Benjamin says:

    I have this problem, when the code it’s executed in a machine with Linux, and if the file is a GIF

    java.lang.RuntimeException: – Unable to render RenderedOp for this operation.
    at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:827)
    at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
    at javax.media.jai.RenderedOp.getWidth(RenderedOp.java:2179)

    Any ideas?

  23. Nix4J says:

    Guys,

    Let me congratulate everyone on doing an extremely wonderful job here.

    I have been working on a TIFF document viewe/editor (applet+JAI) piece.

    It provides zoom and rotate functionality using the “scale” and “transpose” operators on JAI.create(). Now my applet is signed and I have JAI installed on the client machine (jai_imageio.jar, jai_core.jar, jai_codec.jar) in the JRE/lib/ext folder.

    The applets loads fine, downloading my applet JAR, and the TIFF document is retrieved from the server and displayed in the applet, within the browser. I use javascript buttons to call on the applet functions to zoom and rotate.

    ROTATE CODE
    —————————————
    ParameterBlock pb = new ParameterBlock();
    pb.addSource(image);
    pb.add(TransposeDescriptor.ROTATE_90); //assume I always rotate by 90 degrees
    RenderedOp rotatedImage = JAI.create(“transpose”, pb);

    ZOOM CODE:
    ——————————————
    ParameterBlock pb = new ParameterBlock();
    pb.addSource(image);
    pb.add((float)magFactor);
    pb.add((float)magFactor);
    pb.add(0.0F);
    pb.add(0.0F);
    RenderedImage scaledImage = JAI.create(“scale”, pb, null);

    ——————————————————-

    I am getting the following exceptions for Zoom and Rotate respectively

    ZOOM EXCEPTION
    ——————————————————-
    java.lang.reflect.InvocationTargetException

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at gov.wisconsin.viewer.framework.controller.AbstractControllerApplet.invokeService(AbstractControllerApplet.java:83)

    at gov.wisconsin.viewer.framework.controller.AbstractControllerApplet.perform(AbstractControllerApplet.java:106)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at sun.plugin.javascript.invoke.JSInvoke.invoke(Unknown Source)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at sun.plugin.javascript.JSClassLoader.invoke(Unknown Source)

    at sun.plugin.com.MethodDispatcher.invoke(Unknown Source)

    at sun.plugin.com.DispatchImpl.invokeImpl(Unknown Source)

    at sun.plugin.com.DispatchImpl$1.run(Unknown Source)

    at java.security.AccessController.doPrivileged(Native Method)

    at sun.plugin.com.DispatchImpl.invoke(Unknown Source)

    Caused by: java.lang.ExceptionInInitializerError

    at gov.wisconsin.viewer.management.TiffManager.magnifyAndShow(TiffManager.java:240)

    at gov.wisconsin.viewer.management.TiffManager.zoom(TiffManager.java:266)

    … 21 more

    Caused by: java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThreadGroup)

    at java.security.AccessControlContext.checkPermission(Unknown Source)

    at java.security.AccessController.checkPermission(Unknown Source)

    at java.lang.SecurityManager.checkPermission(Unknown Source)

    at sun.applet.AppletSecurity.checkAccess(Unknown Source)

    at java.lang.ThreadGroup.checkAccess(Unknown Source)

    at java.lang.ThreadGroup.(Unknown Source)

    at java.lang.ThreadGroup.(Unknown Source)

    at com.sun.media.jai.util.SunTileScheduler.(SunTileScheduler.java:689)

    at javax.media.jai.JAI.(JAI.java:560)

    … 23 more

    ROTATE EXCEPTION
    ——————————————————-

    java.lang.reflect.InvocationTargetException

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at gov.wisconsin.viewer.framework.controller.AbstractControllerApplet.invokeService(AbstractControllerApplet.java:83)

    at gov.wisconsin.viewer.framework.controller.AbstractControllerApplet.perform(AbstractControllerApplet.java:106)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at sun.plugin.javascript.invoke.JSInvoke.invoke(Unknown Source)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at sun.plugin.javascript.JSClassLoader.invoke(Unknown Source)

    at sun.plugin.com.MethodDispatcher.invoke(Unknown Source)

    at sun.plugin.com.DispatchImpl.invokeImpl(Unknown Source)

    at sun.plugin.com.DispatchImpl$1.run(Unknown Source)

    at java.security.AccessController.doPrivileged(Native Method)

    at sun.plugin.com.DispatchImpl.invoke(Unknown Source)

    Caused by: java.lang.ExceptionInInitializerError

    at gov.wisconsin.viewer.management.TiffManager.rotate(TiffManager.java:337)

    … 21 more

    Caused by: java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThreadGroup)

    at java.security.AccessControlContext.checkPermission(Unknown Source)

    at java.security.AccessController.checkPermission(Unknown Source)

    at java.lang.SecurityManager.checkPermission(Unknown Source)

    at sun.applet.AppletSecurity.checkAccess(Unknown Source)

    at java.lang.ThreadGroup.checkAccess(Unknown Source)

    at java.lang.ThreadGroup.(Unknown Source)

    at java.lang.ThreadGroup.(Unknown Source)

    at com.sun.media.jai.util.SunTileScheduler.(SunTileScheduler.java:689)

    at javax.media.jai.JAI.(JAI.java:560)

    … 22 more

    ———————————————————

    Both exceptions trace down to the line where I use JAI.create() method for scale or rotate. I thought by signing the jar all access exceptions would be resolved. But it seems I am still getting “Caused by: java.security.AccessControlException: access denied” exceptions.

    Now I am not sure what am I doing wrong. Is there anything special that I need to do within applet in order to get the JAI.create() method to work?

    Any feedback, pointers, help would be very appreciated !

    Regards,
    Nix4J

  24. Rabbit says:

    Hi, I have followed your method to do my image scaling & resizing. It works well for jpegs and some png files, but I am having a problem with transparent gifs and some png files. They come out pink in color. What could be the reason? (I have used your code as given above).

Leave a Reply