How to prevent loss of image quality while using ImageIO.write() method? - java

I am dealing with Images in java where I do read and write Images in my local disk. My Problem is while writing Images I am losing the quality of the actual Image I read. It reduces the quality by image file size from 6.19MB(actual image size) file to 1.22MB(written using ImageIO.write() which is drastic loss. How can I prevent this loss when i do use
ImageIO.write(image, "jpg", os);
for writing Image.
Remember, I dont need any compression over here. Just I want to read the Image and write the same image with same quality and same file Size. I also tried,
writer.write(null, new IIOImage(image, null, null), param);
but it takes my execution time more and does a compression process.
Please help me out in this. Is there any chance to write lossless image quality using
ImageIO.write(image, "jpg", os);
or any other way?
Thanks in advance...!

You seem to be a little confused about JPEG "compression" and "quality" (and I partly blame the ImageIO API for that, as they named the setting "compressionQuality").
JPEG is always going to compress. That's the point of the format. If you don't want compression, JPEG is not the format you want to use. Try uncompressed TIFF or BMP. As the commenters have already said, normal JPEG is always going to be lossy (JPEG-LS and JPEG Lossless are really different algorithms btw).
If you still want to go with JPEG, here's some help. Unfortunately, there's no way to control the "compressionQuality" setting using:
ImageIO.write(image, "jpg", os);
You need to use the more verbose form:
ImageReader reader = ...;
reader.setInput(...);
IIOImage image = reader.readAll(0, null); // Important, also read metadata
RenderedImage renderedImage = image.getRenderedImage();
// Modify renderedImage as you like
ImageWriter writer = ImageIO.getImageWriter(reader);
ImageWriteParam param = writer.getDefaultWriteParam();
paran.setCompressionMode(MODE_COPY_FROM_METADATA); // This modes ensures closest to original compression
writer.setOutput(...);
writer.write(null, image, param); // Write image along with original meta data
Note: try/finally blocks and stream close()and reader/writer dispose() omitted for clarity. You'll want them in real code. :-)
And this shouldn't really make your execution time noticeably longer, as ImageIO.write(...) uses similar code internally.

Related

Lossless image extraction from PDF

I'm using PDFBox to extract images out of a PDF file and feed it to another image processing library (that can handle different image formats). My current code is like this:
PDImageXObject pdImage;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedImage image = pdImage.getImage();
ImageIO.write(image, "png", baos);
byte[] imageBytes = baos.toByteArray();
This will take whatever is stored in the PDF file and use Java graphics to convert it to PNG. Is there a better way to avoid conversion and extract the image in whatever format it is embedded? I don't want to degrade image quality (I suppose mitigated by using a lossless format like PNG?) and incur conversion overhead.
The DEFLATE algorithm is used by the FlateDecode filter and by the PNG file format. However a stream of FlateDecode-compressed data isn't itself a PNG file.
Also, you need to consider the colorspace representation of the Image XObject (e.g. DeviceCMYK) versus what PNG actually supports.
By targeting lossless compression for your output image file you won't lose any information. (Be sure you actually need a lossless extracted image, often people assume lossy compression means their image will now have so many changes it's no longer recognizable. Though in many cases depending on the parameters the loss is hardly noticeable to the naked eye and you can substantially benefit from the size savings of Lossy compression.)
If performance is slow it could simply be the quality of your PDF software responsible for extracting the image and saving it.

How to save lossless jpg in java?

I have to save a jpeg image lossless. I am work on a steganography project but Java compressing and saving my result. I research every forums and try everything but it didn't work.
Here my example code for lossless save a jpeg image:
BufferedImage image = ImageIO.read(new File("sources/image.jpg"));
ImageWriter writer = ImageIO.getImageWritersByFormatName("JPEG").next();
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(null);
jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(1f);
writer.setOutput(ImageIO.createImageOutputStream(new File("example.jpg")));
writer.write(null, new IIOImage(image,null,null), jpegParams);
writer.dispose();
After this process I compute PSNR value is 28.53173 and "example.jpg"'s size is bigger than "image.jpg".
I try import JAI library but I am not sure Java 8 is support JAI.
JPEG is lossy all the time. Even at 100% compression quality there will be some loss of information, but it will be minimised.
The reason why your example.jpg has a bigger size is because it was encoded with a 100% quality factor, while most jpeg encoders have a default value of 50%-75%, which is what was most likely used for example.jpg. You can try different quality factors to see when both files have the same size.
A lossless JPEG format does exist (available in JAI), but it should be thought of as a different format to the conventional JPEG. However, it's not widely used and you'll probably not be able to view it in most applications, which would practically defeat the point of sharing an innocent image.

