人脸 98 关键点识别
修订履历
| NO | 版本 | 修订内容 | 修订日期 |
|---|---|---|---|
| 1 | Ver1.0 | 新建 | 2026/06/30 |
本文档中的信息可能会因文档改进而在不事先通知的情况下变更。最新版请参阅本公司网站。
未经株式会社日昇科技书面许可,严禁以任何形式复制本文档。
1. 人脸关键点概要
人脸关键点定位也称为人脸关键点检测或人脸对齐。它是针对输入的人脸图像,估计眉毛、眼睛、鼻子、嘴巴、面部轮廓等面部重要区域位置的处理过程。与人脸检测类似,由于会受到姿态变化、遮挡等因素影响,人脸关键点检测也是一项难度较高的任务。
人脸关键点检测具有多种重要的应用场景。
・人脸姿态对齐:在人脸识别等算法中,为提高模型精度,需要进行人脸姿态对齐。
・人脸校正与编辑:基于关键点,可以准确分析脸型、眼部形状、鼻部形状等,并对人脸特定部位进行处理,从而实现人脸特效、美颜处理、贴纸叠加等娱乐功能。
・表情分析与唇部识别:基于关键点,可以分析面部表情,并用于互动娱乐、行为预测等场景。
本脸部 98 点关键点算法的关键点位置如下图所示:

图1-1 人脸 98 关键点索引定义
算法在数据集上的性能:
| 人脸关键点算法 | NME(%) |
| 300W | 2.78 |
| COFW | 3.08 |
| AFLW | 1.42 |
在 CSUN RV1126B 上的运行效率如下:
| 算法类型 | 模型大小 | 运行效率 |
| face_detect | 44.23MB | 17ms |
| face_landmark98 | 10.88MB | 23ms |
2. 快速开始
2.1 开发环境准备
如果您是首次阅读本文档,请参考《入门指南/开发编译环境的准备与更新》,并按照相关步骤部署编译环境。
在 PC 端 Ubuntu 系统中执行 run 脚本,进入 Docker 开发环境。步骤如下:
cd ~/linuxshare/work/rv1126b/jp/embedded/images/develop_environment./run.sh 2204
图2-1 Docker 开发环境启动
2.2 源代码下载
在 Docker 开发环境中,创建用于保存源代码仓库的管理目录。
cd /opt/linuxshare/work/rv1126b/jp/AI/demo/ai-algorithm使用 git 工具,在管理目录中克隆远程仓库。
git clone https://github.com/csunltd/rv1126b-ai-toolkit.git※ 即使从 GitHub 网页下载,也请下载整个仓库。请不要只单独下载与本示例对应的目录。
※ 如果已经下载完成,可以跳过此步骤。
2.3 模型部署
要运行算法 Demo,首先需要下载人体关键点识别算法模型。
下载链接:
人脸检测模型:
https://dl.dragonwake.com/download/rv1126b/AI/demo/01_face-detect.zip
人脸关键点识别模型:
https://dl.dragonwake.com/download/rv1126b/AI/demo/11_face_landmark98.zip
之后,请将下载的人脸检测模型和人体关键点识别模型复制到 Release/ 目录。

图2-2 Release 目录内的文件配置
2.4 示例构建
进入示例所在目录并执行构建。具体命令如下:
cd rv1126b-ai-toolkit/Demos/algorithm-face_landmark98/./build.sh cpres* 依赖库已配置在开发板上,因此交叉编译过程中需要保持 /mnt 挂载。
sudo mount -t nfs -o vers=3,proto=tcp,mountproto=tcp,nolock,retrans=5,timeo=5 192.168.10.85:/ /mnt* 为 build.sh 脚本指定 cpres 参数后,Release/ 目录内的所有资源都会被复制到开发板。

