关于我为啥捣鼓这玩意
这是我的废话...不想看的直接上拉跳过看下个标题
- 先声明下,本人没有系统地学习过JAVA或者C++
- 我个人觉得学习东西,不要直接就去看那些所谓的教程,现在AI这么发达,完全可以直接问
- 所以,当我产生了开发Autojs插件的想法的时候,我第一时间不是去学JAVA基础,而是直接去看插件开发流程
- 因为有其它语言基础,至少Java一些共通点还是懂得,不懂的时候直接问AI,java的各种代码都是Ai给我写的,我只需要知道每一块代码的作用,并把它们拼接起来
- 于是在AI的帮助下,我成功开发出Autojs的插件了,虽然让我直接写java,可能即使是一个toast我都写不出来,但是至少在阅读或者修改源码方面是无障碍了,而这就足够了
- 在前天,我成功把Autojs的YoloV8插件开发出来了,各种功能都符合预想,但是yolov8训练出来的pt模型导出麻烦,比如修改代码文件后先导出onnx,再用pnnx工具将onnx导出ncnn才能使用
- yolov8目前是自带一键将pt导出ncnn的,但是用在我上面的插件上却不行,而ncnn提供了最新的yolov8.cpp代码,这个是适配一键导出的模型的
- 那么问题就来了....我不会C++
- 此时我有两个选择,
- 凑合用吧,也就导出模型麻烦,至少能用不是吗?
- 继续造,C++而已,干他
我选择了后者,给我自己一天时间,不行就放弃。
- 于是我就思考可行的方案,一种是在现有的代码框架上修改源码,一一比对,看看到底差在哪里了,结果浪费了半天都没比对出个啥,一改就是各种报错,放弃了,毕竟对我来说还是个全新的语言,全文都没看懂怎么改
- 换了一种方案:不去研究代码的实现,去研究如何如何调用,到时候把原有的cpp相关的都删掉,自己再重新调用就好了
- 通过从java层的函数进行调用溯源追踪,一步一步追加到了C++,我明白了整个流程。
- 但是过程没那么顺利,老是有红字报错,差点给他搞奔溃了,AI也没问出个所以然,各种排查问题,交叉编译是真的让人头疼
- 当然,既然你看到了这篇文章,那就说明我最后还是成功了~
效果图示
这是由纯yolov8自带的ncnn一键导出的模型推理的结果。
至于插件的后续开发,还得两三天,在此占个位,后续补充最终插件成果!
配置 CMake 路径
在 build.gradle 文件的 android 节点下配置 CMake 路径。CMake 是用于构建本地代码的工具,通过它可以在 Android 应用中集成 C++ 代码。
android {
...
externalNativeBuild {
cmake {
version "3.22.1" // 指定CMake版本
path file('src/main/jni/CMakeLists.txt') // CMakeLists.txt文件的位置
}
}
}
配置 CMakeLists.txt
CMakeLists.txt 是 CMake 的配置文件,用于定义项目构建的各种规则和选项。在这个文件中,你需要指定使用的 C++ 标准、库文件、以及与 Java 层的接口函数。
# 设置CMake的最低版本
cmake_minimum_required(VERSION 3.22.1)
# 定义项目名称和使用的语言
project(yolov8ncnn LANGUAGES CXX)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加源文件并生成共享库
add_library(yolov8ncnn SHARED yolov8ncnn.cpp)
# 链接必要的库
find_library(log-lib log) # 查找log库,用于日志输出
target_link_libraries(yolov8ncnn ${log-lib} ncnn) # 链接ncnn库和log库
实现接口层 (yolov8ncnn.cpp)
在 C++ 文件中,编写 JNI 接口函数,该函数将暴露给 Java 层使用。需要用 extern "C" 来防止函数名被 C++ 编译器修改(名字修饰)。
#include <jni.h>
#include <vector>
#include <opencv2/opencv.hpp> // OpenCV库
#include "ncnn/include/net.h" // ncnn库
#include "yolov8ncnn.h" // 引入声明函数和数据结构
// 编写具体的 YOLOv8 检测逻辑
int detect_yolov8(const cv::Mat& bgr, std::vector<Object>& objects) {
// 实现检测逻辑,例如利用ncnn库进行YOLOv8推理
// 1. 创建ncnn::Net对象
// 2. 加载YOLOv8模型
// 3. 执行检测并解析输出
return 0; // 假设0表示成功
}
// JNI 接口函数定义
extern "C" {
JNIEXPORT jint JNICALL Java_com_daowuya_ajyolov8_NCNN_detectYolov8(JNIEnv* env, jobject thiz, jobject bitmap) {
// 从Bitmap转换为OpenCV的Mat对象
cv::Mat bgr; // 这里需要完成Bitmap到Mat的转换
std::vector<Object> objects;
// 调用本地检测函数
int result = detect_yolov8(bgr, objects);
// 检测完成后,可以将检测结果返回给Java层
return result; // 返回检测结果
}
}
声明接口层函数 (yolov8ncnn.h)
声明本地函数接口,以便清晰地分离接口层和具体实现。
// 声明你需要的对象结构体
struct Object {
cv::Rect_<float> rect;
int label;
float prob;
};
int detect_yolov8(const cv::Mat& bgr, std::vector<Object>& objects);
Java 对接层 (com.daowuya.ajyolov8.NCNN)
在 Java 层中,使用 System.loadLibrary 加载本地库,并调用 JNI 接口函数。你需要在 Java 类中声明一个 native 方法,这个方法名必须与 C++ 代码中的 JNI 函数名相对应。
package com.daowuya.ajyolov8;
public class NCNN {
// 加载本地库
static {
System.loadLibrary("yolov8ncnn");
}
// 声明一个native方法,用于调用C++中的检测函数
public native int detectYolov8(Bitmap bitmap);
// 可以实现一个Java方法来调用native方法并处理结果
public void runDetection(Bitmap bitmap) {
int result = detectYolov8(bitmap);
// 根据result做进一步处理,例如显示检测结果
}
}
结语
- 本文仅仅是笔主为了记录学习历程、分享技术而作。
- 如果你是C++大佬,本文中如果有不对的地方欢迎留言指正!