美文网首页
android yolov8 实时检测

android yolov8 实时检测

作者: tantanxiqi | 来源:发表于2023-07-27 15:30 被阅读0次

前言

代码全部抄袭自FeiGeChuanShu/ncnn-android-yolov8: Real time yolov8 Android demo by ncnn (github.com)
虽然实现实时检测,但是由于数据全部在jni,java层无法调用,所以改了一下,以便在java层使用检测的数据。

注意

在Yolov8Ncnn类中新增的方法


    public native void detect(boolean start);  //开启或关闭绘制框

    public native void initGlobalObj();    // 全局化 jclass 和 jobject

    public native boolean getDetect();     // 获取 detect 状态

 /**
     * jni 调用的方法
     * 将 native层数据返回给java
     */
    public void processObjects(ArrayList<Object> objects) {
        // 在这里处理处理 jni 传过来 objects
        for (Object obj : objects) {
            // 处理每个 Object 对象
            Log.e(TAG, Thread.currentThread().getId() + "   Yolov8Ncnn======obj:" + obj.toString());
        }
    }

新增了heple类

#ifndef NCNN_ANDROID_YOLOV8_HEPLER_H
#define NCNN_ANDROID_YOLOV8_HEPLER_H

#include <jni.h>
#include "yolo.h"
#include <android/log.h>

class Helper {
public:
    Helper();
    /**
     * 
     * @param vm  用于JVN全局
     */
    void init(JavaVM *vm);
    
    /**
     * 
     * @param g_jclass 全局化jclass
     * @param thiz     全局化jobject
     */
    void initGlobal(jclass g_jclass ,jobject thiz);

    /**
     * 通过jvm 获取当前线程的jniEnv 
     */
    JNIEnv *getJNIEnv();

    /**
     * 将jni层数据传递给java层
     * @param vector 
     */
    void update2Android(std::vector<com::tencent::yolov8ncnn::Object> vector);

    /**
     * 销毁
     */
    void release();

private:
    JavaVM *g_jvm = NULL;
    jclass g_objectClass = NULL;
    jobject g_thiz = NULL;
};

#endif //NCNN_ANDROID_YOLOV8_HEPLER_H
#include "hepler.h"

Helper::Helper() {

}

void Helper::init(JavaVM *vm) {
    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "=======执行Helper::init===========");
    g_jvm = vm;
}

// 调用原生android 方法
void Helper::update2Android(std::vector<com::tencent::yolov8ncnn::Object> objects) {
    JNIEnv *env = this->getJNIEnv();
    // 获取当前线程的 JNIEnv
    if (env == NULL) {
        return; // 获取 JNIEnv 失败
    }
    // 获取 Java 的 ArrayList 类引用
    jclass arrayListClass = env->FindClass("java/util/ArrayList");
    jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", "()V");
    jmethodID arrayListAddMethod = env->GetMethodID(arrayListClass, "add",
                                                    "(Ljava/lang/Object;)Z");

    // 创建 Java 的 ArrayList 对象
    jobject arrayListObject = env->NewObject(arrayListClass, arrayListConstructor);
    if (g_objectClass == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "没有找到java中的Object");
        return;
    }
    jmethodID objectConstructor = env->GetMethodID(g_objectClass, "<init>", "(IF)V");
    env->GetMethodID(g_objectClass, "getLabel", "()I");
    env->GetMethodID(g_objectClass, "getProb", "()F");

    // 将 std::vector<Object> 中的元素逐个转换为 Java 的 Object 对象,并添加到 ArrayList 中
    for (const auto &object: objects) {
        // 创建 Java 的 Object 对象
        jobject objectObject = env->NewObject(g_objectClass, objectConstructor,
                                              object.label,
                                              object.prob);
        // 添加 Object 对象到 ArrayList
        env->CallBooleanMethod(arrayListObject, arrayListAddMethod, objectObject);
        // 删除局部引用
        env->DeleteLocalRef(objectObject);
    }

    // 调用 Java 方法,并将 ArrayList 对象作为参数传递
    jclass javaClass = env->GetObjectClass(g_thiz);
    jmethodID javaMethodID = env->GetMethodID(javaClass, "processObjects",
                                              "(Ljava/util/ArrayList;)V");
    env->CallVoidMethod(g_thiz, javaMethodID, arrayListObject);

    // 删除局部引用
    env->DeleteLocalRef(arrayListObject);


}

