Image file compression in java - java

I'm trying to compress an image file(jpg) using ImageIO. my goal is to compress image to 30 kb or less. Below is the code i used to compress image. The code is working fine. But it is not compressing to 30 kb, even if i tried again with the compressed image.
Then I try to scale the image. that time i got 30 kb file but quality is vet\y poor.
My file is an image of an application form. So I need the data to be readable.
Is ther any way i can do this with ImageIO or any other libraries.
public void jpegCompression(File imageFile,String path,Integer flag) {
try {
File compressedImageFile = new File(path+flag+"_"+imageFile.getName());
InputStream is = new FileInputStream(imageFile);
OutputStream os = new FileOutputStream(compressedImageFile);
float quality = 0.1f;
// create a BufferedImage as the result of decoding the supplied InputStream
BufferedImage image = ImageIO.read(is);
// get all image writers for JPG format
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
if (!writers.hasNext())
throw new IllegalStateException("No writers found");
ImageWriter writer = (ImageWriter) writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
// compress to a given quality
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
// appends a complete image stream containing a single image and
//associated stream and image metadata and thumbnails to the output
writer.write(null, new IIOImage(image, null, null), param);
// close all streams
is.close();
os.close();
ios.close();
writer.dispose();
} catch (IOException e) {
e.printStackTrace();
}
}

Related

How to reduce PNG/JPEG file size in spring boot

I want to reduce the file size while saving the image.
Please, take this code for reference.
And how to reduce the file size.
public void saveImage(MultipartFile image_one, MultipartFile image_two, MultipartFile image_three) throws Exception{
System.out.println("Inside Save image Repo");
String folder = "C:/Users/HP/Photos";
byte[] bytes_one;
try {
bytes_one = image_one.getBytes();
Path path1 = Paths.get(folder + image_one.getOriginalFilename());
System.out.println("Path of 1st imagae : "+path1);
Files.write(path1, bytes_one);
System.out.println("Image-1 size : "+bytes_one.length);
} catch (Exception e) {
System.out.println("Inside Catch Block -> Image not found ");
e.printStackTrace();
}
}
You could use the Java javax.imageio library and use a function to compress your image bytes.
This should do the work:
public byte[] compressImage(MultipartFile image) throws IOException
{
InputStream inputStream = image.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
float imageQuality = 0.3f;
// Create the buffered image
BufferedImage bufferedImage = ImageIO.read(inputStream);
// Get image writers
Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByFormatName("jpg"); // Input your Format Name here
if (!imageWriters.hasNext())
throw new IllegalStateException("Writers Not Found!!");
ImageWriter imageWriter = imageWriters.next();
ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputStream);
imageWriter.setOutput(imageOutputStream);
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
// Set the compress quality metrics
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageWriteParam.setCompressionQuality(imageQuality);
// Compress and insert the image into the byte array.
imageWriter.write(null, new IIOImage(bufferedImage, null, null), imageWriteParam);
byte[] imageBytes = outputStream.toByteArray();
// close all streams
inputStream.close();
outputStream.close();
imageOutputStream.close();
imageWriter.dispose();
return imageBytes;
}
It returns the compressed image bytes so that the value returned can be transformed into a number of things. In your case, in a file...
byte[] compressedImageBytes = compressImage(imageOne);
Files.write(path1, bytesOne);

Java BufferedImage JPG compression without writing to file

I've seen several examples of making compressed JPG images from Java BufferedImage objects by writing to file, but is it possible to perform JPG compression without writing to file? Perhaps by writing to a ByteArrayOutputStream like this?
ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(0.7f);
ImageOutputStream outputStream = createOutputStream();
jpgWriter.setOutput(outputStream);
IIOImage outputImage = new IIOImage(image, null, null);
// in this example, the JPG is written to file...
// jpgWriter.write(null, outputImage, jpgWriteParam);
// jpgWriter.dispose();
// ...but I want to compress without saving, such as
ByteArrayOutputStream compressed = ???
Just pass your ByteArrayOutputStream to ImageIO.createImageOutputStream(...) like this:
// The important part: Create in-memory stream
ByteArrayOutputStream compressed = new ByteArrayOutputStream();
try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(compressed)) {
// NOTE: The rest of the code is just a cleaned up version of your code
// Obtain writer for JPEG format
ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("JPEG").next();
// Configure JPEG compression: 70% quality
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(0.7f);
// Set your in-memory stream as the output
jpgWriter.setOutput(outputStream);
// Write image as JPEG w/configured settings to the in-memory stream
// (the IIOImage is just an aggregator object, allowing you to associate
// thumbnails and metadata to the image, it "does" nothing)
jpgWriter.write(null, new IIOImage(image, null, null), jpgWriteParam);
// Dispose the writer to free resources
jpgWriter.dispose();
}
// Get data for further processing...
byte[] jpegData = compressed.toByteArray();
PS: By default, ImageIO will use disk caching when creating your ImageOutputStream. This may slow down your in-memory stream writing. To disable it, use ImageIO.setCache(false) (disables disk caching globally) or explicitly create an MemoryCacheImageOutputStream (local), like this:
ImageOutputStream outputStream = new MemoryCacheImageOutputStream(compressed);

