人脸识别SDK(Android)之人脸识别引擎代码
关于人脸识别课程,不了解的可先看课程预告:重磅上线!免费带您从0到1手撸一款人脸识别SDK应用
关于人脸识别技术应用及核心技术简介,可以看之前发送的iOS课程第一篇文章:人脸识别技术应用及核心技术简介
接下来,我们将为您呈现 Android 人脸识别SDK的完整系列文章,为大家详细介绍人脸识别的核心技术及SDK开发过程,带您从0到1写一款Android版人脸识别SDK应用,Github 实战Demo地址在最后一篇文章。
本系列文章分为5篇,分别为:
-
人脸识别SDK(Android)之设备相机画面采集
-
人脸识别SDK(Android)之如何在Mac上构建Android NCNN库
-
人脸识别SDK(Android)之人脸识别引擎代码(即本篇)
-
人脸识别SDK(Android)之项目回顾总结
-
人脸识别SDK之(Android)Github示例及集成详解
核心NDK代码
在上一节课程中我们介绍了如何编译Android平台的NCNN库。本节课讲介绍如何通过NCNN识别人脸。
环境准备
OpenCV: 3.4.0 (图像处理,对图片进行裁剪)NDK: 3.10.2 (Android Studio)cmake: 21.1.6352462 (Android Studio)Android studio JDK: 11 (jdk 11)
OpenCV 可以从官方网站下载 https://opencv.org/releases/ , NDK 以及 cmake 打开 Android Studio的 Android SDK 面板-> SDK Tools进行下载如下图所示

开发环境已经配置好了,我们先看下项目的的结构目录

