Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreaded JPEG Image Processing in Java

I have problem with reading JPEG images in Java with help of Image IO in multithreaded environment. Problems only arises if several thread try to read image.

Symptoms vary from incorrect profile loading to exception:

java.awt.color.CMMException: LCMS error 13: Couldn't link the profiles

No matter how i read image, via ImageIO.read or by using ImageReader.

Source data (image) is completly isolated and immutable.

This problem can be related to: https://bugs.openjdk.java.net/browse/JDK-8041429 and https://bugs.openjdk.java.net/browse/JDK-8032243

The question is there any other way to read JPEG files with ImageIO with multiple threads. It seems like there is problem in ImageIO with sharing mutable state of the image color profiles that i have no control over. Only solution I see is completely isolating it on JVM level, which sounds like bad idea.

I use Oracle JDK 8u25. Changing JDK update version have no effect on the issue (not major version), i can't use JDK 7 without rewriting big chunks of the code.

Code for reference.

ImageInputStream input = new MemoryCacheImageInputStream(inputStream);
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);

if (!readers.hasNext()) {
    throw new IllegalArgumentException("No reader for: " + dataUuid.toString());
}

ImageReader reader = readers.next();
try {
    reader.setInput(input);
    BufferedImage image = reader.read(0, reader.getDefaultReadParam());
like image 711
YoK Avatar asked Oct 23 '14 19:10

YoK


2 Answers

Add a hook on JVM start. In the hook, just put :

Class.forName("javax.imageio.ImageIO");

This will force the class loader to load the class and do whatever static initialization it needs. I think your problem is the class is being loaded on a thread, and the 2nd thread is trying to use ImageIO, which cause a clash on locks (or lackof locks) obtained on color profiles.

Edit: You can add this line to your main too. Make sure it's the first line you call. ImageIO was not the class responsible for ColorSpace initialization.

Class.forName("java.awt.color.ICC_ColorSpace");
Class.forName("sun.java2d.cmm.lcms.LCMS");

does the trick tough.

like image 101
sturcotte06 Avatar answered Nov 14 '22 22:11

sturcotte06


I had a similar multithreading problem with ImageIO yesterday and spent all day trying to figure out a solution (JDK 8u31 Win64). Even though I was not getting LCMS exceptions, the jpegs I read with ImageIO would have completely different colors. This happened only with jpegs with embedded color profiles and only when using ImageIO in multiple threads. An not always. Approximately 50% of the time. If it would start normally then it would continue properly reading all the rest of the images, but if it would not - all the rest would also be broken. So it was definetely color profile reading/conversion synchronization issue.

The trick with class loading proposed here did not help in my case.

The final solution was to use https://github.com/haraldk/TwelveMonkeys ImageIO jpeg plugin. I have tested it with thousands of jpegs in multiple threads and so far no issues.

Update

TwelveMonkeys plugin did not solve th e problem completely, still was getting exception. What helped is reverting to the old Kodak CMS by setting system property System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");

like image 21
Rahim Avatar answered Nov 14 '22 23:11

Rahim