OpenCV tiff to PNG REST service produces corrupted output intermittently [closed]

asked 2018-08-20 11:23:06 -0500

I have developed a REST service to convert tiff image to PNG format. The REST service reads the tiff file from local machine and calls the required OpenCV methods to perform the conversion and resize to the requested height and width. The high level steps are 1)Decompress tiff source as a gray scale image because most of tiffs are CCIT1.4 images 2)Scale to requested width and height 3)Encode the image into a PNG

The problem is when there are multiple REST requests coming in, the output PNG is getting corrupted. On reissuing the same request the output PNG appears fine. I believe the problem is multiple Opencv processes triggered via OpenCV JNI is causing this. Before thinking this could be a bug, is there anything basic that should be added or setup to make Opencv work in a multi-threaded environment.

Library version: 3.4.1 OS: RHEL 7.4

The code used is given below:

public class TiffToPNGConverter {

    private static final String PNG_EXTENSION = ".png";
    private static final String ENCODE_CONVERSION_ERROR_MSG = "Opencv failed to encode image";
    private static final String CREATE_IMAGE_ERROR_MSG = "Opencv failed to create scaled image";
    private static final String DECODE_CONVERSION_ERROR_MSG = "Opencv in-memory image decode failed";

    private static final int IMAGE_ROW = 1;
    private static final int DEFAULT_IMAGE_ROW = 0;
    private static final int DEFAULT_IMAGE_COLUMN = 0;
    private static final int DEFAULT_SCALE_FACTOR_XAXIS = 0;
    private static final int DEFAULT_SCALE_FACTOR_YAXIS = 0;
    /*
     * For gray scale text images, not seeing any difference in image size over compression values 1-9 inclusive.
     */
    private static final int[] PNG_COMPRESSIONS_PARAMS = { CV_IMWRITE_PNG_COMPRESSION, 1, 0 };

    static {
        try {
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        } catch (final UnsatisfiedLinkError e) {
            log.error("Unable to load Native Opencv library {}", Core.NATIVE_LIBRARY_NAME, e);
        }
    }

    /**
     * Just this method to utilize opencv java library and perform the required conversion
     *
     * @Param sourceTiffData
     *            source tiff image as raw bytes
     * @Param pngWidth
     *            requested conversion width
     * @return scaled png format
     */
    public byte[] run(byte[] sourceTiffData, int pngWidth) {
        log.debug("Requested png width {}", pngWidth);

        // step 1: Decompress tiff source as a gray scale image because most of tiffs are CCIT1.4 images
        Mat decompressedSource = decompressTiff(sourceTiffData);
        log.debug("decompression complete");

        // step 2: Scale to requested png width and height
        Mat scaledImage = scaleImage(decompressedSource, pngWidth);
        log.debug("Image scaling complete");

        // step 3: Encode the image into a PNG
        return encodeImageToPNG(scaledImage);
    }

    /**
     * Decompress tiff
     *
     * @Param sourceTiffData
     *            source tiff image as raw bytes
     * @return decompressed tiff as opencv image object
     */
    private Mat decompressTiff(byte[] sourceTiffData) {
        // There is a constructor to add the byte[] directly but that is failing, probably because it requires row and
        // column information which is not available by default
        Mat decompressedSourceHolder = new Mat(IMAGE_ROW, sourceTiffData.length, CV_8UC1);
        decompressedSourceHolder.put(DEFAULT_IMAGE_ROW, DEFAULT_IMAGE_COLUMN, sourceTiffData);

        Mat decompressedSource = Imgcodecs.imdecode(decompressedSourceHolder, CV_LOAD_IMAGE_GRAYSCALE);
        decompressedSourceHolder.release();
        decompressedSourceHolder = null;

        if (decompressedSource == null) {
            throw new TiffToPNGConversionException(DECODE_CONVERSION_ERROR_MSG);
        }
        return decompressedSource;
    }

    /**
     * Scale image to width from request
     *
     * @Param decompressedSource
     *            decompressed tiff image
     * @Param pngWidth
     *            scale to image size requested
     * @return scaled image
     */
    @SuppressWarnings("squid:S2583")
    // Opencv could potentially return a nul Mat object if type of file has some issues
    private Mat scaleImage(Mat decompressedSource, int ...
(more)
edit retag flag offensive reopen merge delete

Closed for the following reason question is not relevant or outdated by yarafatin
close date 2018-09-04 13:56:39.196110

Comments

does it get better, if you omit the resize() ?

berak gravatar imageberak ( 2018-08-21 01:31:19 -0500 )edit

also, the assumption, that opencv returns null Mat's is wrong, you have to check for mat.empty() instead.

berak gravatar imageberak ( 2018-08-21 01:33:44 -0500 )edit

Thank you for the reply and sorry about the delayed response

After removing resize(), the problem no longer occurs. I looked for any existing bugs or solutions related to resize and couldnt find one. Is there a workaround? I am going to try to do the resize after PNG conversion instead of resize and convert.

yarafatin gravatar imageyarafatin ( 2018-08-30 14:24:00 -0500 )edit

I take that back. Just received a bunch of corrupted images even after skipping resize() call. The system isn't heavily loaded when this occurred. I had 100 vusers running. The system configuration is as below - 8 CPUs, 8 GB ram, JVM -XmX is 4G, using Jboss EAP 7.0.9

yarafatin gravatar imageyarafatin ( 2018-08-30 16:21:51 -0500 )edit

It turned out to be an issue with a library that was not compatible with Jboss. This issue has nothing to do with OpenCV. Sorry about the false alarm.

yarafatin gravatar imageyarafatin ( 2018-09-04 13:55:58 -0500 )edit