Reduce image quality without resizing

The method I will talk about in this post is how to reduce quality of an image without resizing it. In other words, it is the method to decrease file size of an image without scaling it. As far as I know, there are two ways in order to shrink file size of an image:

- Perform scaling (resizing) of image's width and height.

- Reduce quality by decreasing color bit depth and/or compression quality.

For the former approach, you can refer to this post. For the latter, my solution is as follows:

- Read original image into a BufferedImage object:

		FileInputStream inputStream = new FileInputStream(srcPath);
		BufferedImage originalImage = ImageIO.read(inputStream);

- Obtain an ImageWriter and its ImageWriteParam for image type JPEG:

		Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
		ImageWriter writer = (ImageWriter)iter.next();
		ImageWriteParam iwp = writer.getDefaultWriteParam();

- Set compression mode and compression quality for the ImageWriteParam object (a value between 0.0 and 1.0 – 1.0 is highest quality):

		iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
		float quality = 0.5f;  // reduce quality by 50%
		iwp.setCompressionQuality(quality);

- Then write the image with reduced quality to a destination file:

		File file = new File(destPath);
		FileImageOutputStream output = new FileImageOutputStream(file);
		writer.setOutput(output);

		IIOImage image = new IIOImage(originalImage, null, null);
		writer.write(null, image, iwp);
		writer.dispose();

That's enough to create a sample program as follows:

import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;
import java.awt.image.*;

public class ImageQualityReducer {
	public static void main(String[] args) throws Exception {
		String srcPath = args[0];
		String destPath = args[1];
		float quality = Float.parseFloat(args[2]);

		Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");

		ImageWriter writer = (ImageWriter)iter.next();

		ImageWriteParam iwp = writer.getDefaultWriteParam();

		iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

		iwp.setCompressionQuality(quality);

		File file = new File(destPath);
		FileImageOutputStream output = new FileImageOutputStream(file);
		writer.setOutput(output);

		FileInputStream inputStream = new FileInputStream(srcPath);
		BufferedImage originalImage = ImageIO.read(inputStream);

		IIOImage image = new IIOImage(originalImage, null, null);
		writer.write(null, image, iwp);
		writer.dispose();

		System.out.println("DONE");
	}
}

The above program accepts three arguments from command line:

  1. Path of source image.
  2. Path of destination image.
  3. Quality value (a float between range [0,0, 1.0])

Type the following command at command prompt to run the program:

The program reduces quality of the image PIC1.JPG by setting compression quality to 0.5 and saves output to PIC2.JPG.

We can compare properties of two images using Properties dialog to see what makes difference:

As you can see, the width and height of both images are kept unchanged, but the resolution is decreased in the destination image, from 300dpi down to 96dpi.

Go further, I want to create a program that is able to automatically decrease file size of an image to a threshold, without changing its dimension. With some tweaks, I have the following code for a new program:

import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;
import java.awt.image.*;

public class ImageFileSizeReducer {

	public static void reduceImageQuality(int sizeThreshold, String srcImg, String destImg) throws Exception {
		float quality = 1.0f;

		File file = new File(srcImg);

		long fileSize = file.length();

		if (fileSize <= sizeThreshold) {
			System.out.println("Image file size is under threshold");
			return;
		}

		Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");

		ImageWriter writer = (ImageWriter)iter.next();

		ImageWriteParam iwp = writer.getDefaultWriteParam();

		iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

		FileInputStream inputStream = new FileInputStream(file);

		BufferedImage originalImage = ImageIO.read(inputStream);
		IIOImage image = new IIOImage(originalImage, null, null);

		float percent = 0.1f;	// 10% of 1

		while (fileSize > sizeThreshold) {
			if (percent >= quality) {
				percent = percent * 0.1f;
			}

			quality -= percent;

			File fileOut = new File(destImg);
			if (fileOut.exists()) {
				fileOut.delete();
			}
			FileImageOutputStream output = new FileImageOutputStream(fileOut);

			writer.setOutput(output);

			iwp.setCompressionQuality(quality);

			writer.write(null, image, iwp);

			File fileOut2 = new File(destImg);
			long newFileSize = fileOut2.length();
			if (newFileSize == fileSize) {
				// cannot reduce more, return
				break;
			} else {
				fileSize = newFileSize;
			}
			System.out.println("quality = " + quality + ", new file size = " + fileSize);
			output.close();
		}

		writer.dispose();
	}

	public static void main(String[] args) throws Exception {
		String srcPath = args[0];
		String destPath = args[1];
		int maxSize = Integer.parseInt(args[2]);

		reduceImageQuality(maxSize, srcPath, destPath);

		System.out.println("DONE");
	}
}

The new program, ImageFileSizeReducer, unlike the previous one ImageQualityReducer, does not accept a compression quality value. Instead, it accepts a threshold size to which file size of the image would be decreased. In other words, the new program shrinks file size of an image to around a specified threshold.

Type the following command to run the ImageFileSizeReducer program:

As you can see, the program gradually decreases quality of the image PIC1.JPG until file size is below the specified size (204800 bytes ~200KB) or until the compression cannot be reduced more. And finally it saves output to PIC2.JPG

Be Sociable, Share!

One thought on “Reduce image quality without resizing

  1. Dear sir,
    thank for this sharing..
    i need to convert a bufferedImage file to either bufferedimage or imageicon class in same program .. with same scale and reduction in quality

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <font color="" face="" size=""> <span style="">