Showing posts with label compression. Show all posts
Showing posts with label compression. Show all posts

Wednesday, 29 June 2011

Image Compression using Java JAI API

This post explains how to do the gif, jpg, bmp, png image compression using JAI APIs.  We are not using image compression software but using open source Java Api’s to achieve compression.
The preferred method for reading an image file of any format into a RenderedImage is:

String filename = “// path and name of the file to be read,
that is on an accessible filesystem //”;
RenderedImage image = JAI.create(“fileload”, filename);
or:
URL url = “// URL of the remote image to be read //”;
RenderedImage image = JAI.create(“url”, url);
and for writing a RenderedImage to an image file in a format whose encoder is supported by an ancillary codec, using the default encoding algorithm, is:
RenderedImage image = “// the image to be stored //”;
String filename = “// path and name of the file to be writen //”;
String format = “// the format of the file //”;
RenderedOp op = JAI.create(“filestore”, image,filename, format);



The Java Image I/O API
Due to the many requests for a comprehensive image I/O package the Java Image I/O API was developed. The Java Image I/O API is part of the JavaTM 2 Platform, Standard Edition, version 1.4 (J2SE1.4).
The Future of Image I/O in JAI
A package set called JAI-Image I/O Tools has been released and is available via the JAI home page. The package set includes image reader and writer plug-ins for the Java Image I/O API for numerous formats, image streams which use the Java New I/O API, and JAI operations for reading and writing images using the Java Image I/O API.
In a future JAI release, the image I/O-related operators in JAI-Image I/O Tools will be propagated to JAI. It has not been definitively determined as yet, but it is likely that when the new I/O operators have been added to JAI the old operations will be deprecated.
The classes currently in the com.sun.media.jai.codec and com.sun.media.jai.codecimpl packages will most likely be removed concurrent with a JAI release subsequent to that in which the Java Image I/O API-based operators become available. However, Sun is making publicly available the source code  of the com.sun.media.jai.codec and com.sun.media.jai.codecimpl classes so that developers who have written code based on them will still be able to use them. Please note that no technical support may be provided for these classes once they have been superseded by the Java Image I/O API.Following code does the compression of the image.
Here is the full code listing:

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import javax.media.jai.operator.FileLoadDescriptor;

import org.apache.commons.transaction.util.FileHelper;

import com.sun.media.jai.codec.SeekableStream;


