热门搜索:
美妆娱乐模块
人脸分析
更新时间:2023-07-14 18:02:10
本页目录

人脸分析模块

简介

人脸分析模块提供人脸关键点检测以及人脸属性分析能力。

  • 依赖资源文件

face_info_pack.bin

  • 人脸106关键点索引图

Description

  • 人脸198点关键点索引图 V1

    V1版本198点关键点包含20点眉毛关键点(左右各10),32点眼睛关键点(左右各16)以及40点嘴唇关键点

Description

  • 人脸256关键点索引图 V2

    在106点关键点的基础上,拓展增加了嘴唇、眼睛、眉毛精细关键点,以及新增虹膜关键点。

    其中左右眉毛各新增13点,左右眼睛各22点,嘴唇40点,瞳孔各20点,共新增150点,完整提供256点关键点。

Description

  • 瞳孔关键点索引图

Description

技术规格

支持平台 Android、iOS、Windows、Mac、Linux
支持角度 yaw ≤ ±90° pitch ≤ ±90°
支持输入格式 RGB888
支持最大人脸数 10
支持距离 人脸占图片短边1/10以上
支持最小输入尺寸 短边20px

接口说明

  • 初始化

注意初始化前需要获取联网权限以完成授权

mlsdk::MLSDKEngineBase* engine = mlsdk::GetMLSDKEngineInstance("/path/to/license", ${machineid});
mlsdk::InitModel(engine, MLMODEL_FACE, face_model_path);
  • 人脸检测
cv::Mat original = cv::imread("/path/to/image");
cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
std::vector<mlsdk::FaceInfoOutput> faceInfoList;
std::vector<unsigned char> image_data(image.data, image.data + image.total() * image.channels());
mlsdk::HumanFaceDetection(engine, image_data, image.cols, image.rows, mt, LANDMARKTYPE_EXTRA_LANDMARKS_V2, &faceInfoList);
  • 销毁引擎
mlsdk::DestroyMLSDKEngineInstance(engine);

API参数

 int HumanFaceDetection(MLSDKEngineBase *engine, const std::vector<unsigned char> &input, int width, int height,
                           MLMediaType mediaType, uint64_t config,
                           std::vector<FaceInfoOutput> *output)
  • 参数说明

    参数名 参数说明
    engine 初始化引擎时获得的句柄
    input 连续紧致的RGB缓冲区,其大小为输入原图的width * height * 3, 内容为输入原图的RGBRGB排列的,无须行对齐的、行优先存储的像素
    width 原图宽
    height 原图高
    mediaType 可选:MLMediaType_Image = 1,MLMediaType_Video 注意不要混用图像模式与视频模式,如需同时使用多个模式,请初始化多个engine实例。
    config 选择精细关键点类型、是否打开性别年龄颜值检测、是否打开人脸分割检测等
  • config配置

​ config目前可以由以下部分取或组成:

const uint64_t CONFIG_LMT_NO_EXTRA_LANDMARKS = 0b01; // 无额外关键点
const uint64_t CONFIG_LMT_EXTRA_LANDMARKS_V1 = 0b10; // 198点
const uint64_t CONFIG_LMT_EXTRA_LANDMARKS_V2 = 0b100; // 256点修改版

// 默认完全不检测性别年龄颜值【开启需要有对应的授权】
const uint64_t CONFIG_HUMANFACE_EXTRACT_ATTR = 0x10; //  打开人脸属性识别: 性别年龄人种
const uint64_t CONFIG_HUMANFACE_NO_ATTR = 0x30; //  默认:关闭人脸属性识别: 性别年龄人种

// 默认完全不分割,通过下面分别打开【开启需要有对应的授权】
const uint64_t CONFIG_HUMANFACE_PARSING = 0x200;   // 嘴唇/牙齿/人头分割,注意需要对应的权限
const uint64_t CONFIG_HUMANFACE_NO_PARSING = 0x100;   // 默认,不执行
cpp

