Ask Your Question
0

minMaxIdx missing in the JNI interface?

asked 2016-04-14 01:32:15 -0500

Hi,

I have downloaded the 3.1.0 version of OpenCV and I use the JNI bindings in one of my project. My problem is that I don't find minMaxIdx in Core. Only minMaxLoc is available, but this function is limited to 2-dimensionnal arrays.

Is there any technical reason for minMaxIdx to be excluded of the JNI interface?

Thanks.

edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted
0

answered 2016-04-18 02:47:50 -0500

If someone has the same problem than me, my solution was to edit the Python script which generates the C++ and Java code of the JNI binding: modules/java/generator/gen_java.py.

I added the following code after the declaration of minMaxLoc: line 413, after }, # minMaxLoc

'minMaxIdx' : {
            'j_code'   : """
    // manual port
    public static class MinMaxIdxResult {
        public double minVal;
        public double maxVal;

        public MinMaxIdxResult() {
            minVal=0; maxVal=0;
        }
    }

    // C++: minMaxIdx(Mat src, double* minVal, double* maxVal=0, int* minIdx=0, int* maxIdx=0, InputArray mask=noArray())

    //javadoc: minMaxIdx(src, mask)
    public static MinMaxIdxResult minMaxIdx(Mat src, MatOfInt minIdx, MatOfInt maxIdx, Mat mask) {
        MinMaxIdxResult res = new MinMaxIdxResult();
        long minIdxNativeObj=0;
        if (minIdx != null) {
            minIdxNativeObj=minIdx.nativeObj;
        }
        long maxIdxNativeObj=0;
        if (maxIdx != null) {
            maxIdxNativeObj=maxIdx.nativeObj;
        }
        long maskNativeObj=0;
        if (mask != null) {
            maskNativeObj=mask.nativeObj;
        }
        double resarr[] = n_minMaxIdxManual(src.nativeObj, minIdxNativeObj, maxIdxNativeObj, maskNativeObj);
        res.minVal=resarr[0];
        res.maxVal=resarr[1];
        return res;
    }

    //javadoc: minMaxIdx(src, minIdx, maxIdx)
    public static MinMaxIdxResult minMaxIdx(Mat src, MatOfInt minIdx, MatOfInt maxIdx) {
        return minMaxIdx(src, minIdx, maxIdx, null);
    }

    //javadoc: minMaxIdx(src, minIdx)
    public static MinMaxIdxResult minMaxIdx(Mat src, MatOfInt minIdx) {
        return minMaxIdx(src, minIdx, null, null);
    }

    //javadoc: minMaxIdx(src)
    public static MinMaxIdxResult minMaxIdx(Mat src) {
        return minMaxIdx(src, null, null, null);
    }

""",
            'jn_code'  :
"""    private static native double[] n_minMaxIdxManual(long src_nativeObj, long minIdx_nativeObj, long maxIdx_nativeObj, long mask_nativeObj);\n""",
            'cpp_code' :
"""
// C++: minMaxIdx(Mat src, double* minVal, double* maxVal=0, int* minIdx=0, int* maxIdx=0, InputArray mask=noArray())
JNIEXPORT jdoubleArray JNICALL Java_org_opencv_core_Core_n_1minMaxIdxManual (JNIEnv*, jclass, jlong, jlong, jlong, jlong);

JNIEXPORT jdoubleArray JNICALL Java_org_opencv_core_Core_n_1minMaxIdxManual
  (JNIEnv* env, jclass, jlong src_nativeObj, jlong minIdx_nativeObj, jlong maxIdx_nativeObj, jlong mask_nativeObj)
{

    try {
        LOGD("Core::n_1minMaxIdx()");
        jdoubleArray result;
        result = env->NewDoubleArray(2);
        if (result == NULL) {
            return NULL; /* out of memory error thrown */
        }

        Mat& src = *((Mat*)src_nativeObj);

        double minVal, maxVal;
        int minIdx[src.dims];
        int maxIdx[src.dims];

        if (mask_nativeObj != 0) {
            Mat& mask = *((Mat*)mask_nativeObj);
            minMaxIdx(src, &minVal, &maxVal, minIdx, maxIdx, mask);
        } else {
            minMaxIdx(src, &minVal, &maxVal, minIdx, maxIdx);
        }

        if (minIdx_nativeObj != 0) {
            Mat& minIdx_mat = *((Mat*)minIdx_nativeObj);
            for(int i = 0; i < src.dims; i++) {
                minIdx_mat.at<int>(i) = minIdx[i];
            }
        }
        if (maxIdx_nativeObj != 0) {
            Mat& maxIdx_mat = *((Mat*)maxIdx_nativeObj);
            for(int i = 0; i < src.dims; i++) {
                maxIdx_mat.at<int>(i) = maxIdx[i];
            }
        }

        jdouble fill[2];
        fill[0]=minVal;
        fill[1]=maxVal;

        env->SetDoubleArrayRegion(result, 0, 2, fill);

    return result;

    } catch(const cv::Exception& e) {
        LOGD("Core::n_1minMaxIdx() catched cv::Exception: %s", e.what());
        jclass je = env->FindClass("org/opencv/core/CvException");
        if(!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return NULL;
    } catch (...) {
        LOGD("Core::n_1minMaxIdx() catched unknown exception (...)");
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {core::minMaxIdx()}");
        return NULL;
    }
}

""",
        }, # minMaxIdx

