Multithreaded JPEG Image Processing in Java - 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());

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.

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

I am getting the same error since JRE 1.8.0_31 on a single Win7 machine using PDFRenderer. The suggestions with Class.forName did not fix it. However I could find another trick: By placing the following code at the top of the main method, the error did vanish:
try {
ColorSpace.getInstance(ICC_ColorSpace.CS_sRGB).toRGB(new float[]{0, 0, 0});
} catch (Exception e) {
e.printStackTrace();
}
Hopefully, this will help others as well. So far I cannot confirm, if this hack solves the problem in any case. The problem seems to be related with lazy instantion in the current versions of OpenJDK.

Related

Accessing /dev/mem through Java

I'm trying to use Raspberry PI's PWM from Java, without using WiringPi/Pi4j or any other library. So far I learned that the only way to control the PWMs is by memory-mapping the /dev/mem file and setting appropriate registers there. That's how WiringPi does it (see here) and also people ask about it pretty often on the Raspberry Pi StackExchange.
I'm facing a certain problem that seems to be irrelevant to the Raspberry, that's why I'm asking the question on the general SO forum. I tested the below symptom positive on my Ubuntu laptop.
So, I'm trying with this simple piece of code with seems to be a minimal example that reproduces the problem, runnnig with sudo:
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class Main {
public static void main(String[] args) {
try {
RandomAccessFile file = new RandomAccessFile("/dev/mem", "rw");
long size = 12345; // Anything greater than 0 - it seems to be irrelevant.
MappedByteBuffer out = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);
// Here I plan to make some writes to the registers.
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'm not even trying to read any data or change the internal pointer position. I'm getting the following stack trace, originating from the call to map(...):
java.io.IOException: Invalid argument
at java.base/sun.nio.ch.FileDispatcherImpl.truncate0(Native Method)
at java.base/sun.nio.ch.FileDispatcherImpl.truncate(FileDispatcherImpl.java:86)
at java.base/sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:987)
at Main.main(Main.java:10)
When debugging the code, I noticed this piece in the implementation of map(...) (in sun/nio/ch/FileChannelImpl.java):
filesize = nd.size(fd);
and the file size is calculated to hava a size of 0. I don't know how to debug deeper because it seems to be already a native call, through JNI.
Checking the size of this file through the command-line reveals 1:
$ ll | grep mem
crw-r----- 1 root kmem 1, 1 lip 2 13:12 mem
I checked the man page: https://man7.org/linux/man-pages/man4/mem.4.html, but it doesn't seem to address this specific prolem.
It got more interesting when I found out that the JDK version matters. It doesn't work on 11.0.1 from Oracle, and OpenJDK 14.0.1. However, it does work (doesn't crash) on 8.0.191 from Oracle. I have some problems with attaching sources of this JDK to my debugger, so I cannot debug it as deeply as with the other version. I also cannot use JDK 8 on my Raspberry, I have some problems installing it. I figured it's better to understand the problem better rather than sticking to a single JDK.
What am I doing wrong? It seems to be so easy to map /dev/mem with C/C++ or Python, I see the examples all around the RPI forum. I'd really like to try to stay pure Java as long as possible. In theory there's also a risk of a bug in the newer JDKs, but I want to get some second opinion before I file a bug, especially that I'm not proficient with low-level Linux.
Thanks,
Peter

Byte array to some sort of Java/Scala image. Performance considerations

I am writing an FTP server that receives images and then resizes and uploads them.
My current process (pseudocode) is as follows:
val imagesAsBytes: Array[Byte] = ...
val bufferedImage: BufferedImage = ImageIO.read(new ByteArrayInputStream(bytes))
uploadImage(bufferedImage)
That's the gist of it. I've left out the resizing because it's not important. Essentially, I serialise the Array[Byte] into a BufferedImage using the ImageIO module, and then resize it.
I have done some profiling, and I've noticed that creating a BufferedImage using ImageIO is horribly slow.
If I just upload the Array[Byte], I can achieve about 4x the throughput, than if I actually try and convert it to a BufferedImage. The reason I can't just upload the Array[Byte], is that I do need to resize the image. I am not tied to BufferedImage, it's just my first attempt.
Does anyone know of some ideas I can use to speed this up? Is there a better format I should be using over BufferedImage?
I've already considered pushing resizing out to a separate microservice and perform it asynchronously, but it's not an option for the first release.
Edit: I have reviewed this question, and am aware of this: ImageIO.setUseCache(false)
I would suggest looking at more actively supported library (last release 4.0 at Feb 2020) like scrimage. Under the hood it uses java.awt.*. At least in case of any issues you will be able to address them and get them resolved, moreover using more "scalish" API.
Hope it helps.
Use external program from ImageMagic.

