1

I'm currently writing a JNI accelerator library to replace Java methods on some systems (Linux x64 and macOS).

I have a working code written in Kotlin/Java with JavaCV 3.4.2

I've created a JNI library that do the same job to avoid multiple back and forth between JVM and JNI.

Example:

Kotlin side (JVM + JavaCV) without acceleration :

override fun process(image: Mat): FeaturesDetectorResult {
    val faceDetection = Dlib.faceDetection(image)
    ...
}

JNI accelerator part:

Kotlin

@ByVal
protected external fun Process(imagePtr: Long, lines: List<opencv_core.Rect>): opencv_core.Mat?;

// method of class overriding the native method
override fun process(image: opencv_core.Mat): FeaturesDetectorResult {
    val lines = ArrayList<opencv_core.Rect>()
    val rotatedMat = Process(image.address(), lines)
    return FeaturesDetectorResult(rotatedMat, lines)
}

C++

JNIEXPORT jlong JNICALL Java_fr_tessi_bmd_image_accel_MserFeaturesDetectorNative_Process
  (JNIEnv *env, jobject self, jlong imagePtr, jobject list) {

At the end of my C++ method, I am stuck in converting a native cv::Mat to its java counterpart opencv_core.Mat (no constructor with native address is present).

I've looked at the sources generated by javacpp and it seems that native objects are treated as jlong. All my tests this way lead to a crash.

Anybody knows how to pass org.bytedeco.javacpp objects back and forth with a homemade JNI library ?

EDIT:

I've found a partial workaround to create opencv_mat.Mat from cv::Mat:

//Safety checks removed for simplicity
#define ptr_to_jlong(a) ((jlong)(uintptr_t)(a))
Mat* toReturnToJava;
jclass pointerClass = env->FindClass("org/bytedeco/javacpp/Pointer"); 
jfieldID addressFld = env->GetFieldID(pointerClass, "address", "J");
jobject pointerObj = env->AllocObject(pointerClass);
env->SetLongField(pointerObj, addressFld, ptr_to_jlong(toReturnToJava));

The Mat is returned to Java as a jlong while the Java side declares the return value as opencv.Mat.

The same type of code does not seem to work for Rect (I used the constructor Rect(x, y, w, h) instead).

Xvolks
  • 2,065
  • 1
  • 21
  • 32
  • Although we can use JNI for this, you probably want to set the `long address` field from Java. We can set that `Pointer.address` by creating a subclass, of `Mat`, in this case. Does that sound like what you need to do? – Samuel Audet Dec 24 '18 at 11:55
  • As I wrote in edit, I solved the problem an other way. But the memory management is still an issue. If one call deallocate often the code is much faster than letting the Deallocator do its job. The new PointerScope is great’ish. It also register and free static values lazy created by the code. – Xvolks Dec 24 '18 at 12:14
  • Yeah, it's not perfect, there's no reliable way to know when something gets assigned to a field or something, but we can easily call `PointerScope.detach()` manually and it works well enough, for example, with the callbacks in https://github.com/bytedeco/javacv/blob/master/src/main/java/org/bytedeco/javacv/FFmpegFrameGrabber.java . If there's anything that you still need an answer for though, let me know! – Samuel Audet Dec 25 '18 at 00:41

0 Answers0