// 通过过全局的JVM 找到当前线程jniEnv
JNIEnv *Helper::getJNIEnv() {
    JNIEnv *env;
    int getEnvStat = g_jvm->GetEnv((void **) &env, JNI_VERSION_1_4);
    if (getEnvStat == JNI_EDETACHED) {
        // 如果当前线程未附加到 Java VM,需要调用 AttachCurrentThread 进行附加
        __android_log_print(ANDROID_LOG_DEBUG, "ncnn", " 如果当前线程未附加到 Java VM");
        if (g_jvm->AttachCurrentThread(&env, NULL) != 0) {
            __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "当前线程未附加到 Java VM===失败");
            return NULL;
        }
    } else if (getEnvStat == JNI_EVERSION) {
        // JNI 版本不支持
        __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "当前线程未附加到 JNI 版本不支持");
        return NULL;
    }
    return env;
}

void Helper::initGlobal(jclass g_jclass, jobject thiz) {
    g_objectClass = g_jclass;
    g_thiz = thiz;
}

void Helper::release() {
    JNIEnv *pEnv = getJNIEnv();
    if (pEnv) {
        pEnv->DeleteGlobalRef(g_objectClass);
        pEnv->DeleteGlobalRef(g_thiz);
        g_thiz = NULL;
        g_objectClass = NULL;
    }

}

jniEnv跨线程调用问题以及局部全局化的问题

我处理数据回调的是在绘制检测框的时候调用,不管在时候时候,都会遇到该问题

void MyNdkCamera::on_image_render(cv::Mat &rgb) const {
    // nanodet
    {
        ncnn::MutexLockGuard g(lock);

        if (g_yolo && isDetected) {
            std::vector<com::tencent::yolov8ncnn::Object> objects;
            g_yolo->detect(rgb, objects);
            g_yolo->draw(rgb, objects);
            // 在此处将数据返回给java层
            helper->update2Android(objects);

        } else {
            draw_unsupported(rgb);
        }
    }

    draw_fps(rgb);
}

解决jniEnv跨线程调用问题 是在helper类中的getJNIEnv()方法

// 通过过全局的JVM 找到当前线程jniEnv
JNIEnv *Helper::getJNIEnv() {
    JNIEnv *env;
    int getEnvStat = g_jvm->GetEnv((void **) &env, JNI_VERSION_1_4);
    if (getEnvStat == JNI_EDETACHED) {
        // 如果当前线程未附加到 Java VM,需要调用 AttachCurrentThread 进行附加
        __android_log_print(ANDROID_LOG_DEBUG, "ncnn", " 如果当前线程未附加到 Java VM");
        if (g_jvm->AttachCurrentThread(&env, NULL) != 0) {
            __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "当前线程未附加到 Java VM===失败");
            return NULL;
        }
    } else if (getEnvStat == JNI_EVERSION) {
        // JNI 版本不支持
        __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "当前线程未附加到 JNI 版本不支持");
        return NULL;
    }
    return env;
}

解决局部变量全局化的问题 就是通过NewGlobalRef方法将本地(Native)层的对象引用转换为全局引用


extern "C"
JNIEXPORT void JNICALL
Java_com_tencent_yolov8ncnn_Yolov8Ncnn_initGlobalObj(JNIEnv *env, jobject thiz) {
    jclass clazz = env->FindClass("com/tencent/yolov8ncnn/Object");
    helper->initGlobal((jclass) env->NewGlobalRef(clazz), env->NewGlobalRef(thiz));

}

END

代码ruirui1128/android-yolov8 (github.com)

相关文章

网友评论

      本文标题:android yolov8 实时检测

      本文链接:https://www.haomeiwen.com/subject/rowlpdtx.html