I was trying to get some stats about the compression performance of java ImageIO compression with PNG files, and I was bitten by this crazy behaviour.
I have a bank of PNG (say) 150 test images, and I fed with them this simple code:
for (File ori : files) {
BufferedImage img = ImageIO.read(ori);
File dest = new File("C:/temp/x.png"); // whatever
OutputStream nos = new FileOutputStream(dest)
ImageIO.write(img, "PNG", nos);
nos.close();
long size = dest.length();
// report size
}
(I've stripped down the exception handling and some unimportant code - also, in my real implementation I don't write to a File but to a dummy outputstream that just count bytes - all that has no influence). This is call from a console Java program, nothing much more than the a main()
that calls that. Running from Eclipse, JRE 1.7.0_55-b14 (32bits).
My problem is that this gives quite different results in different runs. Actually, it gives two different sets of results. In one of them, let's call it the "good" case, the compressed sizes are quite smaller (average ~ 87%) thant the "bad" case. I get always these precise two set of results, and only these, and never mixed.
When I get one of the two results seems random, I could not find a pattern. If I run this with only one file, instead of with a long list, I only get one result (the bad one). I could reproduce this with only one image, though the "good" case happens more sporadically, see update below.
I've examined some pairs of good/bad images, and they are OK, no strange things, it just seems as if they were encoded by different encoders.
Can anyone explain this?
Update: this is a pair of "good" and "bad" images.
Update2: Ok, I could reproduce this with only one image. Here's the code:
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Locale;
import javax.imageio.ImageIO;
public class ImageIoTest {
public static void main(String[] args) throws Exception {
Locale.setDefault(Locale.US);
for (int i = 0; i < 3; i++)
testWith(new File("C:/temp/m0550.png"));
}
private static long testWith(File f) throws Exception {
File dest = new File(f.getParent(), "new" + f.getName());
BufferedImage img = ImageIO.read(f);
OutputStream nos = new FileOutputStream(dest);
ImageIO.write(img, "PNG", nos);
nos.close();
long size = dest.length();
System.out.printf("%s\t%d\n", dest, size);
return size;
}
Set you image path appropiately. I use this image (1103429 bytes), though that should not be critical (but at this time I don't trust any "should"). IF I run the above repeteadly, the 4 calls inside the loop return always the same value. But that same value varies from run to run. Sometimes it gives 1099207 sometimes it gives 1498218 (more strangely even, commenting the Locale line sometimes I get the alteration)... Puzzling.
Final update and explanation: @anonymous, in the accepted answer, got it. The issue was having two ImageWriter
s (I think this is because once I installed JAI). And it seems that, in that situation, Java selects the prefered one based on... God knows what, perhaps at random.
Here we get again the deterministic behaviour we all appreciate:
public class ImageIoTest {
public static void main(String[] args) throws Exception {
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("PNG");
for(;iter.hasNext();) {
ImageWriter iw = iter.next();
testWith(new File("C:/temp/m0550.png"),iw);
}
}
private static long testWith(File f, ImageWriter iw) throws Exception {
File dest = new File(f.getParent(), "new" + f.getName());
BufferedImage img = ImageIO.read(f);
OutputStream nos = new FileOutputStream(dest);
ImageOutputStream ios = ImageIO.createImageOutputStream(nos);
iw.setOutput(ios);
iw.write(img);
ios.close();
nos.close();
long size = dest.length();
System.out.printf("%s\t%d\t%s\n", dest, size,iw.getOriginatingProvider().getPluginClassName());
return size;
}
}
Which in my case prints:
C:\temp\newm0550.png 1099207 com.sun.media.imageioimpl.plugins.png.CLibPNGImageWriter
C:\temp\newm0550.png 1498218 com.sun.imageio.plugins.png.PNGImageWriter