图2-3 示例构建结果
2.5 示例运行及结果
将已编译的文件复制到基板。
cp Release/\* /mnt/userdata/Demo/algorithm-face_landmark98/※ 如果使用 ./build.sh cpres 编译,会自动复制。
通过串口调试或 SSH 调试进入开发板后台,并按如下方式移动到示例部署目录。
cd /userdata/Demo/algorithm-face_landmark98/执行示例的命令如下:
./test-face-landmark98 test.jpg
图2-4 示例运行结果
在 Docker 开发环境中,将测试结果图片从基板复制出来。
cp /mnt/userdata/Demo/algorithm-face_landmark98/result.jpg .
图2-5 结果图像复制命令
识别结果如下图所示。

图2-6 人脸 98 关键点识别结果图像
关于 API 的详细说明以及 API 调用方式(本示例的源代码),请参见以下内容。
3. 人脸检测API 说明
3.1 引用方法
为了使用户能够从本地项目中直接调用 EASY EAI API 库,本列表列出了项目需要链接的库和头文件,便于用户直接添加。
| 项目 | 说明 |
| 头文件目录 | easyeai-api/algorithm/face_detect |
| 库文件目录 | easyeai-api/algorithm/face_detect |
| 库链接参数 | -lface_detect |
3.2 人脸检测初始化函数
人脸检测初始化函数原型如下。
int face_detect_init(rknn_context \*ctx, const char \*path)详细说明如下。
| 函数名: face_detect_init() | |
| 头文件 | face_detect.h |
| 输入参数 | ctx:rknn_context 句柄 |
| 输入参数 | path:算法模型路径 |
| 返回值 | 成功时返回值:0 |
| 失败时返回值:-1 | |
| 注意事项 | 无 |
3.3 人脸检测执行函数
人脸检测执行函数 face_detect_run 原型如下。
int face_detect_run(rknn_context ctx, cv::Mat &input_image, std::vector\<det\> &result)详细说明如下。
| 函数名:face_detect_run() | |
| 头文件 | face_detect.h |
| 输入参数 | ctx:rknn_context 句柄 |
| 输入参数 | input_image:图像数据输入(cv::Mat 为 OpenCV 类型) |
| 输出参数 | result:目标检测框输出 |
| 返回值 | 成功时返回值:0 |
| 失败时返回值:-1 | |
| 注意事项 | 无 |
3.4 人脸检测释放函数
人脸检测释放函数原型如下。
int face_detect_release(rknn_context ctx)详细说明如下。
函数名:face_detect_release ()
| 头文件 | face_detect.h |
| 输入参数 | ctx:rknn_context 句柄 |
| 返回值 | 成功时返回值:0 |
| 失败时返回值:-1 | |
| 注意事项 | 无 |
4. 人脸 98 点关键点API 说明
4.1 引用方法
为了使用户能够从本地项目中直接调用 EASY EAI API 库,本项目需要链接的库和头文件如下所示。用户可以直接添加。
| 项目 | 说明 |
| 头文件目录 | easyeai-api/algorithm_api/face_landmark98 |
| 库文件目录 | easyeai-api/algorithm_api/face_landmark98 |
| 库链接参数 | -lface_landmark98 |
4.2 人脸 98 点关键点初始化函数
人脸 98 点关键点初始化函数原型如下。
int face_landmark98_init(rknn_face_landmark_context_t \*p_face_landmark, const char \*p_model_path)详细说明如下。
| 函数名: face_landmark98_init() | |
| 头文件 | face_landmark98.h |
| 输入参数 | p_face_landmark:rknn_face_landmark_context_t句柄 |
| 输入参数 | p_model_path:算法模型路径 |
| 返回值 | 成功时返回值:0 |
| 失败时返回值:-1 | |
| 注意事项 | 无 |
4.3 人脸 98 点关键点执行函数
face_landmark98_run 函数原型如下。
std::vector\<cv::Point\> face_landmark98_run(cv::Mat image, rknn_face_landmark_context_t \*p_face_landmark)详细说明如下。
| 函数名:face_landmark98_run () | |
| 头文件 | face_landmark98.h |
| 输入参数 | p_face_landmark:rknn_face_landmark_context_t句柄 |
| 输入参数 | image:图像数据输入(cv::Mat 为 OpenCV 类型) |
| 返回值 | std::vector<cv::Point>:算法输出的人脸关键点坐标 |
| 注意事项 | 无 |
4.4 人脸 98 点关键点释放函数
人脸 98 点关键点释放函数原型如下。
int face_landmark98_release(rknn_face_landmark_context_t\* p_face_landmark)详细说明如下。
| 函数名:face_landmark98_release () | |
| 头文件 | face_landmark98.h |
| 输入参数 | p_face_landmark:rknn_face_landmark_context_t句柄 |
| 返回值 | 成功时返回值:0 |
| 失败时返回值:-1 | |
| 注意事项 | 无 |
5. 人体关键点识别示例
示例目录为 Demos/algorithm-face_landmark98/test-face-landmark98.cpp,操作流程如下。

