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

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 gpudelete 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.mzph.cn/news/97317.shtml

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

相关文章

番外--Task2:

任务&#xff1a;root与普通用户的互切&#xff08;区别&#xff09;&#xff0c;启动的多用户文本见面与图形界面的互切命令&#xff08;区别&#xff09;。 输入图示命令&#xff0c;重启后就由图形界面转成文本登录界面&#xff1b; 输入图示命令&#xff0c;重启后就由文本…

java实验(头歌)--类的继承以及抽象类的定义和使用

文章目录 第一题第二题第三题 第一题 import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Scanner;//把main 函数的给替换了 public static vo…

MybatisPlus01

MybatisPlus01 1.MybatisPlus初体验 1.1首先要引入MybatisPlus的依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency>1.2定义Mapp…

【JavaEE初阶】 Thread类及常见方法

文章目录 &#x1f334;Thread类的概念&#x1f333;Thread 的常见构造方法&#x1f384;Thread 的几个常见属性&#x1f340;start()-启动一个线程&#x1f332;中断一个线程&#x1f6a9;实例一&#x1f6a9;实例二&#x1f6a9;实例三 &#x1f38d;join()-等待一个线程&…

想要开发一款游戏, 需要注意什么?

开发一款游戏是一个复杂而令人兴奋的过程。游戏开发是指创建、设计、制作和发布电子游戏的过程。它涵盖了从最初的概念和创意阶段到最终的游戏发布和维护阶段的各个方面。 以下是一些需要注意的关键事项&#xff1a; 游戏概念和目标&#xff1a; 确定游戏开发的核心概念和目标…

【SpringBoot】| Thymeleaf 模板引擎

目录 Thymeleaf 模板引擎 1. 第一个例子 2. 表达式 ①标准变量表达式 ②选择变量表达式&#xff08;星号变量表达式&#xff09; ③链接表达式&#xff08;URL表达式&#xff09; 3. Thymeleaf的属性 ①th:action ②th:method ③th:href ④th:src ⑤th:text ⑥th:…

TCP相关面试题

TCP相关面试题 题目1 介绍一下TCP三次握手的过程 介绍TCP三次握手应该从3个方面进行回答&#xff0c;分别是数据包名称&#xff0c;客户端与服务端的状态变化&#xff0c;数据包的序号变化。而不能只是简单回答发送的数据包名称。 TCP三次握手的过程如下&#xff1a; 从数据…

NSSCTF [BJDCTF 2020]easy_md5 md5实现sql

开局一个框 啥都没有用 然后我们进行抓包 发现存在提示 这里是一个sql语句 看到了 是md5加密后的 这里也是看了wp 才知道特殊MD5 可以被识别为 注入的万能钥匙 ffifdyopmd5 加密后是 276F722736C95D99E921722CF9ED621C转变为字符串 后是 or6 乱码这里就可以实现 注入 所…

定时任务详解

1、定时任务 在公司做项目时&#xff0c;经常遇到要用到定时任务的事情&#xff0c;但是对定时任务不熟练的时候会出现重复任务的情况&#xff0c;不深入&#xff0c;这次将定时任务好好学习分析一下 定时任务的原理 假如我将一个现场通过不断轮询的方式去判断&#xff0c;就…

理解一致性哈希算法

摘要&#xff1a;一致性哈希是什么&#xff0c;使用场景&#xff0c;解决了什么问题&#xff1f; 本文分享自华为云社区《16 张图解 &#xff5c; 一致性哈希算法》&#xff0c;作者&#xff1a;小林coding。 如何分配请求&#xff1f; 大多数网站背后肯定不是只有一台服务器…

文件格式转换

把我的悲惨故事说给大家乐呵乐呵&#xff1a;老板让运营把一些数据以json格式给我&#xff0c;当我看到运营在石墨文档上编辑的时候我人都傻了&#xff0c;我理解运营的艰难&#xff0c;可我也是真的难啊&#xff0c;在石墨文档编辑的眼花缭乱的&#xff0c;很多属性都错乱了(诸…