On Swing image formats and service providers

I originally wanted to know which image formats are supported by Swing, platform-independently. Swing is my preferred toolkit, and I am trying to research the decision of which image format to use in my hobby work. Once I know which formats are inherently supported, I will decide whether it is simpler to work with one of them or if it is simpler to find and work with appropriately-licensed third-party library. However, I've hit a snag and I want help to sort out where I went wrong.
I've done some digging through the javax.imageio API and I have unsuccessfully attempted to determine what formats are supported on my PC, by doing the following:
package iiortester;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
public class IIORTester {
public static void main(String[] args) {
IIORegistry iioRegistry = IIORegistry.getDefaultInstance();
ImageReaderSpi imageReaderSpi =
iioRegistry.getServiceProviderByClass(ImageReaderSpi.class);
System.out.println(imageReaderSpi); // output is null
}
}
The default IIORegistry instance did report the category ImageReaderSpi when I made a call to ServiceRegistry.getCategories(), but when I attempt to acquire the image reader service provider interface in the code above, I receive a null reference. This is likely a hint that I'm not going about this the right way, and my plan to query ImageReaderWriterSpi.getFormatNames() has been foiled. However, even if it was successful in reporting supported image formats, that would not tell me whether those formats are supported inherently in any JVM or whether they're supported because I am running a Windows 7 JVM.
Am I looking in the wrong place? Probably. Where should I have been looking? I don't know. Does the JVM even have built-in ImageIO service providers, or is this only for third-party library registration? Where in the Swing API can software look up supported image formats?
GIF, JPEG and PNG should be supported in all JREs.
As a more specific answer to what any particular JRE supports, see the MediaTypes source code which provides lots of info.
To see the supported image formats:
To see which files can be read: ImageIO.getReaderFormatNames();
To see which files can be written: ImageIO.getWriterFormatNames();
JOptionPane.showMessageDialog(null,"Java Image Formats:\r\nread:\t"+Arrays.toString(ImageIO.getReaderFormatNames())+"\r\nwrite:\t"+Arrays.toString(ImageIO.getWriterFormatNames()));
Image Types which are registered in Java will show up in the list.

load jpeg image with exif and/or corrupted meta data

Is there any way in java to load a jpeg image with exif meta data?
I don't need the exif data(Or any other metadata), but the problem is that JavaIO.read gives me:
javax.imageio.IIOException: Unsupported Image Type
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:995) ~[na:1.7.0_09]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:966) ~[na:1.7.0_09]
at javax.imageio.ImageReader.read(ImageReader.java:940) ~[na:1.7.0_09]
If i try to read the images.
On an related note: We also sometimes get jpg images with other meta data which ImageIO.read can't handle either, so I would really like a jpeg reader which don't try to read/handle/understand any metadata. But I have been unable to find any.
The image data itself are newer damaged, and all other software(Gimp, Firefox and so on) can read and display the images correct.
I even tried the following, but it still can't read the images.
Iterator readers = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = (ImageReader)readers.next();
ImageInputStream iis = ImageIO.createImageInputStream(sourceFile);
reader.setInput(iis,true,true);
BufferedImage image = reader.read(0);
There are not many options available here. If one wants to solve a specific issue, it can be done without a doubt in Java. Yet, if you just seek a library to do some image manipulation in your project without the need to be a graphics specialist programmer, then the open source java libraries is not yet on par with the commercial ones , or with the non Java ones.
A simple search on SO will reveal the state. Therefore, If you need to have something reliable with minimal surprises on a production environment, you will most probably (in my opinion) end up with one of the following (with the state as of the date of this post):
1- a commercial product, for example Snowbound RasterMaster
2- use a tool like ImageMagick (or GraphicsMagick) with the option of using a java Interface for them, for example im4java or JMagick.
As for your problem, you mentioned using mogrify. you can as well set the color space for the output image and it might solve your problem: ImageMagick Colorspace
so maybe you can try : "/usr/bin/mogrify -strip -colorspace sRGB" on your jpg files.

