Detecting image encoding in Java - java

I'm accepting an image as input from the user. I want to only allow a JPEG image. The image is arriving as an InputStream (called myInputStream below). In the code below, the Iterator returned by ImageIO.getImageReaders() is always empty.
ImageInputStream imageInputStream = ImageIO.createImageInputStream(
myInputStream);
Iterator<ImageReader> iter = ImageIO.getImageReaders(imageInputStream);
if (!iter.hasNext()) {
// this always happens
}
ImageReader reader = (ImageReader) iter.next();
if (!reader.getFormatName().equals("jpeg")) {
// haven't got this far yet
}
I have also tried passing myInputStream directly to ImageIO.getImageReaders() with the same result.

An empty iterator usually means ImageIO hasn't found a good image reader for decoding your image. This may be because you'are missing the right decoder in your classpath, or you image has an unsupported color model.

Related

Java ImageIO read image byte[] into pre-allocated BufferedImage

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

How to analyze a photo sent through JSON

I have a Web Service that takes a photo through a POST statement and returns a modified copy of that photo back. We are making changes to the way it processes the photo, and I want to verify that the photo at least has different properties coming back than it did before our changes went into effect.
The photo is being returned as a byte stream inside one of the fields of a JSON object. I can analyze the JSON object pretty easily, but I'm trying to figure out how to get the byte stream into an Java image object so that I can get its dimensions.
Possible duplicate of this question
... I'm trying to figure out how to get the byte stream into an Java image object so that i can get its dimensions.
I'd suggest using a BufferedImage in the following format/snippet. Note: I load my image in from disk for the example and use try-with-resources (which you may revert to 1.6-prior if needed).
String fp = "C:\\Users\\Nick\\Desktop\\test.png";
try (FileInputStream fis = new FileInputStream(new File(fp));
BufferedInputStream bis = new BufferedInputStream(fis)) {
BufferedImage img = ImageIO.read(bis);
final int w = img.getWidth(null);
final int h = img.getHeight(null);
}
You can use:
OS Process Sampler and 3rd-party tool like ImageMagick
JSR223 Test Elements, to wit
JSR223 PreProcessor to get information on the photo, you're trying to upload
JSR223 PostProcessor to get information on the photo, returned by the Web Service
JSR223 Assertion to compare two photos
Depending on what parameters you need to compare you can use ImageIO API (out of the box, bundled with JDK), Commons Imaging, ImageJ and so on.

What is the difference between using IOUtils and ImageIO for writing an image file

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

Send a stream of images using ImageIO?

I have a ServerSocket and a Socket set up so the ServerSocket sends a stream of images using ImageIO.write(....) and the Socket tries to read them and update a JFrame with them. So I wondered if ImageIO could detect the end of an image. (I have absolutely no knowledge of the JPEG format, so I tested it instead)
Apparently, not.
On the server side, I sent images continuously by using ImageIO.write(...) in loop with some sleeping in between. On the client side, ImageIO read the first image no problem, but on the next one it returned null. This is confusing. I was expecting it to either block on reading the first image (because it thinks the next image is still part of the same image), or succeed at reading all of them (because it works). What is going on? It looks like ImageIO detects the end of the first image, but not the second one. (The images, by the way, are similar to each other roughly) Is there an easy way to stream images like this or do I have to make my own mechanism that reads the bytes into a buffer until it reaches a specified byte or sequence of bytes, at which point it reads the image out of the buffer?
This is the useful part of my server code:
while(true){
Socket sock=s.accept();
System.out.println("Connection");
OutputStream out=sock.getOutputStream();
while(!socket.isClosed()){
BufferedImage img=//get image
ImageIO.write(img, "jpg", out);
Thread.sleep(100);
}
System.out.println("Closed");
}
And my client code:
Socket s=new Socket(InetAddress.getByName("localhost"), 1998);
InputStream in=s.getInputStream();
while(!s.isClosed()){
BufferedImage img=ImageIO.read(in);
if(img==null)//this is what happens on the SECOND image
else // do something useful with the image
}
ImageIO.read(InputStream) creates an ImageInputStream and calls read(ImageInputStream) internally. That latter method is documented to close the stream when it's done reading the image.
So, in theory, you can just get the ImageReader, create an ImageInputStream yourself, and have the ImageReader read from the ImageInputStream repeatedly.
Except, it appears an ImageInputStream is designed to work with one and only one image (which may or may not contain multiple frames). If you call ImageReader.read(0) more than once, it will rewind to the beginning of the (cached) stream data each time, giving you the same image over and over. ImageReader.read(1) will look for a second frame in a multi-frame image, which of course makes no sense with a JPEG.
So, maybe we can create an ImageInputStream, have the ImageReader read from it, and then create a new ImageInputStream to handle subsequent image data in the stream, right? Except, it appears ImageInputStream does all sorts of caching, read-ahead and pushback, which makes it quite difficult to know the read position of the wrapped InputStream. The next ImageInputStream will start reading data from somewhere, but it's not at the end of the first image's data like we would expect.
The only way to be certain of your underlying stream's position is with mark and reset. Since images can be large, you'll probably need a BufferedInputStream to allow a large readLimit.
This worked for me:
private static final int MAX_IMAGE_SIZE = 50 * 1024 * 1024;
static void readImages(InputStream stream)
throws IOException {
stream = new BufferedInputStream(stream);
while (true) {
stream.mark(MAX_IMAGE_SIZE);
ImageInputStream imgStream =
ImageIO.createImageInputStream(stream);
Iterator<ImageReader> i =
ImageIO.getImageReaders(imgStream);
if (!i.hasNext()) {
logger.log(Level.FINE, "No ImageReaders found, exiting.");
break;
}
ImageReader reader = i.next();
reader.setInput(imgStream);
BufferedImage image = reader.read(0);
if (image == null) {
logger.log(Level.FINE, "No more images to read, exiting.");
break;
}
logger.log(Level.INFO,
"Read {0,number}\u00d7{1,number} image",
new Object[] { image.getWidth(), image.getHeight() });
long bytesRead = imgStream.getStreamPosition();
stream.reset();
stream.skip(bytesRead);
}
}
While perhaps not the optimal way to do this the following code would get you past the issue your having. As a previous answer noted the ImageIO is not leaving the stream at the end of the image, this will find it's way to the next image.
int imageCount = in.read();
for (int i = 0; i < imageCount; i ++){
BufferedImage img = ImageIO.read(in);
while (img == null){img = ImageIO.read(in);}
//Do what ever with img
}
I hit the same problem and found this post. The comment of #VGR inspired me to dig into the problem, an eventually I realized that the ImageIO can not deal with a set of images in the same stream. So I've created the solution (in Scala, sorry) and wrote the blog post with some details and internals.
http://blog.animatron.com/post/80779366767/a-fix-for-imageio-making-animated-gifs-from-streaming
perhaps it will help somebody as well.

Uncompress a TIFF file without going through BufferedImage

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

Categories

Resources