举例:打开256点,检测性别年龄颜值,但不需要分割,则调用

CONFIG_LMT_EXTRA_LANDMARKS_V2 | CONFIG_HUMANFACE_NO_PARSING | CONFIG_HUMANFACE_EXTRACT_ATTR
  • 结果类型 输出结果是一个数组vector,其中每一个FaceInfoOutput代表其中检测到的一个人脸信息,详细结构参考头文件。使用人脸额外关键点、人脸属性、人脸动作识别前,应主动检查返回结果大小,未授权部分结果将保持为空。
struct SegmentationOutput {
  std::vector<float> probmap;
  bool probmap_valid; // 只有probmap_valid=true, 整个结构才有意义
  int width;
  int height;

  int raw_x;  // 对应区域在原图左上角x坐标
  int raw_y;  // 对应区域在原图左上角y坐标
  int raw_width;  // 对应区域在原图的宽
  int raw_height; // 对应区域在原图的高
};

struct FaceInfoOutput {
  DetectionOutput det;
  LandmarkOutput lm;                      // 基础关键点
  LandmarkOutput extra_mouth;             // 精细嘴部关键点
  LandmarkOutput extra_left_eye;          // 精细左眼关键点
  LandmarkOutput extra_right_eye;         // 精细右眼关键点
  LandmarkOutput extra_left_eyebrow;      // 精细左眉毛关键点
  LandmarkOutput extra_right_eyebrow;     // 精细右眉毛关键点
  LandmarkOutput extra_left_iris;         // 精细左瞳孔关键点
  LandmarkOutput extra_right_iris;        // 精细右瞳孔关键点
  std::vector<float> attributes;          // 人脸属性:年龄、性别、颜值
  std::vector<std::string> attributes_str;// 人脸属性对应的中文说明
  std::vector<bool> static_expression;    // 人脸动作识别:静态动作,如嘴巴张开的状态
  std::vector<bool> dynamic_expression;   // 人脸动作识别:动态动作,如张嘴的动作
  SegmentationOutput faceparsing;         // 人脸分割结果,包含嘴唇、口腔、脸部、头发等
};

结果解析

人脸关键点

人脸关键点位于对应的类型为LandmarkOutput的结构体中,对于2D关键点,一般是xyxyxy...的排列,以左上角为(0, 0), 右下角为(width-1, height-1)

人脸姿态

人脸姿态位于 det中,分别是pitch, yaw, roll, 以角度为单位,正对屏幕的人脸为(0, 0, 0), 逆时针为负,顺时针为正

人脸分割

人脸分割包括对以下几个部分的分割结果

  • 背景[0, 1]
  • 脸部区域[2, 3]
  • 头发(含帽子)[4,5]
  • 口腔(含牙齿)[6, 7]
  • 嘴唇[8, 9]

若需要获取人脸分割结果,需要获取对应的授权,且config带上CONFIG_HUMANFACE_PARSING, 分割结果位于faceparsing,注意判断faceparsing.probmap_valid=true,否则分割无效

人脸属性识别

人脸属性需要对应授权,并且config需要带上CONFIG_HUMANFACE_EXTRACT_ATTR, 默认是关闭的。人脸属性位于attributes,下标含义为:

enum HumanFaceAttrType{
   HUMAN_ATTR_GENDER = 0,
   HUMAN_ATTR_RACE_0 = 1,
   HUMAN_ATTR_RACE_1 = 2,
   HUMAN_ATTR_RACE_2 = 3,
   HUMAN_ATTR_RACE_3 = 4,
   HUMAN_ATTR_AGE = 5,
   HUMAN_ATTR_BEAUTY = 6,
};

其中:

  • 性别[0-0.5]男性,(0.5, 1]女性

  • 年龄为真实年龄,一般为[0, 100]

  • 颜值范围是[0, 100], 该效果受数据集影响较大,具体业务方可定制标准

人脸动作识别

