Android ncnn-android-yolov8-seg源码解析 : 实现人像分割

news2025/1/11 0:18:53

1. 前言

上篇文章,我们已经将人像分割的ncnn-android-yolov8-seg项目运行起来了,后续文章我们会抽取出Demo中的核心代码,在自己的项目中,来接入人体识别和人像分割功能。

先来看下效果,整个图像的是相机的原图,左上角部分,是我们进行人像识别、人像分割后,处理得到的图像 (未做镜像处理,所以暂时和原图左右是相反的)

在这里插入图片描述

那我们要怎么在自己的项目中,实现人像分割功能呢 ?

我们看ncnn-android-yolov8-seg的源码,可以发现, 这个项目里的相机也是用c/c++,但是在我们项目中,使用的Java层的Camera API来实现的。要想在自己项目里集成ncnn,那就需要把ncnn-android-yolov8-seg里的核心代码给抽离,然后对接到JavaCamera API中。

那需要怎么做呢 ? 接下来我们先来看一下它的源码

2. 源码分析

首先,我们来分析一下 Digital2Slave/ncnn-android-yolov8-seg Demo 中的源码

2.1 加载模型

加载模型是在Java_com_tencent_yolov8ncnn_Yolov8Ncnn_loadModel方法中,这里附上源码

JNIEXPORT jboolean JNICALL Java_com_tencent_yolov8ncnn_Yolov8Ncnn_loadModel(JNIEnv* env, jobject thiz, jobject assetManager, jint modelid, jint cpugpu)
{
    if (modelid < 0 || modelid > 6 || cpugpu < 0 || cpugpu > 1)
    {
        return JNI_FALSE;
    }

    AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);

    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "loadModel %p", mgr);

    const char* modeltypes[] =
    {
        "n",
        "s",
    };

    const int target_sizes[] =
    {
        320,
        320,
    };

    const float mean_vals[][3] =
    {
        {103.53f, 116.28f, 123.675f},
        {103.53f, 116.28f, 123.675f},
    };

    const float norm_vals[][3] =
    {
        { 1 / 255.f, 1 / 255.f, 1 / 255.f },
        { 1 / 255.f, 1 / 255.f, 1 / 255.f },
    };

    const char* modeltype = modeltypes[(int)modelid];
    int target_size = target_sizes[(int)modelid];
    bool use_gpu = (int)cpugpu == 1;

    // reload
    {
        ncnn::MutexLockGuard g(lock);

        if (use_gpu && ncnn::get_gpu_count() == 0)
        {
            // no gpu
            delete g_yolo;
            g_yolo = 0;
        }
        else
        {
            if (!g_yolo)
                g_yolo = new Yolo;
            g_yolo->load(mgr, modeltype, target_size, mean_vals[(int)modelid], norm_vals[(int)modelid], use_gpu);
        }
    }

    return JNI_TRUE;
}
2.1.1 modelid : 选择某个模型

根据modelid,来选择modeltypes中具体的某个模型。

const char* modeltypes[] =
{
    "n",
    "s",
};
const char* modeltype = modeltypes[(int)modelid];

这里的模型类别是和项目中asserts文件夹下的模型对应的,yolov8是个模型簇,从小到大包括:yolov8nyolov8syolov8myolov8lyolov8x等。
在这里插入图片描述
通常yolov8n速度最快,具体见下表
在这里插入图片描述

2.1.2 cpugpu : 使用CPU或GPU

接着来看Java_com_tencent_yolov8ncnn_Yolov8Ncnn_loadModel方法,
还有一个参数cpugpu是用来决定使用CPU还是GPU0CPU1GPU

bool use_gpu = (int)cpugpu == 1;
2.1.3 初始化模型

最后,调用g_yolo->load()来初始化模型

g_yolo->load(mgr, modeltype, target_size, mean_vals[(int)modelid], norm_vals[(int)modelid], use_gpu);

2.2 操作相机

ncnn-android-yolov8-seg Demo中的相机操作,都是通过NDKCamera API来完成的

