HarmonyOS 原生智能之人脸检测实战
背景
公司很多场景中用到了人脸检测、人脸识别的功能,当时我们图像团队自研了人脸识别、人脸检测相关的模型,其中人脸识别、姿态识别运行在手机端,基于tensorflow的引擎进行推理,检测到人脸后将人脸数据发送到服务端做人脸匹配。还有些特殊场景也应用到图像方面的技术,比如空屋检测等,虽然HarmonyOS 提供了人脸检测接口,但是要对齐之前Android、iOS端效果,还是需要跑通自研模型。
要跑通本地模型首先想到的是将开源的tensorflowlite推理引擎编译成HarmonyOS版本,然后运行加载已有模型进行推理。这个方案首先需要编译tflite开源C++库到HarmonyOS平台,而且在Android端tflite有GPU加速能力,因为推理引擎适配了Android端提供的GPU方式推理的API,但是HarmonyOS平台目前无人适配,也暂时没有看到适配计划,如果全走CPU可能性能比较差。
后面发现HarmonyOS官方提供的端侧AI框架 mindSporeLite 支持tflite模型转换后推理。MindSpore Lite 是一个轻量化、高性能的端侧AI引擎,提供了标准的模型推理和训练接口,内置通用硬件高性能算子库,原生支持Neural Network Runtime Kit使能AI专用芯片加速推理,助力打造全场景智能应用。
本文介绍基于MindSpore Lite引擎实现tflite 模型的推理实战。
模型转换
MindSpore Lite使用.ms格式模型进行推理。对于第三方框架模型,比如 TensorFlow、TensorFlow Lite、Caffe、ONNX等,可以使用MindSpore Lite提供的模型转换工具转换为.ms模型,所以我们需要先将tflite模型转换为.ms模型。
为方便演示,这里直接使用google mediapipe中提供的人脸检测开放模型。
首先需要安装模型转换工具(也可以通过源码编译获取,这里直接使用现成工具):
组件 | 硬件平台 | 操作系统 | 链接 | SHA-256 |
---|---|---|---|---|
端侧推理和训练benchmark工具、converter工具、cropper工具 | CPU | Linux-x86_64 | mindspore-lite-2.1.0-linux-x64.tar.gz | b267e5726720329200389e47a178c4f882bf526833b714ba6e630c8e2920fe89 |
MindSpore Lite模型转换工具提供了多种参数设置,我们可以根据需要来选择使,我们可以输入./converter_lite --help
获取命令参数信息。
我们使用下面命令将tflite模型转换为.ms模型:
1. ./converter_lite --fmk=TFLITE --modelFile=facedetech.tflite --outputFile=facedetech
模型部署推理
HarmonyOS MindSpore Lite 推理引擎提供了C++和ArkTS两种API,这里我们以C++接口为例介绍如何部署人脸检测模型。
首先创建上下文环境:
// 创建并配置上下文,设置运行时的线程数量为2,绑核策略为大核优先
OH_AI_ContextHandle context = OH_AI_ContextCreate();
if (context == NULL) {
printf("OH_AI_ContextCreate failed.\n");
return OH_AI_STATUS_LITE_ERROR;
}
// 优先使用NNRT推理。
// 这里利用查找到的第一个ACCELERATORS类别的NNRT硬件,来创建nnrt设备信息,并设置硬件使用高性能模式推理。还可以通过如:OH_AI_GetAllNNRTDeviceDescs()接口获取当前环境中所有NNRT硬件的描述信息,按设备名、类型等信息查找,找到某一具体设备作为NNRT推理硬件。
OH_AI_DeviceInfoHandle nnrt_device_info = OH_AI_CreateNNRTDeviceInfoByType(OH_AI_NNRTDEVICE_ACCELERATOR);
if (nnrt_device_info == NULL) {
printf("OH_AI_DeviceInfoCreate failed.\n");
OH_AI_ContextDestroy(&context);
return OH_AI_STATUS_LITE_ERROR;
}
OH_AI_DeviceInfoSetPerformanceMode(nnrt_device_info, OH_AI_PERFORMANCE_HIGH);
OH_AI_ContextAddDeviceInfo(context, nnrt_device_info);
// 其次设置CPU推理。
OH_AI_DeviceInfoHandle cpu_device_info = OH_AI_DeviceInfoCreate(OH_AI_DEVICETYPE_CPU);
if (cpu_device_info == NULL) {
printf("OH_AI_DeviceInfoCreate failed.\n");
OH_AI_ContextDestroy(&context);
return OH_AI_STATUS_LITE_ERROR;
}
OH_AI_ContextAddDeviceInfo(context, cpu_device_info);
这里创建的是NNRT(Neural Network Runtime)和CPU异构推理上下文。NNRT对接的加速硬件如NPU,推理能力较强,但支持的算子规格少;通用CPU推理能力较弱,但支持算子规格更全面。MindSpore Lite支持配置NNRT硬件和CPU异构推理:优先将模型算子调度到NNRT推理,若某些算子NNRT不支持,将其调度到CPU进行推理
接下来就是创建和加载模型:
// 创建模型
OH_AI_ModelHandle model = OH_AI_ModelCreate();
if (model == NULL) {
printf("OH_AI_ModelCreate failed.\n");
OH_AI_ContextDestroy(&context);
return OH_AI_STATUS_LITE_ERROR;
}
// 加载与编译模型,模型的类型为OH_AI_MODELTYPE_MINDIR
int ret = OH_AI_ModelBuildFromFile(model, argv[1], OH_AI_MODELTYPE_MINDIR, context);
if (ret != OH_AI_STATUS_SUCCESS) {
printf("OH_AI_ModelBuildFromFile failed, ret: %d.\n", ret);
OH_AI_ModelDestroy(&model);
return ret;
}
通过OH_AI_ModelBuildFromFile 函数从本地文件路径加载模型文件创建 Model,
接下来就是不断给模型喂数据:
// 获得输入张量
OH_AI_TensorHandleArray inputs = OH_AI_ModelGetInputs(model);
if (inputs.handle_list == NULL) {
printf("OH_AI_ModelGetInputs failed, ret: %d.\n", ret);
OH_AI_ModelDestroy(&model);
return ret;
}
//TODO 将摄像头数据写入到inputs
从模型取出代表张量空间的内存地址,将摄像头数据写入到这块内存。这里数据我们从摄像头获取,具体获取方法后面介绍。
下面就可以进行推理了。
// 执行模型推理
OH_AI_TensorHandleArray outputs;
ret = OH_AI_ModelPredict(model, inputs, &outputs, NULL, NULL);
if (ret != OH_AI_STATUS_SUCCESS) {
printf("OH_AI_ModelPredict failed, ret: %d.\n", ret);
OH_AI_ModelDestroy(&model);
return ret;
}
这里使用了OH_AI_ModelPredict函数进行推理。
最后获取推理结果:
// 获取模型的输出张量,并打印
for (size_t i = 0; i < outputs.handle_num; ++i) {
OH_AI_TensorHandle tensor = outputs.handle_list[i];
int64_t element_num = OH_AI_TensorGetElementNum(tensor);
printf("Tensor name: %s, tensor size is %zu ,elements num: %lld.\n", OH_AI_TensorGetName(tensor),
OH_AI_TensorGetDataSize(tensor), element_num);
const float *data = (const float *)OH_AI_TensorGetData(tensor);
float* out1 = outputs[1];
std::vector<float> scores(out1, out1 + 16 * 16);
float* out2 = outputs[0];
std::vector<float> boxes(out2, out2 + 16 * 16);
std::vector<float> highBox;
for (size_t j = 0; j < scores.size(); j++) {
scores[j] = 1.0f / (1.0f + std::exp(-scores[j]));
if (scores[j] > 0.9) {
for (size_t i = 0; i < 16; i++) {
highBox.push_back(boxes[i * 16 + i]);
}
float sx = highBox[0];
float sy = highBox[1];
float w = highBox[2];
float h = highBox[3];
std::cout << "MS_LITE_LOG_AI: score: sx = " << sx << ",sy = " << sy << ", w = " << w << ", h = " << h << std::endl;
float cx = sx;
float cy = sy;
cx /= 100.0f; // 假设 modelInputWidth 为 100
cy /= 100.0f; // 假设 modelInputHeight 为 100
float topleftX = cx - w * 0.5f;
float topleftY = cy - h * 0.5f;
float btmrightX = cx + w * 0.5f;
float btmrightY = cy + h * 0.5f;
std::cout << "MS_LITE_LOG_AI: score: " << scores[j] << std::endl;
std::cout << "MS_LITE_LOG_AI: topleft: " << topleftX << "," << topleftY << std::endl;
std::cout << "MS_LITE_LOG_AI: btmright: " << btmrightX << "," << btmrightY << std::endl;
for (size_t j = 0; j < 6; j++) {
float lx = highBox[4 + (2 * j) + 0];
float ly = highBox[4 + (2 * j) + 1];
lx /= 100.0f;
ly /= 100.0f;
std::cout << "MS_LITE_LOG_AI: key[" << j << "]:" << lx << "," << ly << std::endl;
}
break;
}
}
// 释放内存
for (float* ptr : outputs) {
delete[] ptr;
}
}
这里通过输出张量获取推理结果。
我们要实现实时人脸检测,需要从摄像机不断读取获取然后调用推理引擎进行推理。使用HarmonyOS 媒体接口中的camera来打开和配置摄像头。
let cameraManager = camera.getCameraManager(context);
let camerasInfo = this.cameraManager.getSupportedCameras();
cameraInput = this.cameraManager.createCameraInput(cameraDevice);
await this.cameraInput?.open()
let captureSession = this.cameraManager.createCaptureSession();
captureSession.beginConfig();
captureSession.addInput(this.cameraInput);
let previewProfile = previewProfiles[previewProfileIndex];
let componentSurfacePreviewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.surfaceId);
captureSession.addOutput(this.componentSurfacePreviewOutput);
let mReceiver = image.createImageReceiver(
previewProfile.size.width,
previewProfile.size.height,
image.ImageFormat.JPEG,
8
);
let receivingSurfaceId = await mReceiver.getReceivingSurfaceId();
let imageReceiverPreviewOutput = this.cameraManager.createPreviewOutput(previewProfile, receivingSurfaceId);
captureSession.addOutput(this.imageReceiverPreviewOutput);
mReceiver.on('imageArrival', async () => {
let imageData = await mReceiver.readNextImage();
let imageJPEGComponent = await imageData.getComponent(image.ComponentType.JPEG);
//将imageJPEGComponent.byteBuffer 数据交给推理引擎
await imageData.release();
})
await captureSession.commitConfig()
await startPreview()
参考:
总结
本文介绍了HarmonyOS 提供的MindSpore Lite推理引擎,以及如何将历史的tflite模型转换为.ms引擎,最后我们使用了HarmonyOS的camera接口来打开和配置摄像头,并创建了一个捕获会话以实时预览视频流。通过图像接收器接受摄像头采集画面数据并交给推理引擎推理得到结果并处理。要实现完整流程还需要申请摄像头权限等,为避免冗余没有展开,待完成代码整理后将工程开源出来和大家一起学习。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。