I have JPG image files, which I want to load into a BufferedImage and later write the BufferedImage back into a JPG file. Here is what I am currently doing.
Is there a better way not to lose quality and make read/write faster?
Read:
BufferedImage image = ImageIO.read(new File(storagePath + fileName + extension));
Write:
BufferedImage image = // some jpg image
Iterator iter = ImageIO.getImageWritersByFormatName("JPG");
if (iter.hasNext()) {
ImageWriter writer = (ImageWriter) iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(quality);
File outFile = new File(storagePath + fileName + extension);
FileImageOutputStream output = new FileImageOutputStream(outFile);
writer.setOutput(output);
IIOImage iioImage = new IIOImage(image, null, null);
writer.write(null, iioImage, iwp);
}
You can minimize the loss in quality in recompressing JPEG by using the same quantization tables used to correct the original image. It is still possible to get single bit errors from rounding but you can get it pretty close.
The problem is how to get the quantization tables. If your encoded will allow you to specify them, you can pull the values out of the source image. Otherwise, you have to hope that the images were originally encoded using the same encoder.
"Quality Values" are not part of JPEG. They are a method for selecting quantization tables used by some encoder. The LIBJPEG is the most common encoder but there are others out there that do things differently.
PNG encoding is generally slower than JPEG.
Related
I'm working with ImageIO and JAI and want to read a byte array into a BufferedImage. The byte[] contains data for a JP2000 encoded image, and it's fairly large, around 100MB. I'm currently doing something like:
byte[] imageDataBytes = ...
InputStream imageStream = new ByteArrayInputStream(imageDataBytes);
BufferedImage imageData = ImageIO.read(imageStream);
It seems that ImageIO is creating a new BufferedImage each time read() is called.
Question:
Is there a way to tell ImageIO to read and decode the image byte data into a pre-allocated mutable BufferedImage?
I did some searching through the Javadocs and found that the BufferedImage stores its data in a Raster object, which stores its data in a DataBuffer object. So I'm aware any solution that exists will technically not be writing to the BufferedImage, but instead will be directly writing to the DataBuffer.
It may help to know that all images are the same size: roughly 10,000 x 10,000, so there shouldn't be any problems with the read image not aligning with the buffered image. Ultimately, I would like to have an object pool of buffered images, or rasters, or data buffers, and borrow from the pool every time I read using ImageIO. Something like this pseudocode:
InputStream imageStream = new ByteArrayInputStream(imageDataBytes);
WritableRaster raster = ObjectPool.getAvailableRaster();
ImageIO.readToRaster(imageStream, raster);
BufferedImage imageData = new BufferedImage(raster);
I'm sure there's a simple solution out there. Any help would be appreciated!
Yes, you can set the destination image of an ImageReadParam object. However, there is a caveat: the BufferedImage must have a ColorModel and SampleModel that match the image being loaded.
I’m not sure about JPEG2000 images, but regular JPEGs are usually RGB images, so an image of TYPE_INT_RGB should suffice:
BufferedImage image = new BufferedImage(10000, 10000,
BufferedImage.TYPE_INT_RGB);
while (bytesAvailable) {
byte[] imageDataBytes = getImageBytes();
try (InputStream in = new ByteArrayInputStream(imageDataBytes);
ImageInputStream stream = ImageIO.createImageInputStream(in)) {
ImageReader reader = ImageIO.getImageReaders(stream).next();
reader.setInput(stream);
ImageReadParam param = reader.getDefaultReadParam();
param.setDestination(image);
reader.read(0, param);
}
}
For those who find themselves in this situation, the answer by VGR works well. I like to add that specifically for JPEG-2000 images that contain metadata, use
reader.setInput(stream, true, true);
instead of
reader.setInput(stream);
This avoids a NullPointer exception. you can read more about it here:
https://issues.apache.org/jira/browse/PDFBOX-2103
I have a tiff image stored as Base64 encoded String in a file. My aim is to create a tiff file out of it. This is what I am doing:
String base64encodedTiff = IOUtils.toString(new FileInputStream("C:/tiff-attachment.txt"));
byte[] imgBytes = DatatypeConverter.parseBase64Binary(base64encodedTiff);
BufferedImage bufImg = ImageIO.read(new ByteArrayInputStream(imgBytes));
ImageIO.write(bufImg, "tiff", new File("c:/new-darksouls-imageIO-tiff.tiff"));
ImageIO.write() is throwing IllegalArgumentException because bufImg is null. I don't understand what am I doing wrong here.
On the contrary if I use IOUtils to write, it works fine:
IOUtils.write(imgBytes, new FileOutputStream("c:/new-darksouls-io-tiff.tiff"));
Please help me understand
Why ImageIO is throwing exception
What is the right API and way for what I am trying to achieve.
ImageIO would be useful if, for example, you wanted to convert a PNG to a JPEG. Since you don't need to manipulate the image or convert to another format, don't bother with ImageIO. Just use IOUtils.write() to save the TIFF data verbatim.
ImageIO.read() is returning a null image because it can't read the TIFF file, probably because TIFF isn't one of the standard ImageIO plugin formats. The standard supported image formats are listed here:
http://docs.oracle.com/javase/6/docs/api/javax/imageio/package-summary.html
An additional note -- the code you posted buffers the entire image in memory. If you're concerned about using memory efficiently, consider using some kind of Base64 decoding input stream to perform the decoding on the fly. That might look like this:
try (FileOutputStream out = new FileOutputStream("c:/new-darksouls-io-tiff.tiff");
FileInputStream in = new FileInputStream("C:/tiff-attachment.txt");
Base64InputStream decodedIn = new Base64InputStream(in)) {
IOUtils.copy(decodedIn, out);
}
Any reason this code would changing the resolution of the original JPEG? I can understand if the file size were different because the JPEG quality settings are probably different but I don't understand why this would be resizing an image.
File newfile=new File(mydestinationfolder.concat(imagename));
Files.move(file.toPath(),newfile.toPath(), REPLACE_EXISTING);
Rotation Orientation;
if ((Orientation=Exif_data.get_Exif_Orientation(newfile)) != null) {
System.out.println(Orientation.toString());
BufferedImage oldimage = ImageIO.read(newfile);
BufferedImage tmp = Scalr.rotate(oldimage, Orientation);
oldimage.flush();
oldimage=tmp;
ImageIO.write(oldimage, "JPEG", newfile);
}
Well I am not sure why but the default settings for ImageIO.write() are changing the resolution. If I define a custom writer with JPEG quality set to 100%, the image resolution stays the same.
NOTE: output.close() at the end is important because as long as the stream is open the file is locked.
BufferedImage oldimage = ImageIO.read(newfile);
BufferedImage tmp = Scalr.rotate(oldimage, Orientation);
oldimage.flush();
oldimage=tmp;
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
float quality=1.0f;
iwp.setCompressionQuality(quality);
FileImageOutputStream output = new FileImageOutputStream(newfile);
writer.setOutput(output);
IIOImage image = new IIOImage(oldimage, null, null);
writer.write(null, image, iwp);
writer.dispose();
output.close();
Late answer, but anyway..
As a JPEG read/manipulate/write cycle using ImageIO is always going to be lossy, and all you are doing is rotating, you should have a look at LLJTran from mediautil, as mentioned in this thread.
With that package, you should be able to benefit from a special feature of JPEG block compression to do a lossless JPEG transformation.
Still don't understand why your original code would change the image resolution, nor why your proposed solution would fix it though... Sounds like a bug to me, and should be reported to Oracle. What I do know though, is that setting the JPEG quality to 1.0 (100%) isn't what JPEG was meant for, and is going to cause huge files with no gain in quality (it may possibly be worse than storing at the quality of the original) given the input is already a compressed JPEG .
I am receiving large size CCITT Group 4 compressed TIFF files that need to be written elsewhere as uncompressed TIFF files. I am using the jai_imageio TIFF reader and writer to do that and it works well as long as the product _width * height_ of the image fits in an integer.
Here is the code I am using:
TIFFImageReaderSpi readerSpi= new TIFFImageReaderSpi();
ImageReader imageReader = readerSpi.createReaderInstance();
byte[] data = blobManager.getObjectForIdAndVersion(id, version);
ImageInputStream imageInputStream = ImageIO.createImageInputStream(data);
imageReader.setInput(imageInputStream);
TIFFImageWriterSpi writerSpi = new TIFFImageWriterSpi();
ImageWriter imageWriter = writerSpi.createWriterInstance();
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
//bufferFile is created in the constructor
ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(bufferFile);
imageWriter.setOutput(imageOutputStream);
//Now read the bitmap
BufferedImage bufferedImage = imageReader.read(0);
IIOImage iIOImage = new IIOImage(bufferedImage, null, null);
//and write it
imageWriter.write(null, iIOImage, imageWriteParam);
Unfortunately, the files that I receive are often very large and the BufferedImage cannot be created.
I have been trying to find a way to stream from the ImageReader directly to the ImageWriter but I cannot find out how to do that.
Anybody with a suggestion?
I've had the some issues, and the end result might surprise you :
I ended up calling IrfanView with some command-line options using the Runtime.exec() method. That way, I am not worried about compression or size, it just works and outputs the correct files in the correct folder for me.
If you are on Linux, you can use ImageMagik or something similar.
You can use TIFF tiles to segment a TIFF into smaller portions ("tiles"). If you control the code creating the big images, JAI allows you to retrieve image content tile-by-tile.
Here is an example on how to create tiled image with JAI:
ColorModel cm = source.createColorModel();
// SampleModel with the tilesize
SampleModel sm = cm.createCompatibleSampleModel(tileWidth, tileHeight);
TiledImage image = new TiledImage(0, 0, imageWidth, imageHeight, 0, 0, sm, cm);
TIFFEncodeParam tep = new TIFFEncodeParam();
tep.setTileSize(tileWidth, tileHeight); // Set tile size to avoid OOM
tep.setWriteTiled(true);
JAI.create("filestore", image, filepath, "TIFF", tep);
If you can't control the TIFF production, my knowledge of JAI is too limited to be of much help.
Give your Java VM more memory.
If that doesn't work, look at the source code of the TIFF plugin in the JAI source code. You might be able to write your own processor which just decompresses the data structures using a streaming approach (so you'll never have to keep the whole image in memory at any time).
If that also doesn't work, look at JNA which allows you to call code from a DLL from Java (no C code required; everything is done from pure Java, unlike with Sun's JNI API).
BufferedImage image = ImageIO.read( new ByteArrayInputStream( byteArray ) );
ImageIO.write(image, "BMP", new File("filename.bmp"));
When i tried this thing [ImageIO.read( new ByteArrayInputStream( byteArray ))] returns null value so i cant create new bmp file.
But this works to convert jpg to bmp files.I have raw files and i need to convert to image.
Please help me in this.
The support for readers and writers in the javax.imageio is limited, and if you dont know the format of the byte[] your are trying to convert, then propably it is not included in the list of valid image readers. Then, what you might need is the Java Advanced Imaging API, which you can find here.
These are the readers a
Readers: Writers:
bmp bmp
jpg jpg
jpeg jpeg
wbmp Wbmp
png png
gif gif
This few lines shall help you to get this to work using JAI API
// read byte[]
SeekableStream stream = new ByteArraySeekableStream(image);
// render stream of bytes to valid image format
RenderedImage renderedImage = JAI.create("stream", stream);
// persist image to file
JAI.create("filestore", renderedImage, filename, targetFormat);
// dont't forget to close the stream!
stream.close();
Take a look here: Java Advanced Imaging I/O Tools. The Java Image I/O Reader seems to be able to read "raw"-Files. I think that you can write them with the Java Image I/O Writer as BMP.
regards
Macs