static MyNdkCamera* g_camera = 0;

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "JNI_OnLoad");

    g_camera = new MyNdkCamera;

    return JNI_VERSION_1_4;
}
2.2.1 打开相机

打开相机就是调用g_camera->open()

JNIEXPORT jboolean JNICALL Java_com_tencent_yolov8ncnn_Yolov8Ncnn_openCamera(JNIEnv* env, jobject thiz, jint facing)
{
    if (facing < 0 || facing > 1)
        return JNI_FALSE;

    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "openCamera %d", facing);

    g_camera->open((int)facing);

    return JNI_TRUE;
}
2.2.2 关闭相机

关闭相机是调用g_camera->close()

JNIEXPORT jboolean JNICALL Java_com_tencent_yolov8ncnn_Yolov8Ncnn_closeCamera(JNIEnv* env, jobject thiz)
{
    __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "closeCamera");

    g_camera->close();

    return JNI_TRUE;
}

2.3 人体检测

人体检测是和NDKCamera相关联的,在相机回调的on_image_render方法中,完成了人体检测

2.3.1 on_image_render : 进行人体检测

来看一下on_image_render的源码,主要是通过g_yolo->detect()进行人体检测,g_yolo->draw()标注人体位置,用框框出来。

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

        if (g_yolo)
        {
            __android_log_print(ANDROID_LOG_DEBUG, "myncnn", "g_yolo:true");

            auto start = std::chrono::high_resolution_clock::now();

            std::vector<Object> objects;
            g_yolo->detect(rgb, objects); //人体检测

            start = std::chrono::high_resolution_clock::now();

            g_yolo->draw(rgb, objects); //标注人体位置,用框框出来
        }
        else
        {
            __android_log_print(ANDROID_LOG_DEBUG, "myncnn", "g_yolo:false");
            draw_unsupported(rgb);
        }
    }

    draw_fps(rgb); //绘制当前多少帧率
}
2.3.2 on_image_render 什么时候被调用 ?

那么on_image_render方法是什么时候被调用的呢 ? 来看ndkcamera.cpp中的on_image方法

可以看到,这里会对NV21图像做裁剪和旋转操作,再转成RGB格式,然后才传递给on_image_render()方法处理

// crop and rotate nv21
cv::Mat nv21_croprotated(roi_h + roi_h / 2, roi_w, CV_8UC1);
{
    const unsigned char* srcY = nv21 + nv21_roi_y * nv21_width + nv21_roi_x;
    unsigned char* dstY = nv21_croprotated.data;
    ncnn::kanna_rotate_c1(srcY, nv21_roi_w, nv21_roi_h, nv21_width, dstY, roi_w, roi_h, roi_w, rotate_type);

    const unsigned char* srcUV = nv21 + nv21_width * nv21_height + nv21_roi_y * nv21_width / 2 + nv21_roi_x;
    unsigned char* dstUV = nv21_croprotated.data + roi_w * roi_h;
    ncnn::kanna_rotate_c2(srcUV, nv21_roi_w / 2, nv21_roi_h / 2, nv21_width, dstUV, roi_w / 2, roi_h / 2, roi_w, rotate_type);
}

// nv21_croprotated to rgb
cv::Mat rgb(roi_h, roi_w, CV_8UC3);
ncnn::yuv420sp2rgb(nv21_croprotated.data, roi_w, roi_h, rgb.data);

on_image_render(rgb);
2.3.3 人体检测的流程

也就是说,从相机中得到的NV21数据,会先进行旋转,然后转成RGB格式,再交由g_yolo->detect()进行人体检测,通过g_yolo->draw()来标注人体位置。

到这里,我们核心源码就分析的差不多了,那我们怎么将该功能集成到自己的项目中呢 ?
我们在下一篇文章中来实现下 : Android 在自己的项目接入OpenCV+YOLOv8+NCNN,实现人像分割-CSDN博客。