图5-1 人脸 98 关键点识别算法处理流程
参考示例如下。
#include <stdio.h>#include <stdint.h>#include <stdlib.h>#include <fstream>#include <iostream>#include <vector>#include <fstream>#include <atomic>#include <queue>#include <thread>#include <mutex>#include <chrono>#include <sys/time.h>#include <sys/stat.h>#include <dirent.h>#include <unistd.h>
#include <opencv2/opencv.hpp>
#include <unistd.h>#include <sys/syscall.h>#include "face_detect.h"#include "face_landmark98.h"
using namespace std;using namespace cv;
int main(int argc, char **argv){
if( argc != 2) { printf("./test-face-landmark98 xxx.jpg \n"); return -1; }
struct timeval start; struct timeval end; float time_use=0;
rknn_context detect_ctx; std::vector<det> result; int ret;
cv::Mat src; src = cv::imread(argv[1], 1);
face_detect_init(&detect_ctx, "./face_detect.model");
/* 顔ランドマーク位置推定を初期化します */ rknn_face_landmark_context_t face_landmark;
ret = face_landmark98_init(&face_landmark, "./face_landmark98.model"); if( ret < 0) { printf("face_mask_judgement_init fail! ret=%d\n", ret); return -1; }
gettimeofday(&start,NULL); face_detect_run(detect_ctx, src, result);
gettimeofday(&end,NULL); time_use=(end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);//マイクロ秒 printf("face_detect time_use is %f\n",time_use/1000);
printf("face num:%d\n", (int)result.size());
for (int i = 0; i < (int)result.size(); i++) { int x = (int)(result[i].box.x); int y = (int)(result[i].box.y); int w = (int)(result[i].box.width); int h = (int)(result[i].box.height); int max = (w > h)?w:h;
// 画像の切り出し範囲が境界を超えていないか確認します if( (x < 0) || (y < 0) || ((x +max) > src.cols) || ((y +max) > src.rows) ) { continue; }
// 顔位置 cv::Rect rect(x, y, w, h); cv::Mat face_roi =src(rect).clone();
gettimeofday(&start,NULL); std::vector<cv::Point> keys = face_landmark98_run(&face_landmark, face_roi);
gettimeofday(&end,NULL); time_use=(end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);//マイクロ秒 printf("face_landmark time_use is %f\n",time_use/1000);
// 元画像上の位置 for (int i = 0; i < (int)keys.size(); i++) { keys[i].x += rect.x; keys[i].y += rect.y; }
for (int i = 0; i < (int)keys.size(); i++) { printf("keyPoints %d :[%d, %d]\n", i, keys[i].x, keys[i].y); cv::circle(src, keys[i], 2, CV_RGB(0, 255, 0), 2); }
//cv::rectangle(src, rect, CV_RGB(0, 255, 0), 2);
}
cv::imwrite("result.jpg", src);
/* 顔検出を解放します */ face_detect_release(detect_ctx);
/* 顔ランドマーク位置推定を解放します */ face_landmark98_release(&face_landmark);
return 0;}