How to save photos in full resolution in android?

I have made an app that uses the camera to capture images. The images are passed back to my application as Bitmap. I want to know how to modify my code to save the Bitmap into JPEG format at its full resolution?
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
I think the Bitmap is being compressed into smaller size!
Compression refers to the the reduction of physical disk space required to save the image. It doesn't automatically mean that the image quality or resolution is also reduced.
JPEG is part of a group of file formats that (mostly) belong to the lossy compression algorithms. In other words some minor image detail and quality is sacrificed to reduce the file size of the image, but it still wouldn't reduce the resolution of the image.
If you want to reduce the file size of the image, but don't want to loose any image quality you need to use a file format which supports lossless compression. You can for example use Bitmap.CompressFormat.PNG.
WEBP supports both lossy and lossless compression (and is even smaller than PNG and JPG in file size). But support for WEBP was only added in API level 14 so there might be some backwards compatibility problems. Just use WEBP if possible, otherwise PNG if you care about image quality.
In any case let's look at the compress() method:
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
As you can see you can choose the CompressFormat, pass an OutputStream in and pick a quality. The number you pass in as quality can be between 0 and 100 and it determines if you compress either lossy or lossless. Since you pass in 100 the compression will always be lossless regardless of which CompressFormat you pick!
As an aside: Since PNG only supports lossless compression it will ignore the quality parameter completely and always save the image without reducing its quality!

How to resize an image in a stream (using minimal RAM)?

Looking for something like djpeg which uses O(1) RAM to resize by sub sampling, but in java and able to handle jpg, png, gif, bmp, etc. Maybe some implementation already exists.
How to resize an image in a stream (using minimal RAM)?
The FileImageInputStream doesn't know anything about specific image formats, it's just convenience for reading ints, shorts, bytes, byte arrays, etc, from a file-backed input. File format support is handled by the various ImageReader implementations.
The short answer to your question is: You can't really resize an image without loading it.
From the description of djpeg:
djpeg decompresses the named JPEG file [...]
(Emphasis is mine)
However, you can subsample images, wich is really fast (for most formats), and will uses less memory. Have a look at the ImageReadParam.setSourceSubSampling method and the ImageReader.read(int, ImageReadParam) method. This will create a resized image, quite similar to the "nearest neighbour" or "point sampling" algorithms (ie. the results won't necessary look good).
It's possible to combine subsampling first, with better quality resizing afterwards, to save memory, and possibly get acceptable results. It all depends on what quality you expect/need.
If you really, really want to resize images without loading them into heap memory (perhaps your images are huge), I've written some classes that use memory mapped files you can look at, but they are painfully slow.
The twelvemonkeys jars enable reading of non RGB jpegs
setSourceSubSampling and setSourceRegion permit resizing images in a stream.
Progressive jpegs are not supported
Large output files are not supported
Example:
File javaStreamSubsample(File inFile, int s, Rectangle sourceRegion) throws IOException {
File outFile = File.createTempFile("img", null);;
ImageInputStream input = ImageIO.createImageInputStream(inFile);
try {
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
ImageReader reader = readers.next();
try {
reader.setInput(input);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceSubsampling(s, s, 0, 0);
if(sourceRegion!=null){
param.setSourceRegion(sourceRegion);
}
BufferedImage image = reader.read(0, param);
ImageIO.write(image, "jpg", outFile);
}finally {
reader.dispose();
}
} finally {
input.close();
}
return outFile;
}

Convert BufferedImage to byte[] without losing quality and increasing size

I am trying to convert a BufferedImage to a byte array. I have two conditions. 1. I should not lose the quality of the image. 2. The size of the byte array should be the same as the actual image. I tried a couple of options.
Option 1:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
baos.flush();
byte[] imageBytes = baos.toByteArray();
baos.close();
Option 2:
WritableRaster raster = image.getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
byte[] imageBytes = data.getData();
Both these options increase the size of the image (more than twofold for large images).
Appreciate any help. Thanks!
The PNG format uses lossless data compression, so you shouldn't need to worry about loosing the quality of the image with your option 1.
How are you seeing an increase in the size of the image? I'm sure you know what imageBytes.length is. What are you comparing this to that you're thinking this is twice as large as it should be? (I'm thinking your assumption may be incorrect.)
It is possible that your original source file is just using a higher compression setting than Java is re-compressing it with. You may need to pass some additional parameters into your writer. See how to compress a PNG image using Java for some additional details - specifically the link to http://www.exampledepot.com/egs/javax.imageio/JpegWrite.html.

Categories