3. Android 人像识别 系列文章

  • OpenCV相关

    • Visual Studio 2022 cmake配置opencv开发环境_opencv visualstudio配置_氦客的博客-CSDN博客
    • 在Visual Studio上,使用OpenCV实现人脸识别_氦客的博客-CSDN博客
    • Android Studio 接入OpenCV,并实现灰度图效果_氦客的博客-CSDN博客
    • Android 使用OpenCV实现实时人脸识别,并绘制到SurfaceView上_氦客的博客-CSDN博客
  • NCNN+YOLO8相关

    • Android 导入ncnn-android-yolov8-seg : 实现人体识别和人像分割-CSDN博客
    • Android ncnn-android-yolov8-seg源码解析 : 实现人像分割-CSDN博客
    • Android 在自己的项目接入OpenCV+YOLOv8+NCNN,实现人像分割-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1068091.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

增强现实抬头显示AR-HUD

增强现实抬头显示&#xff08;AR-HUD&#xff09;可以将当前车身状态、障碍物提醒等信息3D投影在前挡风玻璃上&#xff0c;并通过自研的AR-Creator算法&#xff0c;融合实际道路场景进行导航&#xff0c;使驾驶员无需低头即可了解车辆实时行驶状况。结合DMS系统&#xff0c;可以…

【C++】C++11(万能模版、完美转发,可变参数模版,lambda表达式,包装器function+bind绑定)

前言&#xff1a; 我们上一章一节已经接触了C11的一些特性&#xff0c;本章将继续更深入介绍C11中其他使用且重要的一些功能。 目录 &#xff08;一&#xff09;万能模版和完美转发 1、万能模版 2、完美转发 &#xff08;二&#xff09;可变参数模版 1、可变参数模版的使…

【Node.js】NPM 和 package.json

NPM npm 是 Node.js 的包管理工具&#xff0c;基于命令行&#xff0c;用于安装、升级、移除、管理依赖项。 常用命令&#xff1a; npm init&#xff1a;初始化一个新的 npm 项目&#xff0c;创建 package.json 文件。&#xff08;括号里为默认值&#xff09; description&am…

信息可视化和数据可视化的异同和其他比较,到底怎么区分呢?

什么是数据可视化? 根据维塔利弗里德曼的说法&#xff1a; “数据可视化的主要目标是通过图形方式清晰有效地传达数据。” 数据可视化是将数据转化为图表、图形、地图等视觉元素的过程&#xff0c;以便人们更容易理解数据的趋势、关系和模式。它通常强调的是对数据的视觉呈…

Git开发环境使用

目录 git简单介绍 问题 在Git Bash下的操作 在工具idea中使用git 创建分支 合并分支 部分命令合集 git简单介绍 Linux发布了一套属于自己的版本控制系统&#xff0c;运用git可以实现&#xff0c; 代码回溯 &#xff0c; 版本切换 因为是Git是分布式版本控制系统&#x…

unity脚本_Time c#

Time 用于游戏中参与位移 计时 时间暂停等 时间缩放比例&#xff1a; 首先在场景上新建一个空物体 将脚本挂载到空物体上运行 另外重要的两点

从「博客园」的困境,到「用爱发电」~

听人劝、吃饱饭,奉劝各位小伙伴,不要订阅该文所属专栏。 作者:不渴望力量的哈士奇(哈哥),十余年工作经验, 跨域学习者,从事过全栈研发、产品经理等工作,现任研发部门 CTO 。荣誉:2022年度博客之星Top4、博客专家认证、全栈领域优质创作者、新星计划导师,“星荐官共赢计…

JavaScript中的map()和forEach()方法有什么区别?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

【javaweb】学习日记Day11 - tlias智能管理系统 - 文件上传 新增 修改员工 配置文件

目录 一、员工管理功能开发 1、新增员工 postman报错500的原因 &#xff08;1&#xff09;Controller类 &#xff08;2&#xff09;Service类 &#xff08;3&#xff09;Mapper类 2、根据ID查询 &#xff08;1&#xff09;Controller类 &#xff08;2&#xff09;Serv…

【网络安全 --- kali2023安装】超详细的kali2023安装教程(提供镜像资源)

如果你还没有安装vmware 虚拟机&#xff0c;请参考下面博客安装 【网络安全 --- 工具安装】VMware 16.0 详细安装过程&#xff08;提供资源&#xff09;-CSDN博客【网络安全 --- 工具安装】VMware 16.0 详细安装过程&#xff08;提供资源&#xff09;https://blog.csdn.net/m0…

