2

With a normal Visual Studio C++ project i use the following code to load a shape predictor file into dlib shape_predictor variable:

    dlib::shape_predictor spredictor;
    //calling the file with a relative path
    dlib::deserialize("sp68fl.dat") >> spredictor;

However, using JNI in Android, i load this file from my activity and pass it as an byte array. So i have a jbyteArray which is the shape_predictor_68_face_landmarks.dat file used for face detection with Dlib.

I found an overloaded method deserialize(serializable_type& item, std::istream& in) but can't figure out how to use it (maybe i have to convert jbyteArray to this istream).

How can i load this file to the dlib::shape_predictor from the jbyteArray?

EDIT:

This is what i have tried so far:

#include <jni.h>
#include <dlib\image_processing.h>
#include <dlib\image_processing\frontal_face_detector.h>
#include <dlib\image_processing\render_face_detections.h>
#include <dlib\image_processing\generic_image.h>
#include <dlib\image_processing\full_object_detection.h>
#include <dlib\opencv.h>
#include <dlib\geometry.h>
#include <dlib\image_transforms.h>
#include <dlib\serialize.h>
#include <dlib\gui_widgets.h>
#include <dlib\image_io.h>

extern "C" {
struct membuf : std::streambuf
{
    membuf(char* begin, char* end) {
        this->setg(begin, begin, end);
    }
};


JNIEXPORT jstring JNICALL
Java_br_edu_infnet_test16_MainActivity_stringFromJNI(JNIEnv *env, jobject daobj,
                                                     jbyteArray fileBytes,
                                                     jint fileSize) {
    dlib::shape_predictor spredictor;

    jboolean isCopy = true;
    jbyte * a = env->GetByteArrayElements(fileBytes, &isCopy);
    char* teste = (char *) a;

    std::ifstream in;
    //stream is not loading? stream size is zero after read method
    in.read(teste, fileSize);

    try {
        dlib::deserialize(spredictor, in);
    } catch (dlib::serialization_error &serializationError) {
        //deserialize method fails. Dlib error: deserializing object of type int
        std::cout << "ERROR: " << serializationError.what();
    }

    in.close();
    env->ReleaseByteArrayElements(fileBytes, a, JNI_ABORT);

    const char *cppString = "Diego";
    jstring javaString = env->NewStringUTF(cppString);
    return javaString;
}
}

The activity sending the byte array:

package br.edu.infnet.test16;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
//        tv.setText(stringFromJNI());
        byte[] buffer = null;
        try {
            InputStream inputStream = getAssets().open("sp68fl.dat");
            int size = inputStream.available();
            buffer = new byte[size];
            inputStream.read(buffer);
            inputStream.close();
            stringFromJNI(buffer, size);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("ERRO", "DEU EEEEEEERRO: " + e.getMessage());
        }
    }

    public native String stringFromJNI(byte[] fileBytes, int fileSize);
}

Looks like the problem is that ifstream is not being loaded using variable teste, which is a char pointer. This file has approximately 100MB size.

Diego Victor de Jesus
  • 2,575
  • 2
  • 19
  • 30

2 Answers2

3

You could open the file in C++. The catch is that in Android you need full path to the file name, no current directory that you can rely upon. It may be easier to use Java APIs to prepare the full path and pass the string to C++ instead of sending the buffer.

If that's impossible, you must convert the jbyteArray to char[] (see How to convert jbyteArray to native char* in jni?), and then create a std::istream as in Get an istream from a char*.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Thanks, Alex. I couldn't get the deserializer to work using neither istream nor ifstream. I have tried using istream with the suggested membuf struct, but it didn't work. – Diego Victor de Jesus Oct 03 '18 at 09:40
0

I was having the same problem, I only granded permission to read and write on storage and worked. Try add on manifest:

And call for the permission

requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION);

I saved the files on root and used the absolute path to located.