// Function to do the compression
// jpg, gif, bmp, png file formats are accepted for the formatting.
private void compressFile(String realPath, File in, String fileName) {

Image img;
BufferedImage input = null;

try {

if (fileName.endsWith(".jpg") || fileName.endsWith(".JPG")) {
RenderedImage img1 = (RenderedImage)
JAI.create("fileload", in.getAbsolutePath());

input = getBufferedImage(fromRenderedToBuffered(img1));
} else if (fileName.endsWith(".gif") || fileName.endsWith(".GIF")) {

RenderedOp img1 = FileLoadDescriptor.create(in
.getAbsolutePath(), null, null, null);

input = getBufferedImage(img1.getAsBufferedImage());

} else if (fileName.endsWith(".bmp") || fileName.endsWith(".BMP")) {

// Wrap the InputStream in a SeekableStream.
InputStream is;
try {

is = new FileInputStream(in);
SeekableStream s = SeekableStream.wrapInputStream(is, false);

// Create the ParameterBlock and add the SeekableStream to it.
ParameterBlock pb = new ParameterBlock();
pb.add(s);

// Perform the BMP operation
RenderedOp img1 = JAI.create("BMP", pb);

input = getBufferedImage(img1.getAsBufferedImage());

is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else if (fileName.endsWith(".png") || fileName.endsWith(".PNG")) {

// Wrap the InputStream in a SeekableStream.
InputStream is;
try {
is = new FileInputStream(in);
SeekableStream s = SeekableStream.wrapInputStream(is, false);

// Create the ParameterBlock and add the SeekableStream to it.
ParameterBlock pb = new ParameterBlock(); pb.add(s);

// Perform the PNG operation
RenderedOp img1 = JAI.create("PNG", pb);

input = getBufferedImage(img1.getAsBufferedImage());

is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

if (input == null)
return;

// Get Writer and set compression
Iterator iter = ImageIO.getImageWritersByFormatName("jpg");

if (iter.hasNext()) {

ImageWriter writer = (ImageWriter) iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
` iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
float values[] = iwp.getCompressionQualityValues();

iwp.setCompressionQuality(values[2]);
String newName = realPath + "/" + "Compress" + getFileName(fileName);

File outFile = new File(newName);
FileImageOutputStream output;

output = new FileImageOutputStream(outFile);
writer.setOutput(output);
IIOImage image = new IIOImage(input, null, null);
System.out.println(
"Writing " + values[2] + "%");
writer.write(null, image, iwp);

input.flush();
output.flush();
output.close();
writer.dispose();
writer = null;
outFile = null;
image = null;
input = null;
output = null;
}
} catch (FileNotFoundException finfExcp) {
System.out.println(finfExcp);
} catch (IOException ioExcp) {
System.out.println(ioExcp);
}
}

private BufferedImage getBufferedImage(Image img) {

// if the image is already a BufferedImage, cast and return it
// if ((img instanceof BufferedImage)) {
// return (BufferedImage) img;
// }

// otherwise, create a new BufferedImage and draw the original
// image on it
int w = img.getWidth(null);
int h = img.getHeight(null);
int thumbWidth = 330;
int thumbHeight = 250;

// if width is less than 330 keep the width as it is.
if (w < thumbWidth)
thumbWidth = w;

// if height is less than 250 keep the height as it is.
if (h < thumbHeight)
thumbHeight = h;

//if less than 330*250 then do not compress
if (w > 330 || h > 250) {

double imageRatio = (double) w / (double) h;
double thumbRatio = (double) thumbWidth / (double) thumbWidth;

if (thumbRatio < imageRatio) {
thumbHeight = (int) (thumbWidth / imageRatio);
} else {
thumbWidth = (int) (thumbHeight * imageRatio);
}
}
// draw original image to thumbnail image object and
// scale it to the new size on-the-fly
BufferedImage bi = new BufferedImage(thumbWidth, thumbHeight,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
g2d.drawImage(img, 0, 0, thumbWidth, thumbHeight, null);
g2d.dispose();
return bi;
}

public static BufferedImage fromRenderedToBuffered(RenderedImage img) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}

ColorModel cm = img.getColorModel();
int w = img.getWidth();
int h = img.getHeight();
WritableRaster raster = cm.createCompatibleWritableRaster(w,h);
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
Hashtable props = new Hashtable();
String [] keys = img.getPropertyNames();

if (keys != null) {
for (int i = 0 ; i < keys.length ; i++) {
props.put(keys[i], img.getProperty(keys[i]));
}
}
BufferedImage ret = new BufferedImage(cm, raster,
isAlphaPremultiplied,
props);
img.copyData(raster);
cm = null;
return ret;
}

/**
     * @param fileName
     * @return
     */
private String getFileName(String fileName) {
String filName = fileName;
if(!filName.endsWith(".jpg")) {
if (filName.endsWith(".bmp")) {
filName = filName.replaceAll(".bmp", ".jpg");
}
if (filName.endsWith(".jpeg")) {
filName = filName.replaceAll(".jpeg", ".jpg");
}
if (filName.endsWith(".png")) {
filName = filName.replaceAll(".png", ".jpg");
}
if (filName.endsWith(".gif")) {
filName = filName.replaceAll(".gif", ".jpg");
}
}
return filName;
}

Using GZIP for HTTP request response compression

What is GZIP?
It is a compression format created by Jean-Loup Gailly and Mark Adler. Version 0.1 was first publicly released on October 31, 1992.
GZIP is based on the DEFLATE algorithm, which is a combination of LZ77 and Huffman coding. DEFLATE was intended as a replacement for LZW and other patent-encumbered data compression algorithms which, at the time, limited the usability of compress and other popular archivers.

Effect of compression on HTTP transport
The time it takes to transfer an HTTP request and response across the network can be significantly reduced by decisions made by front-end engineers. It's true that the end-user's bandwidth speed, Internet service provider, proximity to peering exchange points, etc. are beyond the control of the development team. But there are other variables that affect response times. Compression reduces response times by reducing the size of the HTTP response.
Starting with HTTP/1.1, web clients indicate support for compression with the Accept-Encoding header in the HTTP request. Accept-Encoding: gzip, deflate
If the web server sees this header in the request, it may compress the response using one of the methods listed by the client. The web server notifies the web client of this via the Content-Encoding header in the response. Content-Encoding: gzip
Gzip is the most popular and effective compression method at this time. It was developed by the GNU project and standardized by RFC 1952. The only other compression format you're likely to see is deflate, but it's less effective and less popular.

Order of reduction in request response size
Gzipping generally reduces the response size by about 70%. Approximately 90% of today's Internet traffic travels through browsers that claim to support gzip. If you use Apache, the module configuring gzip depends on your version: Apache 1.3 uses mod_gzip while Apache 2.x uses mod_deflate.

This is just a start. If the request and response are soap based or any other xml protocol, this compression can be more than 90 %.

Issues with Compression

There are known issues with browsers and proxies that may cause a mismatch in what the browser expects and what it receives with regard to compressed content. Fortunately, these edge cases are dwindling as the use of older browsers drops off. The Apache modules help out by adding appropriate Vary response headers automatically.
Servers choose what to gzip based on file type, but are typically too limited in what they decide to compress. Most web sites gzip their HTML documents. It's also worthwhile to gzip your scripts and stylesheets, but many web sites miss this opportunity. In fact, it's worthwhile to compress any text response including XML and JSON. Image and PDF files should not be gzipped because they are already compressed. Trying to gzip them not only wastes CPU but can potentially increase file sizes.
Gzipping as many file types as possible is an easy way to reduce page weight and accelerate the user experience.

Friday, 15 April 2011

Enable GZIP compression in tomcat

You can check out - effect of gzipping on  HTTP request and response to know about gzip. Here we will see how tomcat has feature to activate this feature.

Tomcat Service has this feature to compress the web pages before being downloaded by surfer, no special plug in or add on at both server or client (browser) side. Also browsers like Internet Explorer, Firefox, Opera etc supports gzip compressed content. These browsers are capable of uncompressing gzip data into plain text. Servers like Apache, Tomcat, JBoss etc supports gzip compression too. Hence if gzip is enabled in such servers, the response if first compressed and then send to client. Hence this increase performance by many folds.

Enabling Gzip compression tomcat (Settings)

Following are the steps to enable GZIP compression on Tomcat:
  • edit file /conf/server.xml 
  • Find connector tag. Something like this:
    <Connector port="80" maxHttpHeaderSize="8192"
    maxThreads="300" minSpareThreads="25" maxSpareThreads="100"
    enableLookups="false" redirectPort="443" acceptCount="100"
    connectionTimeout="120000" disableUploadTimeout="true"
    URIEncoding="utf-8"/>
  • Add to the following to HTTP Connector configuration:
    compression="on"
    compressionMinSize="2048"
    noCompressionUserAgents="gozilla, traviata"
    compressableMimeType="text/html,text/xml" 
     
  • Final HTTP Connector configuration with compression enabled would look like this:
    <Connector port="8080" maxHttpHeaderSize="8192"
    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
    enableLookups="false" redirectPort="8443" acceptCount="100"
    connectionTimeout="20000" disableUploadTimeout="true"
    compression="on"
    compressionMinSize="2048"
    noCompressionUserAgents="gozilla, traviata"
    compressableMimeType="text/html,text/xml"/>

  • Restart the server

Friday, 18 March 2011

Compression and DeCompression in Java using java.util.zip

The below example will demonstrate the following concepts in java using java.util.zip API

Compressing java string

Here are 2 static methods to do compression and decompression
private static String compressString(String myString) {
byte[] myByte = myString.getBytes();

System.out.println(“Original String is :+ myString);

// Compression level of best compression
Deflater compressor = new Deflater();
compressor.setLevel(Deflater.BEST_COMPRESSION);

// Give the Compressor the input data to compress

compressor.setInput(myByte);
compressor.finish();

// Create an expandable byte array to hold the compressed data.
// It is not necessary that the compressed data will be smaller than the
// uncompressed data.
ByteArrayOutputStream bos = new ByteArrayOutputStream(myByte.length);

// Compress the data
byte[] buf = new byte[1024];
while (!compressor.finished()) {
int count = compressor.deflate(buf);
bos.write(buf, 0, count);
}

try {
bos.close();
} catch (Exception e) {
e.printStackTrace();
}

// Get the compressed data
byte[] compressedMyByte = bos.toByteArray();
String compressedMyString = new String(compressedMyByte);

return compressedMyString;
}

private static String deCompressString(String compressedMyString) {

Inflater decompressor = new Inflater();
byte[] buf = new byte[1024];
byte[] compressedMyByte = compressedMyString.getBytes();
decompressor.setInput(compressedMyByte);

// Create an expandable byte array to hold the decompressed data
ByteArrayOutputStream bos = new ByteArrayOutputStream(
compressedMyByte.length);

// Decompress the data
buf = new byte[1024];
while (!decompressor.finished()) {
try {
int count = decompressor.inflate(buf);
bos.write(buf, 0, count);
} catch (DataFormatException e) {
}
}
try {
bos.close();
} catch (Exception e) {
e.printStackTrace();
}

// Get the decompressed data
byte[] decompressedMyByte = bos.toByteArray();

String decompressedMyString = new String(decompressedMyByte);

return decompressedMyString;

}