Search code examples
javaandroidc++opencvjava-native-interface

How to display Mat image on android emulator? Using NDK


I would like to display a Mat image on the android emulator. I am passing an assetManager from java to c++ and loading an image as an asset. I then perform an AAsset_read, and save the image data into a char* buffer, and convert to Mat using cv::imdecode. From here, how do I go about adding the Mat image to the emulator?

In the default stringFromJNI method, they seem to use return->envNewStringUTF(hello.c_str()); which displays the text:

Java_com_example_user_project_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {

std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());

So how do I go about doing this with a Mat image?

edit: here is my c++ code :

JNIEXPORT jintArray JNICALL     Java_com_example_user_activity_MainActivity_generateAssets(JNIEnv* env,jobject thiz,jobject assetManager) {

AAsset* img;

AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
    AAsset *asset = AAssetManager_open(mgr, filename, AASSET_MODE_UNKNOWN);

    if(strcmp(filename, "hi.jpg")==0 ) {
        img = asset;
    }
}
long sizeOfImg = AAsset_getLength(img);
char* buffer = (char*) malloc (sizeof(char)*sizeOfImg);
AAsset_read(img, buffer, sizeOfImg);


std::vector<char> data(buffer, buffer + sizeOfImg);

cv::Mat h = cv::imdecode(data, IMREAD_UNCHANGED);

jintArray resultImage = env->NewIntArray(h.total());
jint *_data = new jint[h.total()];
for (int i = 0; i < h.total(); i++) {
    char b = h.data[4 * i];
    char g = h.data[4 * i + 1];
    char r = h.data[4 * i + 2];
    char a = 255;
    _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
               (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
}
env->SetIntArrayRegion(resultImage, 0, h.total(), _data);
delete[]_data;
return resultImage;

}

And the java code:

    assetManager = getAssets();
    resultImage = generateAssets(assetManager);

    Bitmap imageinjava = Bitmap.createBitmap(resultImage, 100,100, Bitmap.Config.ARGB_8888);

    ImageView tv1;
    tv1= (ImageView) findViewById(R.id.image);
    tv1.setImageBitmap(imageinjava);

Solution

  • In order to convert your native Mat image you can do that:

    JNIEXPORT jintArray JNICALL  Java_com_example_user_activity_MainActivity_generateAssets(JNIEnv* env, jobject thiz, jobject assetManager) {
    AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
    AAsset *img = AAssetManager_open(mgr, "hi.jpg", AASSET_MODE_UNKNOWN);
    
    long sizeOfImg = AAsset_getLength(img);
    char* buffer = (char*)AAsset_getBuffer(img);
    
    std::vector<char> data(buffer, buffer + sizeOfImg);
    
    cv::Mat h = cv::imdecode(data, IMREAD_UNCHANGED);
    
    jintArray resultImage = env->NewIntArray(h.total());
    jint *_data = new jint[h.total()];
    for (int i = 0; i < h.total(); i++) {
        char b = h.data[h.channels() * i];
        char g = h.data[h.channels() * i + 1];
        char r = h.data[h.channels() * i + 2];
        char a = 255;
        _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
                   (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
    }
    env->SetIntArrayRegion(resultImage, 0, h.total(), _data);
    delete[]_data;
    return resultImage;
    

    }

    And in your java code smth like that

        int[] result = generateAssets(getAssets());
        Bitmap bitmap = Bitmap.createBitmap(result, yourimagewidth, yourimageheight, Bitmap.Config.ARGB_8888);
    

    If you want to pass native Mat object between c++ and java - return pointer to your Mat and pass it later into native function;

    Mat *mat = new Mat(100, 100);
    return (jlong) mat;