How to call Jni object in Android Activity?

那年仲夏 提交于 2020-01-06 06:19:11

问题


I am making an Document Scanner. I'm using opencv for image processing. In camera view i'm bounding rectangle on largest contour. Processing part written in Native-lib.cpp. It is detecting largest contour properly. And now i want to capture only boudingRect which is written in native-lib.cpp. So i want object of native-lib in java class. help to get that.

Native-lib.cpp

extern "C"
JNIEXPORT void JNICALL
Java_prisca_ctest_OpenCvCamera_doWithMat(JNIEnv *env, jobject instance, jlong matAddrGr,
                                     jlong matAddrRgba) {
try {
    Mat &image = *(Mat *) matAddrRgba;
    Rect bounding_rect;

    Mat thr(image.rows, image.cols, CV_8UC1);
    cvtColor(image, thr, CV_BGR2GRAY); //Convert to gray
    threshold(thr, thr, 150, 255, THRESH_BINARY + THRESH_OTSU); //Threshold the gray

    vector<vector<Point> > contours; // Vector for storing contour
    vector<Vec4i> hierarchy;
    findContours(thr, contours, hierarchy, CV_RETR_CCOMP,
                 CV_CHAIN_APPROX_SIMPLE); // Find the contours in the image
    sort(contours.begin(), contours.end(),
         compareContourAreas);            //Store the index of largest contour
    bounding_rect = boundingRect((const _InputArray &) contours[0]);

rectangle(image, bounding_rect, Scalar(250, 250, 250) , 5);
} catch (int a) {

}
}

Activity

 protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.cam);
    mOpenCvCameraView = (JavaCameraView) findViewById(R.id.tutorial1_activity_java_surface_view);
    mOpenCvCameraView.setVisibility(View.VISIBLE);

    mOpenCvCameraView.setCvCameraViewListener(this);
    btnCapture = (Button) findViewById(R.id.btnCapture);
    btnCapture.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String timestamp = new SimpleDateFormat("ddMMyyyy_HHmmss", Locale.US).format(new Date());
            File imgFolder = new File(FILE_LOCATION);
            imgFolder.mkdir();
            File image = new File(imgFolder, "Scan" + timestamp + ".jpg");
            String fileName = FILE_LOCATION +
                    "/Scan" + timestamp + ".jpg";
            Toast.makeText(OpenCvCamera.this, image + " saved", Toast.LENGTH_SHORT).show();
            Imgcodecs.imwrite(fileName, mRgba);
        }
    }) ;
}

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    // input frame has RGBA format
    mRgba = inputFrame.rgba();
    mGray = inputFrame.gray();
    doWithMat(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr());
    return mRgba;

}

What should i add above Imgcodecs.imwrite(fileName, mRgba) to crop the matrix and save only boundingRect part?

probably I have to write this before imwrite -

Mat cropped = mRgba.submat( bounding_rect );
            Imgcodecs.imwrite(fileName, cropped);

But i cannot call bounding_rect from Native-lib. How to call it ? Thank u in advance :)


回答1:


You can receive org.opencv.core.Rect from native-lib. The easiest way is to change your native method signature, doWithMat() like this:

private org.opencv.core.Rect mBoundingRect;

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    // input frame has RGBA format
    mRgba = inputFrame.rgba();
    mGray = inputFrame.gray();
    mBoundingRect = doWithMat(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr());
    return mRgba;
}

extern "C"
JNIEXPORT jobject JNICALL
Java_prisca_ctest_OpenCvCamera_doWithMat(JNIEnv *env, jobject instance,
        jlong matAddrGr, jlong matAddrRgba) {

    Mat &image = *(Mat *) matAddrRgba;
    Rect bounding_rect;

    Mat thr(image.rows, image.cols, CV_8UC1);
    cvtColor(image, thr, CV_BGR2GRAY); // Convert to gray
    threshold(thr, thr, 150, 255, THRESH_BINARY + THRESH_OTSU); //Threshold the gray

    vector<vector<Point> > contours; // Vector for storing contour
    vector<Vec4i> hierarchy;
    findContours(thr, contours, hierarchy, CV_RETR_CCOMP,
        CV_CHAIN_APPROX_SIMPLE); // Find the contours in the image
    sort(contours.begin(), contours.end(),
        compareContourAreas);    // Store the index of largest contour
    bounding_rect = boundingRect((const _InputArray &) contours[0]);

    rectangle(image, bounding_rect, Scalar(250, 250, 250) , 5);

    jclass rectClass = env->FindClass("org/opencv/core/Rect");
    jmethodID rectCtorID = env->GetMethodID(rectClass, "<init>", "(IIII)V");
    return env->NewObject(rectClass, rectCtorID, bounding_rect.x, bounding_rect.y, bounding_rect.width, bounding_rect.height);
}

As you can see, I removed try ... catch from the native code; I don't think it can really help, especially when you try to catch an int, instead of exception.

Note that extraction of rectClass and rectCtorID are expensive operations, therefore it's smart to cache these values:

static jclass rectClass = nulltr;
ststic jmethodID rectCtorID = 0;
if (rectCtorID == 0) {
    rectClass = env->NewGlobalRef(env->FindClass("org/opencv/core/Rect"));
    rectCtorID = env->GetMethodID(rectClass, "<init>", "(IIII)V");
}

Note that we need a global reference to Java class, but the method ID is just an int.


Another optimization that I can suggest is to use matAddrGr. If I understand correctly, OpenCV will prepare both matrices for onCameraFrame(), so you there is probably no need to convert RGB to gray.




回答2:


here, you have two options first one you need to process byte of the camera frame using JNI and then after you need to fetch processed pixels and set into the camera preview. Then after using canvas crop bitmap. The second one you need to follow this code for the image cropping.

Mat image = ...; // your image
Mat crop(image, bounding_rect); 
// NOTE: this is only give refrence to you.
Mat output = crop.clone();



回答3:


You need to do a couple of things to be able to call Java_prisca_ctest_OpenCvCamera_doWithMat in Java code.

Do you have CMakeList.txt or something similar where you decide, what files will be part of your compilation?

If so, you can present your 'native-lib.cpp' in Java code like this:

static { System.loadLibrary("native-lib"); }

Then you have to present your native functions to Java code. It is done like this:

public native void doWithMat(long matAddrGr, long matAddrRgba);

Finally you can call your native function anywhere in the same file you have presented that function. Just call e.g.:

doWithMat(1, 2);

But like you might have noticed, doWithMath() is void type function, so it does not return anything. You just simply cant "access object" from C++ in Java code. Instead you have to have getter to transfer object to Java side or return Rect from function doWithMath().

Unfortunately you can't return Rect straight from C++ code, since you cannot specify the function to return C++ types. Instead what you should do IMHO is to get data from Rect (coordinates or whatever it has) and return them in array, and then handle that data in Java side.

I recommend to go through this hello-jni tutorial so you get the idea of how to do stuff in JNI: https://developer.android.com/ndk/samples/sample_hellojni.html



来源:https://stackoverflow.com/questions/47511956/how-to-call-jni-object-in-android-activity

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!