This code could be greatly improved by merging the structures MinMaxLocResult and MinMaxIdxResult, but I didn't want to change the code of minMaxLoc.

edit flag offensive delete link more
0

answered 2016-04-14 01:41:43 -0500

berak gravatar image

updated 2016-04-14 02:32:07 -0500

yes, unfortunately this function is missing from the java interface.

"Is there any technical reason for minMaxIdx to be excluded"

yes. the wrapper mechanism can't handle raw c++ pointers, thus it omits any function like that.

"minMaxLoc is available"

yes, there was a manual workaround made special for this (it's quite a kludge, imho)

"but this function is limited to 2-dimensionnal arrays."

you still can get the index from the Point, like:

Core.MinMaxLocResult mm = Core.minMaxLoc(img);
Point p = mm.maxLoc;
int idx = p.x + p.y * img.cols;
edit flag offensive delete link more

Comments

1

Hi berak, thanks for your response.

you still can get the index from the Point, like int idx = p.x + p.y * img.rows;

I am not sure this would solve my problem. Basically, I need to get the maximum value of a n-dimensional array. I could do it in Java directly, but it would be far less efficient given that each call to Mat.get is translated into a native call (inducing type wrapping and so on).

Is there any chance that the minMaxLoc workaround will be applied to minMaxIdx in the future?

benjaminbillet gravatar imagebenjaminbillet ( 2016-04-14 02:05:24 -0500 )edit

"I could do it in Java directly," -- does above edit make it a bit more clear ?

berak gravatar imageberak ( 2016-04-14 02:23:37 -0500 )edit
1

Actually, this code would crash if img.dims > 2 (http://code.opencv.org/issues/703). I cannot use split or mixChannels to change my matrix, given that I have only one channel (img.channels = 1) but n dimensions (img.dims = n). This is similar to http://answers.opencv.org/question/26...

A solution would be to map my nd matrix to a 2d one and then infer the index for the additional dimensions. I am a beginner with OpenCV and I will look in the documentation to see if there is an efficient way to do that.

benjaminbillet gravatar imagebenjaminbillet ( 2016-04-14 02:51:19 -0500 )edit

ok, sorry, misread your post then, i assumed 1 or 2 dimenssional input, not multi (n>2).

(i'm not even sure, how you would handle n-dimensional Mat's in java at all)

berak gravatar imageberak ( 2016-04-14 02:57:37 -0500 )edit

That's ok :) I will let you know if I find a solution.

benjaminbillet gravatar imagebenjaminbillet ( 2016-04-14 02:59:53 -0500 )edit

I did not found a solution to convert my 3d X.Y.Z map into a 2d X.(Y.Z) map, except by copying each value (which is slow through the JNI binding).

My solution was to add the minMaxIdx to the JNI binding, using the same workaround as minMaxLoc. As you said, this is quite kludgy but it works. Probably someone forgot to add it when minMaxIdx was introduced, do you think I should write a bug report for this issue?

benjaminbillet gravatar imagebenjaminbillet ( 2016-04-14 08:11:43 -0500 )edit

Question Tools

1 follower

Stats

Asked: 2016-04-14 01:32:15 -0500

Seen: 493 times

Last updated: Apr 18 '16