12

I have an Image of say dimensions 800x800 which has a size of 170 kb. I want to resize this image to say 600x600. After resizing i want the image size to be reduced. How can i do this?

Madeyedexter
  • 1,155
  • 2
  • 17
  • 33
  • You could take a look at http://stackoverflow.com/questions/11959758/java-maintaining-aspect-ratio-of-jpanel-background-image/11959928#11959928 – MadProgrammer Oct 14 '12 at 06:33
  • 1) *"After resizing i want the image size to be reduced."* When held in memory, or serialized to PNG, JPG or GIF? The 1st will result in a reduction in bytes, the 2nd often won't. 2) What format is the image? (PNG, GIF, JPEG..) – Andrew Thompson Oct 14 '12 at 06:53
  • If the format is JPEG, it will most likely be more effective to reduce image byte size on disk by increasing the compression ratio. See [`ImageCompressionDemo`](http://stackoverflow.com/a/5998015/418556) for example source. – Andrew Thompson Oct 14 '12 at 07:01

3 Answers3

22

You don't need a full blown Image processing library for simply resizing an image.

The recommended approach is by using progressive bilinear scaling, like so (feel free to use this method as is in your code):

public BufferedImage scale(BufferedImage img, int targetWidth, int targetHeight) {

    int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = img;
    BufferedImage scratchImage = null;
    Graphics2D g2 = null;

    int w = img.getWidth();
    int h = img.getHeight();

    int prevW = w;
    int prevH = h;

    do {
        if (w > targetWidth) {
            w /= 2;
            w = (w < targetWidth) ? targetWidth : w;
        }

        if (h > targetHeight) {
            h /= 2;
            h = (h < targetHeight) ? targetHeight : h;
        }

        if (scratchImage == null) {
            scratchImage = new BufferedImage(w, h, type);
            g2 = scratchImage.createGraphics();
        }

        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);

        prevW = w;
        prevH = h;
        ret = scratchImage;
    } while (w != targetWidth || h != targetHeight);

    if (g2 != null) {
        g2.dispose();
    }

    if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
        scratchImage = new BufferedImage(targetWidth, targetHeight, type);
        g2 = scratchImage.createGraphics();
        g2.drawImage(ret, 0, 0, null);
        g2.dispose();
        ret = scratchImage;
    }

    return ret;

}

Code modified and cleaned from the original at Filthy Rich Clients.


Based on your comment, you can reduce quality and encode JPEG bytes like so:

image is the BufferedImage.

ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageWriter writer = (ImageWriter) ImageIO.getImageWritersByFormatName("jpeg").next();

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.2f); // Change this, float between 0.0 and 1.0

writer.setOutput(ImageIO.createImageOutputStream(os));
writer.write(null, new IIOImage(image, null, null), param);
writer.dispose();

Now, since os is a ByteArrayOutputStream, you can Base64 encode it.

String base64 = Base64.encode(os.toByteArray());
LanguagesNamedAfterCofee
  • 5,782
  • 7
  • 45
  • 72
7

You could use the 100% Java, Apache2 open source imgscalr library (a single static class) and do something like this:

import org.imgscalr.Scalr.*; // static imports are awesome with the lib

... some class code ...

// This is a super-contrived method name, I just wanted to incorporate
// the details from your question accurately.
public static void resizeImageTo600x600(BufferedImage image) {
    ImageIO.write(resize(image, 600), "JPG", new File("/path/to/file.jpg"));
}

NOTE: If the above looks odd, the static import allows me to use the resize call directly without specifying Scalr.resize(...)

Additionally, if the quality of the scaled image written out doesn't look good enough (it will be written out quickly though) you can use more arguments to the resize method like so:

public static void resizeImageTo600x600(BufferedImage image) {
    ImageIO.write(resize(image, Method.ULTRA_QUALITY, 600), "JPG", new File("/path/to/file.jpg"));
}

.. and you can even apply a BufferedImageOp to the result to soften it incase the downscaling makes the image look to jaggy:

public static void resizeImageTo600x600(BufferedImage image) {
    ImageIO.write(resize(image, Method.ULTRA_QUALITY, 600, Scalr.OP_ANTIALIAS), "JPG", new File("/path/to/file.jpg"));
}

You can start playing around with the library by just adding the following dep entry in your Maven POM (imgscalr is in the Maven central repo):

<dependency> 
  <groupId>org.imgscalr</groupId>
  <artifactId>imgscalr-lib</artifactId>
  <version>4.2</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>
Riyad Kalla
  • 10,604
  • 7
  • 53
  • 56
3

Use some good framework which supports Image Processing through Java e.g. IamageJ

There is also some basic support available in Java through some classes such as MemoryImageSource and PixelGrabber.

Yogendra Singh
  • 33,927
  • 6
  • 63
  • 73