mysql MVCC(多版本并发控制)理解

最近看MVCC相关资料&#xff0c;这边做一个记录总结&#xff0c;方便后续理解。 目录 一、MVCC相关概念 二、MVCC实现原理 1.隐藏字段 2.undo log 3.Read View 4.MVCC的整体处理流程 5. RC&#xff0c;RR级级别下的innoDB快照读有什么不同 6.总结 一、MVCC相关概念 1…

在Ubuntu中批量创建用户

一、背景知识 在Linux操作系统中创建新用户可以使用useradd或adduser命令。 使用useradd命令创建用户时&#xff0c;不会在/home目录下创建用户文件夹&#xff0c;需要用户自己指定主目录和bash目录的位置。同时&#xff0c;创建的用户没有设置密码&#xff0c;无法进行登录&a…

electronjs入门-聊天应用程序,与Electron.js通信

随着第一章中构建的应用程序&#xff0c;我们将开始将其与Electron框架中的模块集成&#xff0c;并以此为基础&#xff0c;以更实用的方式了解它们。 过程之间的通信 根据第二章中的解释&#xff0c;我们将发送每个进程之间的消息&#xff1b;具体来说联系人和聊天&#xff1…

对于无法直接获取URL的数据爬虫

在爬学校安全教育题库的时候发现题库分页实际上执行了一段js代码&#xff0c;如下图所示 点击下一页时是执行了函数doPostBack&#xff0c;查看页面源码如下 点击下一页后这段js提交了一个表单&#xff0c;随后后端返回对应数据&#xff0c;一开始尝试分析获取对应两个参数&a…

MM-Camera架构-ProcessCaptureRequest 流程分析

文章目录 processCaptureRequest\_3\_41.1 mDevice1.2 mDevice->ops->process\_capture\_request1.3 hardware to vendor mct\_shimlayer\_process\_event2.1 mct\_shimlayer\_handle\_parm2.2 mct\_shimlayer\_reg\_buffer processCaptureRequest_3_4 sdm660的摄像头走…

stm32的时钟、中断的配置(针对寄存器),一些基础知识

一、学习参考资料 &#xff08;1&#xff09;正点原子的寄存器源码。 &#xff08;2&#xff09;STM32F103最小系统板开发指南-寄存器版本_V1.1&#xff08;正点&#xff09; &#xff08;3&#xff09;STM32F103最小系统板开发指南-库函数版本_V1.1&#xff08;正点&#xff0…

QChart使用说明

一.使用说明 Qt官网例程&#xff1a;https://doc.qt.io/qt-5/qtcharts-examples.html QChart&#xff1a;用于管理图表中的线、图例和轴的图形表示。可以简单理解为是一个画布。QChartView&#xff1a;视图组件&#xff0c;无法单独进行显示&#xff0c;需要依附其他组件进行…

高德地图开发实战案例:实现信息弹出框的富文本展示效果

marker.content "<p classcardsBg></p>";.cardsBg {width: 246px;height: 426px;background: url(../images/cards.png) no-repeat center center; }其中cards.png为整个弹出模态框的背景图片&#xff0c;做到这一步。仍旧会自带高德地图的样式&#xf…

C指针【嵌入式】

一、到底什么是指针 1.指针变量和普通变量的区别 &#xff08;1&#xff09;首先非常关键的是&#xff0c;指针的实质就是一个变量&#xff0c;它根普通变量没有任何本质区别。指针完整的名字应该叫指针变量&#xff0c;简称为指针。 #include<stdio.h> void main(){//a…

perl语言——length.pl脚本(统计fasta文件序列长度)

Perl脚本——stat.pl&#xff08;统计fasta文件序列长度&#xff09; 相比Perl语言&#xff0c;现在python用的多。但是perl依旧是生信学习的一门课程&#xff0c;还是有人在写&#xff0c;所以你至少要会读。 #!/use/bin/perl #perl解析器$inputFile $ARGV[0]; #输…