Multithreaded JPEG image processing in Java

I have a problem reading JPEG images in Java using Image IO in a multi-threaded environment. Problems only arise if multiple threads try to read the image.

Symptoms range from improper profile loading to an exception:

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

No matter how I read the image, using ImageIO.read or using ImageReader .

The source data (image) is completely isolated and immutable.

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

The question is, is there any other way to read JPEG files with ImageIO with multiple streams. ImageIO seems to have a problem with changing the state of the color profiles of the image together, which I do not control. Only the solution that I see completely isolates it at the JVM level, which sounds like a bad idea.

I am using Oracle JDK 8u25 . Changing the version of the JDK update does not affect the problem (and not the main version), I can not use JDK 7 without overwriting large pieces of 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()); 
+6
source share
3 answers

Add a hook to the top of the JVM. On the hook just put:

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

This will force the class loader to load the class and perform any static initialization that it needs. I think your problem is that the class is loading into the stream, and the second stream is trying to use ImageIO, which causes a collision with the locks (or the absence of locks) obtained in color profiles.

Edit: you can add this line to your main body. Make sure this is the first line you call. ImageIO was not the class responsible for initializing ColorSpace.

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

the trick is hard.

+7
source

I had a similar multi-threaded problem with ImageIO yesterday and tried all day to find a solution (JDK 8u31 Win64). Despite the fact that I did not get LCMS exceptions, the jpeg that I read with ImageIO would have completely different colors. This happened only with jpegs with built-in color profiles and only when using ImageIO in multiple streams. Not always. In about 50% of cases. If it started normally, then it would continue to read all the other images correctly, but if it weren’t, everything else would be broken too. Thus, this was a definite problem of reading / converting the color profile synchronization.

The trick with class loading suggested here didn't help in my case.

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

Update

The TwelveMonkeys plugin did not completely solve the problem, it still received an exception. Which helped to return to the old Kodak CMS by setting the System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider"); system property System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");

+3
source

I get the same error as JRE 1.8.0_31 on the same Win7 machine using PDFRenderer. Suggestions with Class.forName did not fix. However, I could find another trick. By placing the following code at the beginning of the main method, the error disappeared:

  try { ColorSpace.getInstance(ICC_ColorSpace.CS_sRGB).toRGB(new float[]{0, 0, 0}); } catch (Exception e) { e.printStackTrace(); } 

Hope this helps others too. So far I can’t confirm if this hack solves the problem anyway. The problem seems to be due to lazy updating in current versions of OpenJDK.

+2
source

Source: https://habr.com/ru/post/977150/


All Articles