人脸动作识别目前提供共17类静态动作以及5类动态动作,静态动作可由单张图判断,动态动作是指需要多帧图片才可以获得的,对于MLMediaType_Image模式,结果无效。 人脸返回结果中,以下两个数组内,true部分表示该位置对应的人脸动作被触发。

std::vector<bool> static_expression;  // 人脸动作识别:静态动作,如嘴巴张开的状态
std::vector<bool> dynamic_expression;   // 人脸动作识别:动态动作,如张嘴的动作

具体的位置下标为:

enum DynamicExpression
{
    EYE_BLINK = 0,  // 眨眼
    MOUTH_AH = 1,   // 张嘴
    HEAD_YAW = 2,   // 摇头
    HEAD_PITCH = 3, // 点头
    BROW_JUMP = 4   // 挑眉
};

enum StaticExpression
{
    HEAD_NORMAL = 0,    // 正脸
    FACE_YAW_LEFT = 1, // 正左脸
    FACE_YAW_RIGHT = 2,    // 正右脸
    FACE_PITCH_YAW_LEFT = 3,   // 倾斜左脸
    FACE_PITCH_YAW_RIGHT = 4,  // 倾斜右脸
    RISED_HEAD = 5,  // 抬头
    BOWED_HEAD = 6, // 低头
    TWO_EYE_CLOSE = 7,  // 双眼闭
    TWO_EYE_OPEN = 8,   // 双眼睁
    LEFTEYE_OPEN_RIGHTEYE_CLOSE = 9,    // 左眼睁右眼闭
    LEFTEYE_CLOSE_RIGHTEYE_OPEN = 10,   // 左眼闭右眼睁
    CLOSED_MOUTH = 11,   // 闭嘴
    OPENED_MOUTH = 12,    // 张嘴
    LIPS_SMILE = 13,  // 嘴角上扬,类似微笑
    LIPS_POUTED = 14,  // 嘟嘴
    LIPS_SMIRK_LEFT = 15,       // 左上歪嘴龙王笑
    LIPS_SMIRK_RIGHT = 16,      // 右上歪嘴龙王笑
};

解析示例

  static std::vector<std::string> static_expression_names = {
        "HEAD_NORMAL",
        "FACE_YAW_LEFT",
        "FACE_YAW_RIGHT",
        "FACE_PITCH_YAW_LEFT",
        "FACE_PITCH_YAW_RIGHT",
        "RISED_HEAD",
        "BOWED_HEAD",
        "TWO_EYE_CLOSE",
        "TWO_EYE_OPEN",
        "LEFTEYE_OPEN_RIGHTEYE_CLOSE",
        "LEFTEYE_CLOSE_RIGHTEYE_OPEN",
        "CLOSED_MOUTH",
        "OPENED_MOUTH",
        "LIPS_SMILE",
        "LIPS_POUTED",
        "LIPS_SMIRK_LEFT",
        "LIPS_SMIRK_RIGHT",
};

static std::vector<std::string> dynamic_expression_names = {
        "EYE_BLINK",
        "MOUTH_AH",
        "HEAD_YAW",
        "HEAD_PITCH",
        "BROW_JUMP",
};

