Edit: please read the better way here: Better way to Resize Images Using Java!
———————–
I am building a Seam application which need to support users uploading images, and then the site displaying them. I wanted to resize big images into something reasonable (say 1024 or 800 px wide) and generate a thumbnail, and I also wanted to standardize on a single image format just to make things easy and consistent.
So I needed method that would take an uploaded image as a byte array (byte
[]) and would resize the image (if needed) and convert it to a fixed quality JPG, and give me back a byte array to store in the database. I am using Seam, so I get a handy byte array, but this method should work fine for non-Seam applications as well.
/**
* 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 iamge could not be manipulated correctly.
*/
public byte[] resizeImageAsJPG(byte[] pImageData, int pMaxWidth) throws IOException {
// Create an ImageIcon from the image data
ImageIcon imageIcon = new ImageIcon(pImageData);
int width = imageIcon.getIconWidth();
int height = imageIcon.getIconHeight();
mLog.info("imageIcon width: #0 height: #1", width, height);
// If the image is larger than the max width, we need to resize it
if (pMaxWidth > 0 && width > pMaxWidth) {
// Determine the shrink ratio
double ratio = (double) pMaxWidth / imageIcon.getIconWidth();
mLog.info("resize ratio: #0", ratio);
height = (int) (imageIcon.getIconHeight() * ratio);
width = pMaxWidth;
mLog.info("imageIcon post scale width: #0 height: #1", width, height);
}
// Create a new empty image buffer to "draw" the resized image into
BufferedImage bufferedResizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Create a Graphics object to do the "drawing"
Graphics2D g2d = bufferedResizedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// Draw the resized image
g2d.drawImage(imageIcon.getImage(), 0, 0, width, height, null);
g2d.dispose();
// Now our buffered image is ready
// Encode it as a JPEG
ByteArrayOutputStream encoderOutputStream = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(encoderOutputStream);
encoder.encode(bufferedResizedImage);
byte[] resizedImageByteArray = encoderOutputStream.toByteArray();
return resizedImageByteArray;
}
In my application I call this method twice, once to convert the uploaded image into a limited size JPG, and then once again to generate a much smaller thumbnail. I store both of these in the database and will use caching at the Apache layer to ensure performance.
I have gotten better quality in the past when creating the BufferedImage with the following logic to perform the resize:
Thank Matt, I will give that a try!
— edit:
How do you convert an incoming ImageIcon or Image to a RenderedImage needed for the SubsampleAverageDescriptor.create() call? I can see how to do it if I go through the process of painting it full size into a another BufferedImage, but that seems like it would make this call at least 2X as computationally expensive..
Or did you mean just use the rendering hints you have there, and leave the rest of the code alone? Does the SubsampleAverageDescriptor provide superior resizing?
Sorry about the not including anything about how to get a RenderedImage as I am not using an ImageIcon. A BufferedImage implements RenderedImage. You can creaete a BufferedImage from a byte[] by creating a ByteArrayInputStream and passing it to ImageIO.read().
This post shows visual results of resizing with and without using SubsampleAverage: https://www.i-proving.ca/space/Technologies/Java+Advanced+Imaging
I was experiencing similar results where edges were not smooth.
[…] How To Resize Uploaded Images Using Java […]
http://www.comesolvego.com/2008/04/29/resize-images-with-java-high-quality-and-working-solution/
I was wondering if anyone else was getting weird behavior doing this sort of thing on Mac OSX. I am using the code above to create a thumbnail and when the code is executed the org.apache.catalina.startup.Bootstrap process shows up on the menu bar at the top of my screen. This occurs on both Tiger and Leapord. On Tiger you can shutdown the machine and the machine stops the apache process and shuts down. On Leapord the machine does not shut down until the user chooses to quit org.apache.catalina.startup.Bootstrap manually. I really need to be able to size images but I can not expect my user base to manually quit org.apache.catalina.startup.Bootstrap before shutting down their machine.
Has anyone else seen this behavior? By the way, it works fine on Windows.
Thanks in advance,
Doug
@Doug:
my production system is Linux, so it isn’t a big deal for me. I have noticed that on my dev box (OS X Leopard), JBoss will create a JBoss Main process with a menu, which sounds similar. When I shutdown JBoss though, that process also dies. I haven’t looked into it much however.
If you find out how to prevent it, I’d love to know.
Thanks!
Devon
On the upside at least I am not alone. On the down side…no solution…yet…..
Hi Devon, I was wondering if you could perhaps help me on this problem. I have tried two different ways of sizing the image and have tracked it down to the following lines of code (from 2 different methods).
First Way : ImageIcon imageIcon = new ImageIcon( pImageData ); //Where pImageData is the byte[]. When this line of code is executed the org.apache.catalina.startup.Bootstrap pops up on the upper right.
Second way : Image image = Toolkit.getDefaultToolkit().createImage( pImageData );//Where pImageData is the byte[].
When this line of code is executed the org.apache.catalina.startup.Bootstrap pops up on the upper right.
Do you know of other ways of accomplishing this? The first example is right from this post. Any help would be GREATLY APPRECIATED!!!! I am at the point where I will try anything just to solve the problem.
Thanks,
Doug
@Doug:
Try adding this to your start script:
export JAVA_OPTS=”-Djava.awt.headless=true”
Let me know if that helps.
It WORKS! You rock Devon!
Thank you very much for your help I appreciate it.
@Doug:
No problem! I’m glad it worked!
One last question please! What is the proper sytax for the following…it doesn’t seem to work when I want to put 2 parms on the JAVA_OPTS.
I was setting just the memory and now I need the headless part as well. Can you tell me what is wrong with my JAVA_OPTS below?
# JAVA_OPTS=”-Xms128M -Xmx512M”
JAVA_OPTS=”-Djava.awt.headless=true;-Xms128M -Xmx512M”
Thanks Again,
Doug
@Doug:
Just use spaces:
JAVA_OPTS=”-Djava.awt.headless=true -Xms128M -Xmx512M”
Thanks!
it works! i tried it and voila
tks
Thanks for this code. I’m a newbie in Java. Do you can share with us the final code and libraries initialization? I’m wanting to compile this and don’t know how to begin. Cheers.
@Marco:
please look at this post:
http://172.31.20.105/tech-blog/java/how-to-resize-uploaded-images-using-java-better-way.html
It generates better quality images by far.
The imports for that code are:
Thanks Devon. I’ll try. One question: is possible to resize TIFF images using this code? What do you think about?
@Marco: TIFF is a supported format so it should work just fine. Although i haven’t tested it myself.
int width = imageIcon.getIconWidth();
int height = imageIcon.getIconHeight();
both height and width return ‘-1’ for TIFF format.
means TIFF not supported ?
@Fui: please try the code here: http://172.31.20.105/tech-blog/java/how-to-resize-uploaded-images-using-java-better-way.html
It’s better quality and may fix your issue as well.
Thanks ! Worked great for me too.
Hi Devon.i am uploading 5 images using<rich:upload up to that every thing working fine.after saving that images. i want to display uploaded image as <rich:modelpanel pop up.i have no idea that how uploded image will come into modelpanel.please help me
Venu,
well I assume you’re able to serve up the images based on an id or name? Either from a Servlet pulling from your DB, or directly from the FS or wherever you put the images after upload. If you can do that, you just add the reference to the image in the modal panel.
You define your modal:
<rich:modalPanel id="pictureModal" autosized="true" width="750" zindex="2000">
And add an image tag for the image:
<img src="#{facesContext.externalContext.requestContextPath}/image/#{selectedImage.image.id}.jpg" />
where selectedImage is your backing bean pointing to the image you want to display.
If you want this to work without a page reload, you need to put the img src inside a ajax re-renderable block. If you’re using seam you can use s:div, otherwise an a4j tag should work