0

I have a native c++ function that acts on a cv::Mat (opencv) object passed from Java through JNI (OpenCV4Android).

This is my function declaration:

extern "C" jboolean Java_com_test_JNIActivity_track(JNIEnv *env, jobject obj, jlong inMatGr, jlong inMatRgba, jint currFrame);

and this is the code where I access and print passed arguments:

extern "C" jboolean Java_com_test_JNIActivity_track(JNIEnv *env, jobject obj, jlong inMatGr, jlong inMatRgba, jint currFrame)
{
   ALOG("Native: Rgba@%.8x, Gray@%.8x", inMatRgba, inMatGr);

   cv::Mat& captured_image = *(cv::Mat *)inMatRgba;
   cv::Mat_<uchar>& grayscale_image = *(cv::Mat_<uchar> *)inMatGr;
   ...
}

The Java declaration part is set as follows:

private native boolean track(long grayAddr, long rgbaAddr, int currFrameNum);

and this is OnCameraFrame() method implementation:

public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    mRgba = inputFrame.rgba();
    mGray = inputFrame.gray();
    Log.d(TAG_LOG, "Java: Rgba@0x" + Long.toString(mRgba.getNativeObjAddr(),16) +
            ", Gray@0x" + Long.toString(mGray.getNativeObjAddr(),16));
    track(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr(), currFrameNum++);
    //Log.d(TAG_LOG, "Java: Frame(" + currFrameNum + ')');
    return inputFrame.rgba();
}

However, I get different values(when inside the native function) for the two arguments from what I set on Java part (Mat.getNatvieObjAddr()).

These are the values in both sides of JNI interface:

In Java: 1st argument = 0x405ab288, 2nd argument = 0x557ab9d8
In Native: 1st argument = 0x00000000, 2nd argument = 0x405ab288

The last argument is a jint and gives the correct value, could this issue have something to do with jlong having different sizes on different architectures (the code is run on armv7a processor).

I appreciate any help. Thanks.

EHH
  • 151
  • 7

1 Answers1

1

I've figured it out myself and I am sharing to whom is in my situation.

private native boolean track(long grayAddr, long rgbaAddr, int currFrameNum);

Java's long type is 8 bytes in size, so does jlong (typedef as either _int64_t on c++ or long long on C, check jni.h).

cv::Mat& captured_image = *(cv::Mat *)inMatRgba;

inMatRgba is a pointer to a cv::Mat object. On 64-bit machines, a pointer is 8 bytes (same as jlong) so it should work well; however, on 32-bit machines, a pointer is 4 bytes, and only the low 4 bytes of inMatRgba are considered the address to cv::Mat object. This is a problem if the 32bit machine is big endian (the low 4 bytes hold value 0 in this case).

Solution:

I had to define different assignment for each case:

int *ptrRgba = NULL;   
int *ptrGr = NULL;

if (4 == sizeof(int *) && is_big_endian()) {
   ptrRgba = (int *)&inMatRgba;
   ptrGr = (int *)&inMatGr;
}

cv::Mat& captured_image = ptrRgba ? *(cv::Mat *)(*(ptrRgba+1)) : *(cv::Mat *)inMatRgba;
cv::Mat_<uchar>& grayscale_image = ptrGr ? *(cv::Mat_<uchar> *)(*(ptrGr+1)) : *(cv::Mat_<uchar> *)inMatGr;
EHH
  • 151
  • 7