How to reduce size of a multipart file in java

I have Java Spring MVC application in which there is an option to upload an image and save to the server. i have the following method:
#RequestMapping(value = "/uploaddocimagecontentsubmit", method = RequestMethod.POST)
public String createUpdateFileImageContentSubmit(#RequestParam("file") MultipartFile file, ModelMap model)
{
//methods to handle file upload
}
I am now trying to reduce the size of the image refering the following:
increasing-resolution-and-reducing-size-of-an-image-in-java and decrease-image-resolution-in-java
The problem I am facing is that in the above examples, we are dealing with java.io.File Objects which are saved to a specified location. I dont want to save the image. Is there any way that I can use something similar to compress my Multipart Image file and continue with the upload.
Why don't you resize it on the client before upload? That will save some bandwidth
BlueImp JQuery Upload can do this
It was my first time taking a deep dive into the ImageIO package. I came across the MemoryCacheImageOutputStream, which allows you to write an image output stream to an output stream, i.e. ByteArrayOutputStream. From there, The data can be retrieved using toByteArray() and toString(), after compression. I used toByteArray, as I am storing images to postgresql and it stores the images as a byte array. I know this is late, but I hope it helps someone.
private byte[] compressImage(MultipartFile mpFile) {
float quality = 0.3f;
String imageName = mpFile.getOriginalFilename();
String imageExtension = imageName.substring(imageName.lastIndexOf(".") + 1);
// Returns an Iterator containing all currently registered ImageWriters that claim to be able to encode the named format.
// You don't have to register one yourself; some are provided.
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName(imageExtension).next();
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // Check the api value that suites your needs.
// A compression quality setting of 0.0 is most generically interpreted as "high compression is important,"
// while a setting of 1.0 is most generically interpreted as "high image quality is important."
imageWriteParam.setCompressionQuality(quality);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// MemoryCacheImageOutputStream: An implementation of ImageOutputStream that writes its output to a regular
// OutputStream, i.e. the ByteArrayOutputStream.
ImageOutputStream imageOutputStream = new MemoryCacheImageOutputStream(baos);
// Sets the destination to the given ImageOutputStream or other Object.
imageWriter.setOutput(imageOutputStream);
BufferedImage originalImage = null;
try (InputStream inputStream = mpFile.getInputStream()) {
originalImage = ImageIO.read(inputStream);
} catch (IOException e) {
String info = String.format("compressImage - bufferedImage (file %s)- IOException - message: %s ", imageName, e.getMessage());
logger.error(info);
return baos.toByteArray();
}
IIOImage image = new IIOImage(originalImage, null, null);
try {
imageWriter.write(null, image, imageWriteParam);
} catch (IOException e) {
String info = String.format("compressImage - imageWriter (file %s)- IOException - message: %s ", imageName, e.getMessage());
logger.error(info);
} finally {
imageWriter.dispose();
}
return baos.toByteArray();
}

Using ImageIO to write JPEG 2000 with layers (i.e. decomposition levels)

Ok, here is our issue:
We are trying to convert a series of black and white .tiff files into jpeg2000 .jpf files, using imageio. We are always getting viewable .jpf files, but they usually do not have the specified number of layers or decomposition levels for zooming.
Here is our code:
//Get the tiff reader
Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByFormatName("tiff");
ImageReader tiffreader = readerIterator.next();
//make an ImageInputStream from our tiff file and have the tiff reader read it
ImageInputStream iis = ImageIO.createImageInputStream(itemFile);
tiffreader.setInput(iis);
//just pass empty params to the tiff reader
ImageReadParam tparam;
tparam = new TIFFImageReadParam();
IIOImage img = tiffreader.readAll(0, tparam);
//set up target file
File f = new File(itemTargetDirectory.getAbsolutePath() + "/" + destFileName);
//we have tried FILTER_97 as well as different ProgressionTypes and compression settings
J2KImageWriteParam param;
param = new J2KImageWriteParam();
param.setProgressionType("layer");
param.setFilter(J2KImageWriteParam.FILTER_53);
//Our problem is that this param is not always respected in the resulting .jpf
param.setNumDecompositionLevels(5);
//get the JPEG 2000 writer
Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByFormatName("JPEG 2000");
J2KImageWriter jp2kwriter = null;
jp2kwriter = (J2KImageWriter) writerIterator.next();
//write the jpf file
ImageOutputStream ios = ImageIO.createImageOutputStream(f);
jp2kwriter.setOutput(ios);
jp2kwriter.write(null, img, param);
It has been an odd experience, as the same code has behaved differently on subsequent runs.
Any insights will be appreciated!
Do all the TIFF files have the same settings (color model)? J2KImageWriter.java shows the decomposition levels getting set (forced) to zero when indexed-color or multi-pixel packed source images are used as input.
Drew was on the right track, and here is the code that ended up sorting things out for us:
public void compressor(String inputFile, String outputFile) throws IOException {
J2KImageWriteParam iwp = new J2KImageWriteParam();
FileInputStream fis = new FileInputStream(new File(inputFile));
BufferedImage image = ImageIO.read(fis);
fis.close();
if (image == null)
{
System.out.println("If no registered ImageReader claims to be able to read the resulting stream");
}
Iterator writers = ImageIO.getImageWritersByFormatName("JPEG2000");
String name = null;
ImageWriter writer = null;
while (name != "com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriter") {
writer = (ImageWriter) writers.next();
name = writer.getClass().getName();
System.out.println(name);
}
File f = new File(outputFile);
long s = System.currentTimeMillis();
ImageOutputStream ios = ImageIO.createImageOutputStream(f);
writer.setOutput(ios);
J2KImageWriteParam param = (J2KImageWriteParam) writer.getDefaultWriteParam();
IIOImage ioimage = new IIOImage(image, null, null);
param.setSOP(true);
param.setWriteCodeStreamOnly(true);
param.setProgressionType("layer");
param.setLossless(false);
param.setCompressionMode(J2KImageWriteParam.MODE_EXPLICIT);
param.setCompressionType("JPEG2000");
param.setCompressionQuality(0.1f);
param.setEncodingRate(1.01);
param.setFilter(J2KImageWriteParam.FILTER_97);
writer.write(null, ioimage, param);
System.out.println(System.currentTimeMillis() - s);
writer.dispose();
ios.flush();
ios.close();
image.flush();
}

How can I convert png/gif/jpg in grails to jpg maintaining a high quality in the image?

I use the following method for converting images to jpg. The problem with my solution is that it will reduce the quality to much.
What is a good way to maintain quality of the image, while reducing the file size?
def convertToJpg(currentImage) {
try {
InputStream inStreamCrop = new ByteArrayInputStream(currentImage)
BufferedImage bufferedImage = ImageIO.read(inStreamCrop)
// create a blank, RGB, same width and height, and a white background
BufferedImage newBufferedImage = new BufferedImage(bufferedImage.getWidth(),
bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB);
newBufferedImage.createGraphics().drawImage(bufferedImage, 0, 0, Color.WHITE, null);
ByteArrayOutputStream baos=new ByteArrayOutputStream()
// write to jpeg file
ImageIO.write(newBufferedImage, "jpg", baos);
baos.flush()
def image = baos.toByteArray()
baos.close()
return image
} catch (IOException e) {
e.printStackTrace();
}
}
You can control the quality of the JPEG by getting an ImageWriter object and setting it through the parameters. For example:
import javax.imageio.stream.*
import javax.imageio.*
BufferedImage bufferedImage = ImageIO.read(new File("test.png"));
float quality = 0.9;
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
writer.setOutput(new FileImageOutputStream(new File("test.jpg")));
writer.write(null, new IIOImage(bufferedImage, null, null), param);
The quality parameter can range between 0 and 1, with 1 having the least compression. Try different values; I've found 0.9 is a good choice for low compression (but large file sizes).
Note that JPEG images are not supported with OpenJDK: ImageIO not able to write a JPEG file

Categories