...
...
  
  if(!faceInfoList.empty()){
  	// pitch yaw roll
  	cv::putText(image, "Pit: " + std::to_string(faceInfoList[0].det.pitch), {0, 10}, cv::FONT_HERSHEY_PLAIN, 0.5, {0, 255, 0}, 1);
  	cv::putText(image, "Yaw: " + std::to_string(faceInfoList[0].det.yaw), {0, 35}, cv::FONT_HERSHEY_PLAIN, 0.5, {0, 255, 0}, 1);
  	cv::putText(image, "Rol:" + std::to_string(faceInfoList[0].det.roll), {0, 60}, cv::FONT_HERSHEY_PLAIN, 0.5, {0, 255, 0}, 1);
  	// static expression
  	for(int i=0; i < faceInfoList[0].static_expression.size(); i++){
  		bool exp = faceInfoList[0].static_expression[i];
  		if(exp){
  			cv::putText(image, static_expression_names[i], {0, 60 + 15 * (1 + i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {255, 255, 255}, 1);
  		}else{
  			cv::putText(image, static_expression_names[i], {0, 60 + 15 * (1+ i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {128, 128, 128}, 1);
  		}
  	}

	for(int i=0; i < faceInfoList[0].dynamic_expression.size(); i++){
		bool exp = faceInfoList[0].dynamic_expression[i];
		if(exp){
			cv::putText(image, dynamic_expression_names[i], {200, 60 + 15 * (1 + i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {255, 255, 255}, 1);
		}else{
			cv::putText(image, dynamic_expression_names[i], {200, 60 + 15 * (1+ i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {128, 128, 128}, 				1);
		}
	}

完整示例



 cv::Mat image = cv::imread("/path/to/image");
 cv::Mat original = image.clone();
 cv::cvtColor(image, image, cv::COLOR_BGR2RGB);	// 网络输入使用RGB buffer
 std::vector<mlsdk::FaceInfoOutput> faceInfoList;
 std::vector<unsigned char> image_data(image.data, image.data + image.total() * image.channels());
 mlsdk::HumanFaceDetection(engine, image_data, image.cols, image.rows, mt, CONFIG_LMT_EXTRA_LANDMARKS_V2, &faceInfoList);
 printf("%lu faces detected\n", faceInfoList.size());
 for(auto face: faceInfoList){
	auto& box = face.det.box;
	for(int _i=0; _i < face.lm.landmarks.size()/2 && _i < 106; _i++){
		cv::circle(image, {(int)face.lm.landmarks[_i*2], (int)face.lm.landmarks[_i*2+1]}, 2, {255, 0, 0}, -1);
	}

	for(auto& lm: {face.extra_right_eye, face.extra_left_eye, face.extra_mouth, face.extra_left_iris, face.extra_left_eyebrow, 				face.extra_right_iris, face.extra_right_eyebrow}){
		for(int _i=0; _i < lm.landmarks.size()/2; _i++){
			cv::circle(image, {(int)lm.landmarks[_i*2], (int)lm.landmarks[_i*2+1]}, 2, {0, 255, 0}, -1);
		}
	}	
}

  if(!faceInfoList.empty()){
  	// pitch yaw roll
  	cv::putText(image, "Pit: " + std::to_string(faceInfoList[0].det.pitch), {0, 10}, cv::FONT_HERSHEY_PLAIN, 0.5, {0, 255, 0}, 1);
  	cv::putText(image, "Yaw: " + std::to_string(faceInfoList[0].det.yaw), {0, 35}, cv::FONT_HERSHEY_PLAIN, 0.5, {0, 255, 0}, 1);
  	cv::putText(image, "Rol:" + std::to_string(faceInfoList[0].det.roll), {0, 60}, cv::FONT_HERSHEY_PLAIN, 0.5, {0, 255, 0}, 1);
  	// static expression
  	for(int i=0; i < faceInfoList[0].static_expression.size(); i++){
  		bool exp = faceInfoList[0].static_expression[i];
  		if(exp){
  			cv::putText(image, static_expression_names[i], {0, 60 + 15 * (1 + i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {255, 255, 255}, 1);
  		}else{
  			cv::putText(image, static_expression_names[i], {0, 60 + 15 * (1+ i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {128, 128, 128}, 1);
  		}
  	}

	for(int i=0; i < faceInfoList[0].dynamic_expression.size(); i++){
		bool exp = faceInfoList[0].dynamic_expression[i];
		if(exp){
			cv::putText(image, dynamic_expression_names[i], {200, 60 + 15 * (1 + i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {255, 255, 255}, 1);
		}else{
			cv::putText(image, dynamic_expression_names[i], {200, 60 + 15 * (1+ i)}, cv::FONT_HERSHEY_PLAIN, 0.5, {128, 128, 128}, 				1);
		}
	}
}

cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
cv::imshow("demo", image);