-
assets目录下的yolov5n-0.5.bin(模型权重)以及yolov5n-0.5.param(模型的结构信息) 人脸识别的模型文件。
-
cpp 目录下include 目录下的opencv以及opencv2是opencv相关的代码,include 目录下的其他头文件是编译ncnn库时生成的头文件,可以回顾下第三课的内容。
-
face_jni.cpp 提供识别人脸的c方法
-
yoloface.cpp以及yoloface.h 对图片进行人脸检测并提取人脸的五个主要关键点,双眼、鼻尖和两侧嘴角。
-
jniLibs 这里是ncnn编译后的静态库文件
在第一节课中我们说到在onPreviewFrame方法会返回相机采取的每一帧图片信息,我们须需要将获取到的图片信息进行人脸检测。
下面提供了CMakeLists.txt配置文件的说明以及对图片进行人脸检测的c代码。
CMakeLists.txt文件配置信息
#导入头文件目录include_directories(include../cpp/)#导入OpenCV的配置文件set(OpenCV_DIR /Users/admin/work/OpenCV-android-sdk/sdk/native/jni)find_package(OpenCV 3 REQUIRED)#source directory源文件目录file(GLOB FACE_SRC *.h*.cpp)set(FACE_COMPILE_CODE ${FACE_SRC})#添加ncnn库add_library(libncnn STATIC IMPORTED )set_target_properties(libncnnPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libncnn.a)#编译为动态库add_library(FaceSHARED${FACE_COMPILE_CODE})#添加工程所依赖的库find_library(log-lib log android)target_link_libraries(Facelibncnnandroidzjnigraphics${OpenCV_LIBS}${log-lib})
具体的人脸识别检测代码如下
extern "C"JNIEXPORT jobject JNICALLJava_com_ncnn_demo_detect_FaceDetect_faceDetectPoint(JNIEnv *env, jobject instance,jbyteArray yuv,jint imageWidth, jint imageHeight) {int input_size = 640;// letterbox pad to multiple of 32int w = imageWidth;int h = imageHeight;float scale = 1.f;if (w > h) {scale = (float) input_size / w;w = input_size;h = h * scale;} else {scale = (float) input_size / h;h = input_size;w = w * scale;}//将yuv byte数组转为jbyte*jbyte *yuvBytes = env->GetByteArrayElements(yuv, nullptr);//jbyte* 强转char*unsigned char *yuvChars = (unsigned char *) yuvBytes;auto *faceImageCharDate = new unsigned char[(int) imageHeight * (int) imageWidth * 3];//ncnn 将yuv 逐句转为rgb格式ncnn::yuv420sp2rgb(yuvChars, imageWidth, imageHeight, faceImageCharDate);//ncnn 将图像转为ncnn::Mat格式ncnn::Mat ncnn_img;ncnn_img = ncnn::Mat::from_pixels(faceImageCharDate, ncnn::Mat::PIXEL_RGB, imageWidth,imageHeight);ncnn::Mat in = ncnn::Mat::from_pixels_resize(faceImageCharDate, ncnn::Mat::PIXEL_RGB,imageWidth, imageHeight, w, h);//finalBbox 人脸检测信息std::vector<Object> finalBbox;g_yoloface->detect(in, finalBbox, imageWidth, imageHeight, scale);// 创建ArrayList<FaceInfo>jclass faceListClass = env->FindClass("java/util/ArrayList");jmethodID faceListConstructor = env->GetMethodID(faceListClass, "<init>", "()V");jobject faceList = env->NewObject(faceListClass, faceListConstructor);for (int i = 0; i < finalBbox.size(); i++) {// 创建FaceArea对象jclass faceAreaClass = env->FindClass("com/ncnn/demo/detect/FaceInfoList$FaceArea");jmethodID faceAreaConstructor = env->GetMethodID(faceAreaClass, "<init>", "(IIII)V");jobject faceArea = env->NewObject(faceAreaClass, faceAreaConstructor,(int) finalBbox[i].rect.x, (int) finalBbox[i].rect.y,(int) finalBbox[i].rect.width,(int) finalBbox[i].rect.height);// 创建ArrayList<FacePointInfo>(这里先创建一个空的,因为示例中没有FacePointInfo的详细构造)jclass pointListClass = env->FindClass("java/util/ArrayList");jmethodID pointListConstructor = env->GetMethodID(pointListClass, "<init>", "()V");jobject pointList = env->NewObject(pointListClass, pointListConstructor);for (int j = 0; j < finalBbox[i].pts.size(); j++) {// 创建FacePointInfo对象jclass facePointInfoClass = env->FindClass("com/ncnn/demo/detect/FaceInfoList$FacePointInfo");jmethodID facePointInfoConstructor = env->GetMethodID(facePointInfoClass, "<init>","(II)V");jobject facePointInfo = env->NewObject(facePointInfoClass, facePointInfoConstructor,(int) finalBbox[i].pts[j].x,(int) finalBbox[i].pts[j].y);// 将FacePointInfo对象添加到list中jmethodID pointListAdd = env->GetMethodID(pointListClass, "add", "(Ljava/lang/Object;)Z");env->CallBooleanMethod(pointList, pointListAdd, facePointInfo);}// 创建FaceInfo对象jclass faceInfoClass = env->FindClass("com/ncnn/demo/detect/FaceInfoList$FaceInfo");jmethodID faceInfoConstructor = env->GetMethodID(faceInfoClass, "<init>","(Lcom/ncnn/demo/detect/FaceInfoList$FaceArea;Ljava/util/ArrayList;)V");jobject faceInfo = env->NewObject(faceInfoClass, faceInfoConstructor, faceArea, pointList);// 将FaceInfo对象添加到faceList中jmethodID listAdd = env->GetMethodID(faceListClass, "add", "(Ljava/lang/Object;)Z");env->CallBooleanMethod(faceList, listAdd, faceInfo);}// 创建FaceInfoList对象jclass faceInfoListClass = env->FindClass("com/ncnn/demo/detect/FaceInfoList");jmethodID faceInfoListConstructor = env->GetMethodID(faceInfoListClass, "<init>","(Ljava/util/ArrayList;)V");jobject faceInfoList = env->NewObject(faceInfoListClass, faceInfoListConstructor, faceList);//释放内存finalBbox.clear();in.release();ncnn_img.release();free(faceImageCharDate);env->ReleaseByteArrayElements(yuv, yuvBytes, 0);return faceInfoList;}
检查代码的具体步骤:
-
先将图片yuv数组转为jbyte
-
jbyte* 强转char*
-
使用ncnn 将yuv 数据转为RGB格式,在RGB格式中,每个像素由红、绿、蓝三个分量组成,因此转换后的图像数据大小将是width * height * 3字节
-
ncnn 将图像转为ncnn::Mat格式并通过from_pixels_resize 进行缩放(最长方向宽或者高的值为640)
-
通过g_yoloface->detect 进行检测,如果finalBbox数组的size大于0说明检测到人脸了
-
finalBbox 遍历人脸信息将人脸的信息进行返回。
欢迎感兴趣的朋友通过下方名片关注我们,共同学习探讨人脸识别技术。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)