[SWPUCTF 2021 新生赛]sql - 联合注入

这题可以参考文章&#xff1a;[SWPUCTF 2021 新生赛]easy_sql - 联合注入||报错注入||sqlmap 这题相比于参考文章的题目多了waf过滤 首先&#xff0c;仍然是网站标题提示参数是wllm 1、fuzz看哪些关键字被过滤&#xff1a;空格、substr、被过滤 2、?wllm-1/**/union/**/selec…

微信小程序 movable-area 区域拖动动态组件演示

movable-area 组件在小程序中的作用是用于创建一个可移动的区域&#xff0c;可以在该区域内拖动视图或内容。这个组件常用于实现可拖动的容器或可滑动的列表等交互效果。 使用 movable-area 组件可以对其内部的 movable-view 组件进行拖动操作&#xff0c;可以通过设置不同的属…

消息驱动 —— SpringCloud Stream

Stream 简介 Spring Cloud Stream 是用于构建消息驱动的微服务应用程序的框架&#xff0c;提供了多种中间件的合理配置 Spring Cloud Stream 包含以下核心概念&#xff1a; Destination Binders&#xff1a;目标绑定器&#xff0c;目标指的是 Kafka 或者 RabbitMQ&#xff0…

信息增益,经验熵和经验条件熵——决策树

目录 1.经验熵 2.经验条件熵 3.信息增益 4.增益比率 5.例子1 6.例子2 在决策树模型中&#xff0c;我们会考虑应该选择哪一个特征作为根节点最好&#xff0c;这里就用到了信息增益 通俗上讲&#xff0c;信息增益就是在做出判断时&#xff0c;该信息对你影响程度的大小。比…

抖音seo源代码开源部署----基于开放平台SaaS服务

抖音SEO搜索是什么&#xff1f; 抖音SEO搜索是指在抖音平台上进行搜索引擎优化&#xff08;Search Engine Optimization&#xff09;的一种技术手段。 通过优化抖音账号、发布内容和关键词等&#xff0c;提高抖音视频在搜索结果中的排名&#xff0c;从而增加视频曝光量和用户点…

ValueError: high is out of bounds for int32 报错

问题描述&#xff1a; 笔者在Windows 64位平台跑一个在Ubuntu上运行正常的程序时&#xff0c;出现了以下报错&#xff1a; 具体为&#xff1a; seed np.random.randint(0, 2 ** 32) # make a seed with numpy generatorFile "mtrand.pyx", line 763, in numpy.ra…

Moonbeam Ignite强势回归

参与Moonbeam上最新的流动性计划 还记得新一轮的流动性激励计划吗&#xff1f;Moonbeam Ignite社区活动带着超过300万枚GLMR奖励来啦&#xff01;体验新项目&#xff0c;顺便薅一把GLMR羊毛。 本次Moonbeam Ignite活动的参与项目均为第二批Moonbeam生态系统Grant资助提案中获…

BaseQuickAdapter触底刷新实现

触底刷新实现 使用BaseQuickAdapter&#xff0c;在适配器中实现 LoadMoreModule即可&#xff0c;如下加上即可&#xff0c;无需多写代码 以下为分页实现&#xff1a; 视图中 // 获取加载更多模块loadMoreModule blogAdapter.getLoadMoreModule();loadMoreModule.setOnLoadMo…

无线振弦采集仪在岩土工程中如何远程监测和远程维护

无线振弦采集仪在岩土工程中如何远程监测和远程维护 随着岩土工程施工的不断发展和科技水平的不断提高&#xff0c;远程监测和远程维护设备也得到了广泛关注和应用。无线振弦采集仪是一种广泛应用于岩土工程中的测量仪器&#xff0c;在现代化施工中扮演着重要的角色。本文将就…