Java BufferedImage JPG compression without writing to file - java

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);

Related

Image file compression in 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();
}
}

Write GeoTIFF metadata from one file to other

My task is to take one GeoTIFF, make some image segmentation on in, and save it to new GeoTIFF(with existing coordinates). If I understand correctly, the coordinates are preserved in GeoTIFF metadata.
So I grab metadata from the original file:
File file = new File(inputFilePath);
ImageInputStream iis = ImageIO.createImageInputStream(file);
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
IIOMetadata metadata=null;
ImageReader reader=null;
if (readers.hasNext()) {
// pick the first available ImageReader
reader = readers.next();
// attach source to the reader
reader.setInput(iis, true);
// read metadata of first image
metadata = reader.getImageMetadata(0);
}
And when I do
System.out.println("Metadata: "+metadata);, I see the correct XML tree of metatags.
So I'm do some magic with image
System.out.println("Starting segmentation");
BufferedImage image = UtilImageIO.loadImage(inputImage);
// Select input image type. Some algorithms behave different depending on image type
ImageType<MultiSpectral<ImageFloat32>> imageType = ImageType.ms(3, ImageFloat32.class);
ImageSuperpixels alg = FactoryImageSegmentation.fh04(new ConfigFh04(500, 30), imageType);
// Convert image into BoofCV format
ImageBase color = imageType.createImage(image.getWidth(), image.getHeight());
ConvertBufferedImage.convertFrom(image, color, true);
// Segment and display results
performSegmentation(alg, color);
System.out.println("Segmentation finished");
In result I obtain a BufferedImage(resultBufferedImage) with successfully image segmentation.
And here starts my problems, I'm trying to save this BufferedImage with old metadata:
BufferedOutputStream out;
ImageWriter writer = ImageIO.getImageWriter(reader);
ImageOutputStream imgout = null;
FileOutputStream fos =null;
fos = new FileOutputStream(outputImage);
out = new BufferedOutputStream(fos);
imgout = ImageIO.createImageOutputStream(out);
writer.setOutput(imgout);
ImageWriteParam param = writer.getDefaultWriteParam();
IIOImage destIIOImage = new IIOImage(resultBufferedImage, null, metadata);
System.out.println("Before write");
writer.write(null, destIIOImage, null);
System.out.println("After write");
I get printed "After write". But program is still running, I tried to wait, but no results. So when I kill process the file is created successfully, even with geodata. How can I determine the finish of writing and stop program?
p.s. Image in default Ubuntu viewer seems to be nice, but when I opened it in QGIS I have transparent fields, and how can I make gray background transparent?
Not a real answer, but here's two answers on how to make a TIFF transparent:
QGis problem with raster transparent
How to make transparent the background of a topographic map in QGis 1.8.0?

Compression of PNG using JAI (Java Advanced Imaging)

I am using javax.imageio API and JAI for compressing different types of images. It works fine for JPEG using JPEGImageWriter and GIF using GIFImageWriter. But it is not supporting for PNG compression using PNGImageWriter which throws an exception like compression type is not set or "No valid compression", etc. So I used this below ImageWriter for PNG. It works but image quality is too bad.
Can anyone suggest how to use PNGImageWriter for PNG compression and which JAR contains it?
File input = new File("test.png");
InputStream is = new FileInputStream(input);
BufferedImage image = ImageIO.read(is);
File compressedImageFile = new File(input.getName());
OutputStream os =new FileOutputStream(compressedImageFile);
Iterator<ImageWriter>writers =
ImageIO.getImageWritersByFormatName("jpg"); // here "png" does not work
ImageWriter writer = (ImageWriter) writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
writer.write(null, new IIOImage(image, null, null), param);
It seems the default com.sun.imageio.plugins.png.PNGImageWriter that is bundled with the JRE does not support setting compression. This is kind of surprising, as the format obviously supports compression. However, the PNGImageWriter always writes compressed.
You can see from the source code that it uses:
Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
Which will give you good, but slow compression. It might be good enough for you, but for some cases it might be better to use faster compression and larger files.
To fix your code, so that it would work with any format name, change the lines:
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
to:
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed()) {
// NOTE: Any method named [set|get]Compression.* throws UnsupportedOperationException if false
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
}
It will still write compressed PNGs.
If you need more control over the PNG compression, like setting compression or filter used, you need to find an ImageWriter that supports it. As you mention JAI, I think the CLibPNGImageWriter that is part of jai-imageio.jar or jai-imageio-tools.jar supports setting compression. You just need to look through the ImageWriters iterator, to see if you have it installed:
Iterator<ImageWriter>writers = ImageIO.getImageWritersByFormatName("png");
ImageWriter writer = null;
while (writers.hasNext()) {
ImageWriter candidate = writers.next();
if (candidate.getClass().getSimpleName().equals("CLibPNGImageWriter")) {
writer = candidate; // This is the one we want
break;
}
else if (writer == null) {
writer = candidate; // Any writer is better than no writer ;-)
}
}
With the correct ImageWriter, your code should work as expected.
Please check that this code may work for you.
ImageWriteParam writeParam = writer.getDefaultWriteParam();
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("PackBits");
writeParam.setCompressionQuality(0.5f);
Thanks.

How do I write a BufferedImage as a PNG with no compression?

I need to write a BufferedImage as a .png with no compression performed. I've looked around, and come up with the following code.
public void save(String outFilePath) throws IOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("png");
ImageWriter writer = iter.next();
File file = new File(outFilePath);
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
writer.setOutput(ios);
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(1.0f);
IIOImage image = new IIOImage(mapImage, null, null);
writer.write(null, image, iwp);
writer.dispose();
//ImageIO.write(mapImage, "png", file);
}
Here's the exception being thrown.
Exception in thread "main" java.lang.UnsupportedOperationException: Compression not supported.
at javax.imageio.ImageWriteParam.setCompressionMode(Unknown Source)
at Map.MapTransformer.save(MapTransformer.java:246)
at Map.MapTransformer.main(MapTransformer.java:263)
PNG images achieve compression by first applying a prediction filter (you can choose among five variants), and then compressing the prediction error with ZLIB. You cannot omit these two steps, what you can do is to specify "NONE" as prediction filter, and compressionLevel=0 for the ZLIB compression, which would roughly correspond to a non-compressed image. The javax.imageio.* package does not allow (I think) to select this parameters, perhaps you can try with this or this

image upload resolution problems

when i am uploading an image with image io it is converting the resolutions of the image
what i mean to say is i am uploading a image with 300 dpi it get converts into 96 dpi why?
i am not using any resizing
Guess, you don't copy image metadata, here goes simplistic example how this can be done:
ImageInputStream iis =ImageIO.createImageInputStream(new File("test.jpg"));
ImageReader reader = (ImageReader) ImageIO.getImageReaders(iis).next();
reader.setInput(iis, true);
IIOMetadata meta = reader.getImageMetadata(0);
BufferedImage image = reader.read(0);
/*
do image manipulations here
*/
ImageOutputStream ios = ImageIO.createImageOutputStream(new File("out.jpg"));
ImageWriter writer = ImageIO.getImageWriter(reader);
writer.setOutput(ios);
writer.write(meta, new IIOImage(image, null, null), null);
Since you've not provided any specific details, i've tested it on local files. However, i hope that it provides you a hint.

Categories