Search code examples
imagedicomjavax.imageiodcm4che

ImageIO.read is sporadically reading an image file


I am using the following Java code to read a Dicom image, trying to convert it later to JPEG file. When the reading happens in the line

tempImage = ImageIO.read(dicomFile);

, the returned image either has an image type of 10 or something else, like 0 or 11. The problem here is that the reading happens sporadically. Sometimes the returned image type is 10, and sometimes it is not. When the returned image type is 10, the writing of the converted JPEG file succeeds and returns true and I get my JPEG file. However, when the returned image type is not 10, the writing fails and returns false, and doesn't produce any file. This is the statement I am using for writing:

writerReturn = ImageIO.write(image, "jpeg", new File(tempLocation + studyId + File.separator + seriesUID + File.separator + objectId + thumbnail+ ".jpeg"));

I have spent long time trying to figure out why this sporadic behaviour is happening but couldn't reach to anything. Could you please help?


Solution

  • From extra information in the comments, we have discovered that the application is running in a Glassfish server, and that there are two ImageIO plugins installed, both capable of reading DICOM images. The problem is not really related to reading, but sometimes writing the decoded image to JPEG fails.

    The service providers for mentioned plugins are org.dcm4cheri.imageio.plugins.DcmImageReaderSpi and org.dcm4che2.imageioimpl.plugins.dcm.DicomImageReaderSpi, but only the latter (DicomImageReaderSpi) seems to work. This is because it produces an 8 bits per sample BufferedImage, which is what the JPEGImageWriter is able to write (the DcmImageReaderSpi creates a 16 bit per sample image, which can't be written as a JFIF JPEG and thus unsupported by the JPEGImageWriter).

    Because of the (by default) unspecified (read: unpredictable) order of ImageIO plugins, the result is that sometimes you get the 8 bps version and sometimes the 16 bit version of the image, and the end result is that sometimes the conversion won't work.


    Now, the good news is that we can set an explicit order of ImageIO plugins, or we can unregister plugins at runtime, to get a stable predictable result. What is the better of these options, depends on wether there are other code on your server that depends on the undesired plugin or not. If you don't need it, unregister it.

    The code below shows both of the above options:

    // Get the global registry
    IIORegistry registry = IIORegistry.getDefaultInstance();
    
    // Lookup the known providers
    ImageReaderSpi goodProvider = lookupProviderByName(registry, "org.dcm4che2.imageioimpl.plugins.dcm.DicomImageReaderSpi");
    ImageReaderSpi badProvider = lookupProviderByName(registry, "org.dcm4cheri.imageio.plugins.DcmImageReaderSpi");
    
    if (goodProvider != null && badProvider != null) {
        // If both are found, EITHER
        // order the good provider BEFORE the bad one
        registry.setOrdering(ImageReaderSpi.class, goodProvider, badProvider);
    
        // OR
        // un-register the bad provider
        registry.deregisterServiceProvider(badProvider);
    }
    

    // New and improved (shorter) version. :-)
    private static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName) {
        try {
            return (T) registry.getServiceProviderByClass(Class.forName(providerClassName));
        }
        catch (ClassNotFoundException ignore) {
            return null;
        }
    }
    

    You should also make sure you run this code only once, for a container-based application, a good time is at application context start-up.

    With the above solution, ImageIO.read(...) will always use the good plugin, and ImageIO.write(...) will work as expected.