Moving files after failed validation (Java)

We are validating XML files and depending on the result of the validation we have to move the file into a different folder.
When the XML is valid the validator returns a value and we can move the file without a problem. Same thing happens when the XML is not valid according to the schema.
If however the XML is not well formed the validator throws an exception and when we try to move the file, it fails. We believe there is still a handle in the memory somewhere that keeps hold of the file. We tried putting System.gc() before moving the file and that sorted the problem but we can't have System.gc() as a solution.
The code looks like this. We have a File object from which we create a StreamSource. The StreamSource is then passed to the validator. When the XML is not well formed it throws a SAXException. In the exception handling we use the .renameTo() method to move the file.
sc = new StreamSource(xmlFile);
validator.validate(sc);
In the catch we tried
validator.reset();
validator=null;
sc=null;
but still .renameTo() is not able to move the file. If we put System.gc() in the catch, the move will succeed.
Can someone enlight me how to sort this without System.gc()?
We use JAXP and saxon-9.1.0.8 as the parser.
Many thanks
Try creating a FileInputStream and passing that into StreamSource then close the FileInputStream when you're done. By passing in a File you have lost control of how/when to close the file handle.
When you set sc = null, you are indicating to the garbage collector that the StreamSource file is no longer being used, and that it can be collected. Streams close themselves in their destroy() method, so if they are garbage collected, they will be closed, and therefore can be moved on a Windows system (you will not have this problem on a Unix system).
To solve the problem without manually invoking the GC, simply call sc.getInputStream().close() before sc = null. This is good practice anyway.
A common pattern is to do a try .. finally block around any file handle usage, eg.
try {
sc = new StreamSource(xmlFile);
// check stuff
} finally {
sc.getInputStream().close();
}
// move to the appropriate place
In Java 7, you can instead use the new try with resources block.
Try sc.getInputStream().close() in the catch
All the three answers already given are right : you must close the underlying stream, either with a direct call to StramSource, or getting getting the stream and closing it, or creating the stream yourself and closing it.
However, I've already seen this happening, under windows, since at least three years : even if you close the stream, really every stream, if you try to move or delete the file, it will throw exception .. unless ... you explicitly call System.gc().
However, since System.gc() is not mandatory for a JVM to actually execute a round of garbage collection, and since even if it was the JVM is not mandated to remove all possible garbage object, you have no real way of being sure that the file can be deleted "now".
I don't have a clear explanation, I can only imagine that probably the windows implementation of java.io somehow caches the file handle and does not close it, until the handle gets garbage collected.
It has been reported, but I haven't confirmed it, that java.nio is not subject to this behavior, cause it has more low level control on file descriptors.
A solution I've used in the past, but which is quite a hack, was to :
Put files to delete on a "list"
Have a background thread check that list periodically, calla System.gc and try to delete those files.
Remove from the list the files you managed to delete, and keep there those that are not yet ready to.
Usually the "lag" is in the order of a few milliseconds, with some exceptions of files surviving a bit more.
It could be a good idea to also call deleteOnExit on those files, so that if the JVM terminates before your thread finished cleaning some files, the JVM will try to delete them. However, deleteOnExit had it's own bug at the time, preventing exactly the removal of the file, so I didn't. Maybe today it's resolved and you can trust deleteOnExit.
This is the JRE bug that i find most annoying and stupid, and cannot believe it is still in existence, but unfortunately I hit it just a month ago on windows Vista with latest JRE installed.
Pretty old, but some people may still find this question.
I was using Oracle Java 1.8.0_77.
The problem occurs on Windows, not on Linux.
The StreamSource instanciated with a File seems to automatically allocate and release the file resource when processed by a validator or transformer. (getInputStream() returns null)
On Windows moving a file into the place of the source file (deleting the source file) after the processing is not possible.
Solution/Workaround: Move the file using
Files.move(from.toPath(), to.toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
The use of ATOMIC_MOVE here is the critical point. Whatever the reason ist, it has something to do with the annoying behavior of Windows locking files.

Categories

Resources