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

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.

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.

Java - reading a compressed bitmap

I'm looking to read in the RGB values of a bitmap (or the hex colour codes, either work).
I have tried both this code :
File image = serverConfig.get(map.bmp);
BufferedImage buffer = ImageIO.read(image);
dimX = buffer.getWidth();
dimY = buffer.getHeight();
byte[] pixlesB = (byte[]) buffer.getRaster().getDataElements(0, 0, buffer.getWidth(), buffer.getHeight(), null);
and this code :
File image = serverConfig.get(map.bmp);
BufferedImage buffer = ImageIO.read(image);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(buffer, "bmp", baos );
baos.flush();
byte[] pixlesB = baos.toByteArray();
baos.close();
The both work fine for a small bitmap, but when I load a large bitmap, the data gets compressed and the array returns a bunch of semi random numbers.
for example:
A green pixel will read 2,2,2 instead of 0,255,0
A red pixel will read 5,5,5 instead of 255,0,0
A yellow pixel will read 8,8,8 instead of 255,255,0
The bitmaps I'm using only include the colours red, yellow and green.
My problem is I have no way of knowing what colour 2,2,2 relates to without checking it manually (which I cannot do since it changes with each bitmap)
I know that there is some metadata in the bitmap that specifies 2 is green, but I don't know how to access it or use it to turn 2 back into 0,255,0
And this is not a duplicate of Java - get pixel array from image since that doesn't mention compressed files.
And while I did ask this question a while back, it was just redirected to the above site.
Thanks in advance!
EDIT: Just thought this might make the question a bit clearer. I believe the file is being read correctly, it is just compressed. How do I decompress it?
If you want to read an image as a bitmap or as rgb values, you need to transform the image's format first.
Jpeg is a compressed image format, you need to use a tool or library in order to read as rgb.
check this answer:
How to get the rgb values of a jpeg image in TYPE_3BYTE_BGR?
Hope this helps

How to rotate a jpeg lossless (90, 180, 270, 360) [duplicate]

This question already has answers here:
Lossless JPEG Rotate (90/180/270 degrees) in Java?
(5 answers)
Closed 8 years ago.
I want to rotate an jpeg image lossless.
My code works with success but is with loss:
ByteArrayInputStream inputstream = new ByteArrayInputStream(imgByteArraySource);
BufferedImage oldBufferedImage = ImageIO.read(inputstream);
BufferedImage newBufferedImage = rotate(oldBufferedImage, degrees);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(newBufferedImage, "jpg", baos);
baos.flush();
byte[] imageInByte = baos.toByteArray();
I found an old post on this site:
Lossless JPEG Rotate (90/180/270 degrees) in Java?
Maybe there is a better solution.
I'm not very confident about the media-util library (it is old, and it is packaged in a .exe file).
Has someone an idea?
Is there a new and better way to do this?
Thanks in advance.
Bobby
It is only possible to rotate losslessly in 90 degree increments.
To do it in 90 increments, you need to either compress using the same quantization tables used in the original (which still may introduce loss, depending upon the encoder) or use a special program that rearranges the MCUs in the JPEG stream.
Any time you decompress a JPEG using different quantization tables you will get loss.

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

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.

How to calculate java BufferedImage filesize

I have a servlet based application that is serving images from files stored locally. I have added logic that will allow the application to load the image file to a BufferedImage and then resize the image, add watermark text over the top of the image, or both.
I would like to set the content length before writing out the image. Apart from writing the image to a temporary file or byte array, is there a way to find the size of the BufferedImage?
All files are being written as jpg if that helps in calculating the size.
BufferedImage img = = new BufferedImage(500, 300, BufferedImage.TYPE_INT_RGB);
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
ImageIO.write(img, "png", tmp);
tmp.close();
Integer contentLength = tmp.size();
response.setContentType("image/png");
response.setHeader("Content-Length",contentLength.toString());
OutputStream out = response.getOutputStream();
out.write(tmp.toByteArray());
out.close();
No, you must write the file in memory or to a temporary file.
The reason is that it's impossible to predict how the JPEG encoding will affect file size.
Also, it's not good enough to "guess" at the file size; the Content-Length header has to be spot-on.
Well, the BufferedImage doesn't know that it's being written as a JPEG - as far as it's concerned, it could be PNG or GIF or TGA or TIFF or BMP... and all of those have different file sizes. So I don't believe there's any way for the BufferedImage to give you a file size directly. You'll just have to write it out and count the bytes.
You can calculate the size of a BufferedImage in memory very easily. This is because it is a wrapper for a WritableRaster that uses a DataBuffer for it's backing. If you want to calculate it's size in memory you can get a copy of the image's raster using getData() and then measuring the size of the data buffer in the raster.
DataBuffer dataBuffer = bufImg.getData().getDataBuffer();
// Each bank element in the data buffer is a 32-bit integer
long sizeBytes = ((long) dataBuffer.getSize()) * 4l;
long sizeMB = sizeBytes / (1024l * 1024l);`
Unless it is a very small image file, prefer to use chunked encoding over specifying a content length.
It was noted in one or two recent stackoverflow podcasts that HTTP proxies often report that they only support HTTP/1.0, which may be an issue.
Before you load the image file as a BufferedImage make a reference to the image file via the File object.
File imgObj = new File("your Image file path");
int imgLength = (int) imgObj.length();
imgLength would be your approximate image size though it my vary after resizing and then any operations you perform on it.

Categories