High-Quality Image Resize with Java
Image resize with java isn't a new topic. What I want to explain here is a technique to create small high-quality images using the Java2D API, since I couldn't find any reasonable solution on the Internet. The are some solutions available - like this one - but the final quality is low.
You can use this approach for different purposes, but the best example I can think of is the implementation of small avatars on web applications (see Nabble, DZone, etc.) or desktop software (e.g., Skype, MSN, etc.).
In order to show the contribution of this technique, I have to explain image resize from the beginning. We will improve the algorithm as we go and I also assume you have some background with Java2D.
Reading and Writing the Image
Since this is a simple example, I will read an image from the file system, resize it and save it back. The image I am using is below. Note that I am using a JPG file, which is the common format for pictures.

To read this image, we can use the ImageIO class:
First Attempt
Now we have to find a way to resize this image. Our first attempt is to use the drawImage() method of the Graphics interface:
g.drawImage(image, 0, 0, width, height, null);
g.dispose();
return resizedImage;
With this method, we can create a 24x24 avatar by calling:
The last step is to save the image so that we can see the results. The ImageIO class can do this job:
Note that the second parameter is the format of the saved image. I used "png" because it is simpler. If you want to save it as JPG, you will have to use the JPEGImageEncoder class (and the result is the same, trust me).
The result of this method is a low-quality image:

Pay attention to the details of the image (look closer). There is no anti-alias and the face is distorted. Would you add such algorithm to your web application? I don't think so. Users could get scared by looking at those images.
Second Attempt
We noticed that the main problem of the resize() method above is the fact that it doesn't have anti-aliasing. So we could use the Rendering Hints of the Java2D API as an attempt to solve that problem:
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
g.setRenderingHint(RenderingHints.KEY_RENDERING,
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
g.drawImage(image, 0, 0, width, height, null);
g.dispose();
return resizedImage;
The result is below (right image). If you pay attention to the details, it is somewhat better, but still a crap:


The Trick
Now I will explain how to improve the quality of this image. Basically, the trick is to blur the image before resizing. The code that blurs an image in java is:
float[] blurKernel = {
ninth, ninth, ninth,
ninth, ninth, ninth
Map
map.put(RenderingHints.KEY_INTERPOLATION,
map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints hints = new RenderingHints(map);
BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, hints);
return op.filter(image, null);
Since the original image is in the JPG format, we can't blur it directly. We have to create a compatible image first:
int w = image.getWidth();
int h = image.getHeight();
BufferedImage result = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
Graphics2D g2 = result.createGraphics();
g2.drawRenderedImage(image, null);
g2.dispose();
return result;
Blurring the image before resizing it isn't the only trick. You have to do this when the width or height of the image is 100px. Since small avatars are usually a square (width = height), you can resize it to 100x100, call the blur() method and resize again to 24x24:
image = resize(image, 100, 100);
image = blurImage(image);
image = resize(image, width, height);
return image;
(the resize() method is the one described in the second attempt)
Now you can compare the three results:



You have to look closer, but the last result is much better than the others. Now you can let users upload avatars on your java applications!
Conclusion
This was my contribution to the tough problem of shrinking images using the Java2D API. I would like to remember that this isn't the ultimate algorithm to achieve that goal, but it is good enough considering its simplicity. If you have comments or feedback, I would like to hear them.
Comments
First |
Previous |
1 | 2 | 3 | Next |
Last |
private Image scaleImage(Image p_image, int p_width, int p_height) {
Image final_image = null;
if (p_image != null || p_width > 0 || p_height > 0) {
try {
Image image = p_image;
final_image = null;
int thumbWidth = p_width;
int thumbHeight = p_height;
// Make sure the aspect ratio is maintained, so the image is not
// skewed
double thumbRatio = (double) thumbWidth / (double) thumbHeight;
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
double imageRatio = (double) imageWidth / (double) imageHeight;
if (thumbRatio < imageRatio) {
thumbHeight = (int) (thumbWidth / imageRatio);
} else {
thumbWidth = (int) (thumbHeight * imageRatio);
}
BufferedImage thumbImage = new BufferedImage(thumbWidth,
thumbHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = thumbImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
g.dispose();
final_image = (Image) thumbImage;
} catch (Exception e) {
}
}
return final_image;
}
it has really helped me a lot..
public void showImage(String filePath,HttpServletResponse response){
ImageOutputStream imageOut = null;
BufferedImage image = null;
try {
//in = new FileInputStream(filePath);
image = ImageIO.read(new File(filePath));
imageOut = ImageIO.createImageOutputStream(response.getOutputStream());
if (imageOut != null){
if (filePath.endsWith(".jpg") || filePath.endsWith(".JPG")){
ImageIO.write(image,"jpg",imageOut);
} else if (filePath.endsWith(".bmp") || filePath.endsWith(".BMP")){
ImageIO.write(image,"bmp",imageOut);
} else {//tiff
ImageIO.write(image,"tiff",imageOut);
}
}
} catch (FileNotFoundException ffe) {
logger.error("FileNotFoundException when trying to create instream showImage() " + ffe);
} catch (IOException ioe) {
logger.error("IOException when trying to create instream showImage()) " + ioe);
} catch (Exception e){
logger.error("Exception when trying to create instream showImage()) " + e);
} finally{
try {
if(image != null){
image.flush();
}
if(imageOut != null){
imageOut.close();
}
} catch (Exception e) {
logger.error("Exception thrown in finally block " + e);
}
}
}
Any help will be appreciated. Thanks
public class BlurImage {
public image blur_image(image i, int radius) {
int count3=0;
int redarray[][] = new int[i.rows][i.columns];
int greenarray[][] = new int[i.rows][i.columns];
int bluearray[][] = new int[i.rows][i.columns];
int l=0,m=0,p=0,q=0;
float sum1 = 0, sum2 = 0, sum3 = 0;
int red[][] = new int[i.rows][i.columns];
int green[][] = new int[i.rows][i.columns];
int blue[][] = new int[i.rows][i.columns];
if(i.rows>> 16 ) & 0xFF;
System.out.println("red"+red[j][k]);
green[j][k] = ( i.data[j][k] >>> 8 ) & 0xFF;
System.out.println("green"+green[j][k]);
blue[j][k] = i.data[j][k] & 0xFF;
System.out.println("blue"+blue[j][k]);
}
}
for(int j=0;j=i.rows)
l = i.rows-1;
if(m>=i.columns)
m = i.columns-1;
p = j-radius;
q = k-radius;
if(p<0)
p=0;
if(q<0)
q=0;
for(int a=p;a<=l;a++)
{
for(int b=q;b<=m;b++)
{
++count3;
}
}
for(int y=p;y<=l;y++)
{
for(int z=q;z<=m;z++)
{
sum1 = sum1 + red[y][z];
sum2 = sum2 + green[y][z];
sum3 = sum3 + blue[y][z];
}
}
sum1 = sum1/count3;
System.out.println("sum1 "+sum1);
sum2 = sum2/count3;
System.out.println("sum2 "+sum2);
sum3 = sum3/count3;
System.out.println("sum3 "+sum3);
redarray[j][k] = (int)sum1;
greenarray[j][k] = (int)sum2;
bluearray[j][k] = (int)sum3;
System.out.println("red array = "+redarray[j][k]);
System.out.println("green array = "+greenarray[j][k]);
System.out.println("blue array = "+bluearray[j][k]);
count3 = 0;
sum1 = 0;
sum2 = 0;
sum3 = 0;
}
}
for(int j=0;j
for(int k=0;k
red[j][k] = redarray[j][k];
green[j][k] = greenarray[j][k];
blue[j][k] = bluearray[j][k];
System.out.println( red[j][k]);
System.out.println( green[j][k]);
System.out.println( blue[j][k]);
Color c = new Color(red[j][k],green[j][k],blue[j][k]);
String s = Integer.toHexString( c.getRGB() & 0x00ffffff );
System.out.println(s);
}
}
return i;
}
public static void main(String[] args) {
//TestCase 1
try {
image i = new image();
i.rows = 5;
i.columns = 3;
i.data = new int[][]{{6, 12, 18}, {5, 11, 17}, {4, 10, 16}, {3, 9, 15}, {2, 8, 14}};
BlurImage obj = new BlurImage();
image res = obj.blur_image(i, 2);
System.out.println("TestCase 1");
if (res != null) {
for (int k = 0; k < i.rows; k++) {
System.out.println();
for (int j = 0; j < i.columns; j++) {
System.out.print(res.data[k][j] + ",");
}
}
} else
System.out.println("Null");
}
catch (Exception e) {
e.printStackTrace();
}
//TestCase 2
try {
image i = new image();
i.rows = 3;
i.columns = 5;
i.data = new int[][]{{0x5a0060, 0x6a0050, 0x6a0050, 0x6a0050, 0x7f0038},
{0x5a0060, 0x6a0050, 0x6a0050, 0x6a0050, 0x7f0038},
{0x5a0060, 0x6a0050, 0x6a0050, 0x6a0050, 0x7f0038}};
BlurImage obj = new BlurImage();
image res = obj.blur_image(i, 1);
System.out.println(" TestCase 2");
if (res != null) {
for (int k = 0; k < i.rows; k++) {
System.out.println();
for (int j = 0; j < i.columns; j++) {
System.out.print("0x"+res.data[k][j] + ",");
}
}
} else
System.out.println("NULL");
}
catch (Exception e) {
e.printStackTrace();
}
//TestCase 3
try {
image i = new image();
i.rows = 3;
i.columns = 5;
i.data = new int[][]{{0x5a0060, 0x6a0050, 0x6a0050, 0x6a0050, 0x7f0038},
{0x5a0060, 0x6a0050, 0x6a0050, 0x6a0050, 0x7f0038},
{0x5a0060, 0x6a0050, 0x6a0050, 0x6a0050, 0x7f0038}};
BlurImage obj = new BlurImage();
image res = obj.blur_image(i, 4);
System.out.println(" TestCase 3");
if (res != null) {
for (int k = 0; k < i.rows; k++) {
System.out.println();
for (int j = 0; j < i.columns; j++) {
System.out.print(res.data[k][j] + ",");
}
}
} else
System.out.println("NULL");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
please help in this code to get correct output for testcases as in the same procedure given in the code with the image class.
It could be easily understand : With a Gaussian kernel, you don't give the same height to all kernel pixel, then you obtain a result more natural. To obtain more scientist informations => PSF (Point Spread Function) Gaussian
For my purposes I needed down-sized square images, but I still think you might find my algorithm good. It's a simple matter of down-sampling with 2x anti-aliasing.
Aside from simple array operations, only one buffered image is created with a couple of calls to it. No Graphics2D objects or anything like that. Works really fast on my old laptop.
/*************************************/
int size = 24;
String location = "hugo.jpg";
File file = new File(location);
BufferedImage img = ImageIO.read(file);
width = img.getWidth();
height = img.getHeight();
data = new int[width * height];
img.getRGB(0,0, width, height, data, 0, width);
int[] resized = new int[size * size];
float ratio = (width > height) ? (float) height / size : (float) width / size;
float hr = ratio / 2;
int i, j, k, l, m;
for (int x = 0; x > 2) & m;
}
}
}
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
img.setRGB(0, 0, size, size, resized, 0, size);
ImageIO.write(img, "jpg", file);
int size = 24;
String location = "hugo.jpg";
File file = new File(location);
BufferedImage img = ImageIO.read(file);
width = img.getWidth();
height = img.getHeight();
data = new int[width * height];
img.getRGB(0,0, width, height, data, 0, width);
int[] resized = new int[size * size];
float ratio = (width > height) ? (float) height / size : (float) width / size;
float hr = ratio / 2;
int i, j, k, l, m;
for (int x = 0; x > 2) & m;
}
}
}
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
img.setRGB(0, 0, size, size, resized, 0, size);
ImageIO.write(img, "jpg", file);
This comment box doesn't know how to handle LESS THAN sign...
int size = 24;
String location = "hugo.jpg";
File file = new File(location);
BufferedImage img = ImageIO.read(file);
width = img.getWidth();
height = img.getHeight();
data = new int[width * height];
img.getRGB(0,0, width, height, data, 0, width);
int[] resized = new int[size * size];
float ratio = (width > height) ? (float) height / size : (float) width / size;
float hr = ratio / 2;
int i, j, k, l, m;
for (int x = 0; x < size; x ++) {
for (int y = 0; y < size; y ++) {
i = (int) (x * ratio);
j = (int) (y * ratio);
k = (int) (i + hr);
l = (int) (j + hr);
for (int p = 0; p < 3; p ++) {
m = 0xFF << (p * 8);
resized[x + y * size] |= (
(data[i + j * width] & m) +
(data[k + j * width] & m) +
(data[i + l * width] & m) +
(data[k + l * width] & m) >> 2) & m;
}
}
}
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
img.setRGB(0, 0, size, size, resized, 0, size);
ImageIO.write(img, "jpg", file);
First |
Previous |
1 | 2 | 3 | Next |
Last |

Stumble it!
Dzone.com
Digg.com
Reddit.com
Del.icio.us
First
Previous
