美文网首页
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