1

I have a lot of operations involving generating a BufferedImage object using a third-party library and saving it to a jpg file. Sometimes one processing can have over 10,000 such savings. Currently I'm using ImageIO.write(image, "jpg", file) directly but the performance is not very satisfactory.

I'm wondering if I can use direct ByteBuffer to make disk writing faster? I'm thinking about putting the BufferedImage into a direct ByteBuffer and finally save to disk using a FileChannel. I didn't find a way to do so. I wonder how can I put a BufferedImage to direct ByteBuffer? Some example code would help a lot.

J Freebird
  • 3,664
  • 7
  • 46
  • 81
  • Do you need to store the images as JPEG? If so, your best bet is probably using [native bindings for libJPEGTurbo](https://github.com/libjpeg-turbo/libjpeg-turbo/tree/master/java). It's possible to use nio-backed `ImageOutputStream`s, but I've yet to see substantial gains from this. However, if you just want to dump the pixels from memory with no file format, using a direct buffer may work. Here's some [sample code, using memory mapped `BufferedImage`s](https://github.com/haraldk/TwelveMonkeys/blob/master/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/image/MappedImageFactory.java). – Harald K Mar 14 '19 at 12:28
  • Other things you might do to improve performance regarding ImageIO, is to disable disk caching (`ImageIO.setUseCache(false)`). But I doubt this will help much if you write to a file anyway. – Harald K Mar 14 '19 at 12:45
  • @haraldK Yes, I'll need to write the the image to jpg file in the end. How does disk caching help? – J Freebird Mar 14 '19 at 12:49
  • Disk caching *does not help* (for performance that is, it *does* help in saving memory). But it's enabled by default. I'm suggesting you disable it. – Harald K Mar 14 '19 at 12:53
  • JAI ImageIO has support for [nio `FileChannel`-backed `ImageOuputStream`s](https://github.com/jai-imageio/jai-imageio-core/blob/master/src/main/java/com/github/jaiimageio/stream/FileChannelImageOutputStream.java), you may want to try to see if that helps. But still, I think libJPEGTurbo is faster, as mentioned above. – Harald K Mar 14 '19 at 12:57
  • @haraldK So is there a way to encode the bytebuffer to jpg and save it as jpg image? The save might still be faster since it's directly mapped to memory. – J Freebird Mar 14 '19 at 12:58
  • 1
    I think you are looking at this the wrong way. Memory mapped files are not going to be faster than memory. And your `BufferedImage`s are already in memory. If you do some profiling, most likely you will find that the time is spent in the JPEG encoding process, not writing the result to disk (assuming a decent SSD). Yes, you could probably make disk writing a little faster. But the real gain is switching to a faster encoder. – Harald K Mar 14 '19 at 13:04

1 Answers1

-1
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public byte[] toByteArray(BufferedImage image) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();            
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baos);
    encoder.encode(image);            
    return baos.toByteArray();
}

From this answer try this. Should be faster.

Automatik
  • 329
  • 1
  • 7
  • 15
  • I managed to do it as well, but later I found that the `byte[]` cannot be saved as jpg file. – J Freebird Mar 14 '19 at 12:50
  • *A Java program that directly calls into sun.\* packages is not guaranteed to work on all Java-compatible platforms. In fact, such a program is not guaranteed to work even in future versions on the same platform.* Read more in [Why Developers Should Not Write Programs That Call 'sun' Packages](https://www.oracle.com/technetwork/java/faq-sun-packages-142232.html). – Harald K Mar 14 '19 at 12:50