10

Apologies for any ignorance, but I have never worked with jpeg images (let alone any types of images) in Java before.

Supposing I want to send a jpeg image from a web service to a client. Is there any way that I can reduce the jpeg file size by manipulating the colour profile of the image in some way?

I have already been able to reduce the image size by scaling it using a neat tool for BufferedImages called imgscalr. See here.

I would also like a jpeg that has less colours than a high quality jpeg image. For example, I would like to be able to use 8bit colour in my jpeg instead of say 16bit colour.

What exactly would I need to change if I have a BufferedImage from Java's 2D package?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Joeblackdev
  • 7,217
  • 24
  • 69
  • 106

4 Answers4

5

Another way to reduce image size is to change compression level. You can do that using ImageWriter.

    ImageWriter writer = null;
    Iterator<ImageWriter> iwi = ImageIO.getImageWritersByFormatName("jpg");
    if (!iwi.hasNext())
        return;
    writer = (ImageWriter) iwi.next();
    ImageWriteParam iwp = writer.getDefaultWriteParam();
    iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
    iwp.setCompressionQuality(compressionQuality);
    writer.setOutput(...);
    writer.write(null, image, iwp);
Lev Khomich
  • 2,247
  • 14
  • 18
  • Hi, What impact (visually) will changing the compression of the jpeg have? Thanks – Joeblackdev Jul 26 '11 at 13:39
  • 1
    Take a look at this post http://www.codinghorror.com/blog/2006/07/a-comparison-of-jpeg-compression-levels-and-recompression.html – Lev Khomich Jul 26 '11 at 13:41
  • I'll take a closer look, thanks for the link. I was wondering however if there is a way (using the images in that link) to say use less colours in the jpeg? For example, to use 8bit colour instead of 16bit colour? Thanks – Joeblackdev Jul 26 '11 at 13:45
  • 3
    @Joeblackdev: This is the answer. JPEGs work only with full color palette. If You want to use another format (png or gif) than You'll have the choice to shorten the palette. – Rekin Jul 26 '11 at 13:50
  • @Rekin do you have an example anywhere of shortening the palette with PNG images? – Joeblackdev Jul 26 '11 at 14:16
  • @Joeblackdev: Take a look at pictures in http://en.wikipedia.org/wiki/Dither and http://en.wikipedia.org/wiki/List_of_monochrome_and_RGB_palettes. The best is to load up Photoshop or Gimp and try save for web feature or something similar. It gives you interactive dialog where You can set options (including palette size) and view results immediately. I'm pretty sure You'll end up using jpegs and compression ratio instead of palette reduction (if gradients are dominating over flat tone regions, that is always the case with real world photos). – Rekin Jul 26 '11 at 14:49
  • Dithering is an option when *de*compressing a JPEG image. However, in the context of the JPEG compression method itself, reducing the color palette doesn't make any sense, as JPEG uses a fundamentally analog method. You can save some space by going black&white, though. – comingstorm Jul 28 '11 at 23:05
1

The easiest way to do this is to decompress the byte stream into a Java Image, optionally resize it (which makes it smaller) and then regenerate a JPEG image from this with the desired quality setting.

This new image is then what is sent to the client.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
  • Thanks for this, but this is exactly what I have so far. I use a tool called imgscalr to resize the jpeg image before it is sent to the client. However, I would also like to update the colour profile of the jpeg to reduce the colours in the jpeg. By "reduce" I mean use less colours. I'm not sure the terminology of this... – Joeblackdev Jul 26 '11 at 13:34
1

Have a look at the ImageIO class. As for reducing file size: since the image would already be a JPEG the only things you could do is reduce the quality or the image size.

Another thing to keep in mind: if the image is a CMYK jpeg it might be bigger. Unfortunately ImageIO can't handle those, but you can try JAI ImageIO to convert from CMYK to RGB (which should be much smaller).

Thomas
  • 87,414
  • 12
  • 119
  • 157
1

Two of the possible solutions are downscaling the image, here's how you'd do it:

BufferedImage original = //your image here
scaled = original.getScaledInstance(finalWidth, finalHeight, Image.SCALE_SMOOTH); // scale the image to a smaller one

BufferedImage result = new BufferedImage(finalWidth, finalHeight, original.getType());
Graphics2D g = result.createGraphics();     

g.drawImage(scaled, 0, 0, null); //draw the smaller image
g.dispose();

Obviously, you have to calculate the scaled width and height so the image stays by the same aspect ratio.

Once you have drawn it smaller, you can now turn this image into a JPEG file:

BufferedImage image = // this is the final scaled down image

JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(output);
JPEGEncodeParam jpegEncodeParam = jpegEncoder.getDefaultJPEGEncodeParam(image);

jpegEncodeParam.setDensityUnit(JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);
jpegEncodeParam.setXDensity(92);
jpegEncodeParam.setYDensity(92);

jpegEncodeParam.setQuality( 0.8F , false);
jpegEncoder.encode(image, jpegEncodeParam);

These classes are from the JAI package (more exactly com.sun.image.codec.jpeg) and the JVM might complain that they should not be used directly, but you can ignore that.

You can possibly download JAI from here, if it does not work I have github mirrors setup for the two libraries, JAI core and JAI ImageIO.

Maurício Linhares
  • 39,901
  • 14
  • 121
  • 158