赞
踩
关于YOLO8学习(一)环境搭建,官方检测模型部署到手机
关于YOLO8学习(二)数据集收集,处理
关于YOLO8学习(三)训练自定义的数据集
关于YOLO8学习(四)模型转换为ncnn
关于YOLO8学习(五)安卓部署ncnn模型–视频检测
前文第五章,讲述了部署自定义模型后,进行安卓视频检测人脸。
本文将会讲解:
(1)使用前文可以运行的demo,讲解如何使用模型对图片进行识别,并且标注,自定义返回结果
win10、python 3.11、cmake、pytorch2.0.1+cu117、pycharm、ultralytics==8.0.134
要特别注意,不要升级到python312,目前onnx还没支持到312,所以转换了,会导致转换模型失效。
对于上述环境如何配置,请看我之前的文章。
安卓:as4+,ndk21+,jdk11
本次实现Yolo8的安卓图片检测,对于新手来说,应该会十分坎坷,因为整个流程,涉及到了java,安卓,jni,python这几个语言的糅合,话不多说,直接上干活。
(1)对于“检测出目标的位置”这个流程,是不是时曾相识?没错,前面的文章中,我们已经实现了视频的检测,细心一点的朋友可能会发现,源码中的yolo.cpp类,detect和draw方法,不就是检测和绘制吗?正确
(2)对于返回给上层的信息,是不是也可以通过jni定义好,然后c层直接封装,java曾直接解析,就可以了?
关键点说完了,下面直接看关键的代码:
博主先在Yolov8Ncnn.java文件中,定义了一个jni方法,名字为
public native List<HashMap<String, String>> detectedStaticPic(Bitmap bitmap);
这个方法,就是要求c层,返回一个列表,列表每一项中,都是一个key,value组成的map,那么这个map,就可以放下我们的x,y,label等信息了。然后再看输入对象是一个bitmap。到此位置,jni层的java类已经定义完成。
接着在yolov8ncnn.cpp文件中,直接创建对应的方法,具体代码如下:
/**
* 檢測圖片
* */
extern "C"
JNIEXPORT jobject JNICALL
Java_com_north_light_yolov8ncnn_Yolov8Ncnn_detectedStaticPic(JNIEnv *env, jobject thiz,
jobject bitmap) {
return g_yolo->detected_static_pic(env, thiz, bitmap);
}
可以看到,这里直接调用回yolo.cpp的方法,同样,按照c语言的习惯,我们直接在yolo.h和yolo.cpp定义好即可。具体代码如下:
jobject detected_static_pic(JNIEnv *pEnv, jobject pJobject, jobject pJobject1);
jobject Yolo::detected_static_pic(JNIEnv *env, jobject thiz, jobject bitmap)
定义好了以后,所有有关的检测逻辑,都在yolo.cpp里面编写,最后返回一个List<HashMap<String,String>>对象给上层即可。
至此,我们已经搭建完成整个开发骨架了,接下来,就是具体业务的编写:
这里先提供一个思路,后面会放出所有的代码:
// 检测静态图片
jobject Yolo::detected_static_pic(JNIEnv *env, jobject thiz, jobject bitmap) {
//结果数据
jclass arrayListClass = env->FindClass("java/util/ArrayList");
jmethodID arrayListInit = env->GetMethodID(arrayListClass, "<init>", "()V");
jobject arrayListObj = env->NewObject(arrayListClass, arrayListInit);
// 获取Bitmap信息
AndroidBitmapInfo info;
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
return arrayListObj;
}
if (info.format != ANDROID_BITMAP_FORMAT_RGB_565 &&
info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
return arrayListObj;
}
// 锁定Bitmap像素
void *pixels = nullptr;
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
return arrayListObj;
}
// 计算RGB数据大小
size_t pixelCount = info.width * info.height;
size_t rgbDataSize = pixelCount * 3; // 每个像素3个字节(RGB)
std::vector<unsigned char> rgbData(rgbDataSize);
// 提取RGB值
uint8_t *p = static_cast<uint8_t *>(pixels);
for (size_t i = 0; i < pixelCount; ++i) {
if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {
// 对于RGB_565格式,需要转换为RGB888
uint16_t pixel = *reinterpret_cast<uint16_t *>(p);
rgbData[i * 3] = ((pixel >> 8) & 0xF8) | ((pixel >> 13) & 0x07); // R
rgbData[i * 3 + 1] = ((pixel >> 3) & 0xF8) | ((pixel >> 11) & 0x07); // G
rgbData[i * 3 + 2] = ((pixel << 3) & 0xF8) | ((pixel >> 5) & 0x07); // B
p += 2; // RGB_565每像素2字节
} else if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
// 直接提取RGB分量,忽略Alpha
rgbData[i * 3] = p[0]; // R
rgbData[i * 3 + 1] = p[1]; // G
rgbData[i * 3 + 2] = p[2]; // B
p += 4; // ARGB_8888每像素4字节
}
}
int width = info.width;
int height = info.height;
// 解锁Bitmap像素
AndroidBitmap_unlockPixels(env, bitmap);
// 确保数据量与宽度、高度匹配
if (rgbData.size() != static_cast<size_t>(width * height * 3)) {
return arrayListObj;
}
cv::Mat mat(height, width, CV_8UC3, (void *) rgbData.data());
//完成了数据的转换,开始检测
std::vector<Object> objects;
detect(mat, objects);
draw(mat,objects);
// __android_log_print(ANDROID_LOG_DEBUG, "ncnn", "objects size %d", objects.size());
if (objects.size() <= 0) {
return arrayListObj;
}
//上述步骤,可以检测出具体的框
//组装数据
jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
if (objects.size() > 0) {
for (int i = 0; i < objects.size(); i++) {
int label_pos = objects[i].label;
jclass hashMapClass = env->FindClass("java/util/HashMap");
jmethodID hashMapInit = env->GetMethodID(hashMapClass, "<init>", "()V");
jobject hashMapObj = env->NewObject(hashMapClass, hashMapInit);
jstring xKeyStr = charToString(env, "x");
jstring xValue = intToString(env, objects[i].rect.x);
jstring yKeyStr = charToString(env, "y");
jstring yValue = intToString(env, objects[i].rect.y);
jstring wKeyStr = charToString(env, "w");
jstring wValue = intToString(env, objects[i].rect.width);
jstring hKeyStr = charToString(env, "h");
jstring hValue = intToString(env, objects[i].rect.height);
jstring labelKeyStr = charToString(env, "label");
jstring labelValue = intToString(env, objects[i].label);
jstring labelStrKeyStr = charToString(env, "label_str");
jstring labelStrValue = charToString(env, class_names[label_pos]);
jstring probKeyStr = charToString(env, "prob");
jstring probValue = floatToString(env, objects[i].prob);
putKeyValueToMap(env, hashMapObj, hashMapClass, xKeyStr, xValue);
putKeyValueToMap(env, hashMapObj, hashMapClass, yKeyStr, yValue);
putKeyValueToMap(env, hashMapObj, hashMapClass, wKeyStr, wValue);
putKeyValueToMap(env, hashMapObj, hashMapClass, hKeyStr, hValue);
putKeyValueToMap(env, hashMapObj, hashMapClass, labelKeyStr, labelValue);
putKeyValueToMap(env, hashMapObj, hashMapClass, labelStrKeyStr, labelStrValue);
putKeyValueToMap(env, hashMapObj, hashMapClass, probKeyStr, probValue);
env->CallBooleanMethod(arrayListObj, addMethod, hashMapObj);
env->DeleteLocalRef(hashMapObj);
env->DeleteLocalRef(xKeyStr);
env->DeleteLocalRef(xValue);
env->DeleteLocalRef(yKeyStr);
env->DeleteLocalRef(yValue);
env->DeleteLocalRef(wKeyStr);
env->DeleteLocalRef(wValue);
env->DeleteLocalRef(hKeyStr);
env->DeleteLocalRef(hValue);
env->DeleteLocalRef(labelKeyStr);
env->DeleteLocalRef(labelValue);
env->DeleteLocalRef(labelStrKeyStr);
env->DeleteLocalRef(labelStrValue);
env->DeleteLocalRef(probKeyStr);
env->DeleteLocalRef(probValue);
}
}
return arrayListObj;
}
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
package com.north.light.yolov8ncnn;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.Surface;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Yolov8Ncnn implements Serializable {
public native boolean loadModel(AssetManager mgr, int modelid, int cpugpu);
/**
* @param facing 0前置 1后置
*/
public native boolean openYoloCamera(int facing);
public native boolean closeYoloCamera();
public native boolean setOutputWindow(Surface surface);
public native List<HashMap<String, String>> detectedStaticPic(Bitmap bitmap);
static {
System.loadLibrary("yolov8ncnn");
}
private static final int[] colors = new int[]{
Color.rgb(54, 67, 244),
Color.rgb(99, 30, 233),
Color.rgb(176, 39, 156),
Color.rgb(183, 58, 103),
Color.rgb(181, 81, 63),
Color.rgb(243, 150, 33),
Color.rgb(244, 169, 3),
Color.rgb(212, 188, 0),
Color.rgb(136, 150, 0),
Color.rgb(80, 175, 76),
Color.rgb(74, 195, 139),
Color.rgb(57, 220, 205),
Color.rgb(59, 235, 255),
Color.rgb(7, 193, 255),
Color.rgb(0, 152, 255),
Color.rgb(34, 87, 255),
Color.rgb(72, 85, 121),
Color.rgb(158, 158, 158),
Color.rgb(139, 125, 96)
};
public List<TrainDetectedObject> trainDataToDetectedObject(List<HashMap<String, String>> data) {
if (data == null || data.size() == 0) {
return new ArrayList<>();
}
List<TrainDetectedObject> result = new ArrayList<>();
for (int i = 0; i < data.size(); i++) {
HashMap<String, String> item = data.get(i);
if (item.containsKey("label") && item.containsKey("label_str") &&
item.containsKey("prob") && item.containsKey("x") &&
item.containsKey("y") && item.containsKey("w") &&
item.containsKey("h")) {
TrainDetectedObject obj = new TrainDetectedObject();
obj.label = item.get("label");
obj.labelName = item.get("label_str");
obj.prob = Float.parseFloat(item.get("prob"));
obj.x = Float.parseFloat(item.get("x"));
obj.y = Float.parseFloat(item.get("y"));
obj.w = Float.parseFloat(item.get("w"));
obj.h = Float.parseFloat(item.get("h"));
result.add(obj);
}
}
return result;
}
public Bitmap showObjects(List<TrainDetectedObject> objects, Bitmap orgBitmap) {
if (objects == null) {
return null;
}
// draw objects on bitmap
Bitmap rgba = orgBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(rgba);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
Paint textbgpaint = new Paint();
textbgpaint.setColor(Color.WHITE);
textbgpaint.setStyle(Paint.Style.FILL);
Paint textpaint = new Paint();
textpaint.setColor(Color.BLACK);
textpaint.setTextSize(26);
textpaint.setTextAlign(Paint.Align.LEFT);
for (int i = 0; i < objects.size(); i++) {
paint.setColor(colors[i % 19]);
canvas.drawRect(objects.get(i).x, objects.get(i).y, objects.get(i).x + objects.get(i).w, objects.get(i).y + objects.get(i).h, paint);
{
String text = objects.get(i).labelName + " = " + String.format("%.1f", objects.get(i).prob * 100) + "%";
float text_width = textpaint.measureText(text);
float text_height = -textpaint.ascent() + textpaint.descent();
float x = objects.get(i).x;
float y = objects.get(i).y - text_height;
if (y < 0)
y = 0;
if (x + text_width > rgba.getWidth())
x = rgba.getWidth() - text_width;
canvas.drawRect(x, y, x + text_width, y + text_height, textbgpaint);
canvas.drawText(text, x, y - textpaint.ascent(), textpaint);
}
}
return rgba;
}
}
···
通过以上的方式,就可以实现jni层检测,安卓层绘制边框的逻辑了。
### 更详细的安卓代码,如下:
## 安卓demo源码:关注并回复 “yolo8检测人脸图片检测代码”即可
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4d865e2d13444726b3ea24a9acb55897.jpeg)
that's all--------------------------------------------------------------------------------------------
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。