Face++(旷视)人脸检测SDK开发流程

1. SDK下载

当前最新版SDK地址
其实整个SDK里面只用到三个核心文件、三个util脚本,当然如果不想自己创建工程,它里面有一个demo是可以直接跑的。

2. 核心文件导入

SDK包含三个核心文件:

  • MGFaceppSDK-0.5.2.aar :里面包含SDK 的接口,一般使用最多的接口都封装在这里;
  • MGLicenseManagerSDK-0.3.1.aar :授权SDK,专门负责检验授权,每次应用启动第一件事就是检测授权;
  • megviifacepp_0_5_2_model :做人脸检测的模型文件,旷视将其合并压缩(很好奇为什么能做的这么小)。

两个.aar文件位于:~\MegviiFacepp_Android_0.5.2\Android-Demo\Android-Demo\faceppdemo\libs,将它们拷贝到YourProject\app\libs;模型文件位于~\MegviiFacepp_Android_0.5.2\Android-Demo\Android-Demo\faceppdemo\src\main\res\raw,将其拷贝到YourProject\app\src\main\res\raw(文件夹raw可能并不存在,手动创建就好)。

在 AS 中修改module的配置文件build.gradle:在dependencies结构中添加如下两行

compile(name: 'MGLicenseManagerSDK-0.3.1', ext: 'aar')
compile(name: 'MGFaceppSDK-0.5.2', ext: 'aar')

android结构中添加如下代码

repositories {
    flatDir {
        dirs 'libs'
    }
}

注意:官方文档给出的是compile命令,在最新的Android框架中可能会提示使用implementation去替代;name需要跟你使用的SDK版本保持一致,否则会报错。接下来重新编译工程即可:菜单栏Build -> Clean Project-> Rebuild Project。成功之后可以在工程中使用如下命令导入和使用SDK:

import com.megvii.facepp.sdk.Facepp;
import com.megvii.licensemanager.sdk.LicenseManager;
3. util脚本

这三个脚本其实并不是必需的,但是确实是要用到里面的一些方法,那既然官方写好了那我们直接用就好。

  • SharedUtil.java:用来存储数据的
  • ConUtil.java:一些实用的方法
  • Util.java:你的API_Key、Secret_Key就放在这儿

直接将这三个文件拷贝到YourProject\app\src\main\java\com\**\**\util下面,然后修改每个文件头部的package,修改Util.javakey值为你自己就好。key值需要到官网注册账号,然后新建API,绑定Bundle(在“应用管理”中可以看到),Bundle的ID最好是跟项目的applicationId保持一致(我试过不一样也能用好像~)。

4. 联网授权

SDK分为离线授权和联网授权两种,离线授权就是你买它的SDK,他给你的时候就把权限写在SDK里面了;联网授权一般是在免费使用版本中,每一次使用SDK的时候需要通过网络访问来获取权限,这里以联网授权的版本为例。

  1. 联网授权SDK(试用版)有效期为 1 天,有效期内可免费授权五次,同一APP重复使用、不卸载安装都只会在首次进行联网授权(卸载重装会占用一次),之后会将License文件保存在本地,直到License过期。
  2. 联网授权需要几个权限:INTERNETACCESS_NETWORK_STATE,然后还有关于图像数据的读写、相机权限等需要自己添加,最好是动态授权。

联网授权最关键的代码部分:

private void network() {
        // 获取联网授权类型
        int type = Facepp.getSDKAuthType(ConUtil.getFileContent(this, R.raw.megviifacepp_0_5_2_model));
        if ( type == 2) {// 非联网授权
            authState(true,0,"");
            return;
        }

        // 开始联网授权
        final LicenseManager licenseManager = new LicenseManager(this);

        String uuid = ConUtil.getUUIDString(this);      // 手机唯一序列号
        long apiName = Facepp.getApiName();             // 大概是SDK的版本名称,API自己获取不用管
        licenseManager.setAuthTimeBufferMillis(0);      // 设置时间缓冲,猜测可能跟SDK的License期限有关

        // 回调
        licenseManager.takeLicenseFromNetwork(
                Util.CN_LICENSE_URL,    // 联网授权地址
                uuid,
                Util.API_KEY,
                Util.API_SECRET,
                apiName,
                "1",                    // 使用期限,试用版本只能是1天
                new LicenseManager.TakeLicenseCallback() {
                    @Override
                    public void onSuccess() {
                        authState(true,0,"");
                    } 

                    @Override
                    public void onFailed(int i, byte[] bytes) {
                        if (TextUtils.isEmpty(Util.API_KEY)||TextUtils.isEmpty(Util.API_SECRET)) {      
                            if (!ConUtil.isReadKey(MainActivity.this)) {
                                authState(false,1001,"");
                            }else{
                                authState(false,1001,"");
                            }
                        }else{
                            String msg="";
                            if (bytes!=null&&bytes.length>0){
                                msg=  new String(bytes);
                            }
                            authState(false,i,msg);
                        }
                    }
                });
    }

    private void authState(boolean isSuccess,int code,String msg) {
        if (isSuccess) {		// 联网授权成功
            Log.d("联网授权", "成功");
            
            Intent intent = new Intent();
            intent.setClass(this, FaceActionActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
            finish();
        } else {				// 联网授权失败
            Log.d(code, msg);
        }
    }

最为关键的就是licenseManager.takeLicenseFromNetwork,它里面重写了onSuccessonFailed函数,分别会在授权成功/失败时调用。授权成功的话我们会让它跳转至FaceActionActivity.class,同时销毁当前的MainActivity,然后在FaceActionActivity里进行人脸关键点的检测,这样做的原因是:联网授权的network(其实就是里面的licenseManager.takeLicenseFromNetwork)是一个异步事件,需要在它的OnSuccess里面做检测,如果在这之后做检测可能会有问题。
官方给出的一些说明好像跟实际应用开发并不一致,比如关于权限次数:官方文档写明一个API_Key一天有五次机会,也就是说如果你把当前的API_Key删除,重新创建一个API_Key会拥有新的五次机会,但实践证明不会。可以能会得到以下的返回错误:

  • LICENSE_NOT_FOUND:删除次数已满的API_Key,使用新建的API_Key,可能会得到这个错误(它以为你用了正式API_Key,但是本地并没有找到);
  • MG_RETCODE_INVALID_LICENSE:授权失败;
  • DAILY_QUOTA_EXCEEDED:当前API_Key免费使用次数达到上限。
5. 调用SDK实现人脸关键点检测

(1)初始化实例,然后加载模型

Facepp facepp = new Facepp();
String errorCode = facepp.init(this, ConUtil.getFileContent(this, R.raw.megviifacepp_0_5_2_model),  1);

这里可能会存在R.raw不存在的问题,关掉项目重新打开一下就好;最后一个参数表示是否只检测单个人脸,0表示否,1表示是。一般如果联网授权没问题,这里的errorCode返回值是null,否则会返回具体的错误信息。
(2)检测参数设置

// 设置检测器参数(这里只列出一部分)
Facepp.FaceppConfig faceppConfig = new Facepp.FaceppConfig();
faceppConfig.detectionMode = Facepp.FaceppConfig.DETECTION_MODE_TRACKING_FAST;  // 检测模式:FAST,ROUBUST
faceppConfig.minFaceSize = 1;           // 最小人脸尺寸
faceppConfig.roi_right = w;             // 感兴趣区域
faceppConfig.roi_bottom = h;
facepp.setFaceppConfig(faceppConfig);

(3)人脸检测,获取数据

// 检测人脸,最后一个参数是输入图像的格式
final Facepp.Face[] faces = facepp.detect(imgData, w, h, Facepp.IMAGEMODE_NV21);

// 获取人脸数据
facepp.getLandmarkRaw(faces[0], Facepp.FPP_GET_LANDMARK106);
facepp.get3DPose(faces[0]);

检测人脸的时候输入数据类型是byte[],官方给出支持 NV21、BGR 格式的检测(就是detect方法的最后一个参数),但是亲测好像只支持NV21,所以最好是将图像转成 NV21 格式的byte[];如果把BGR数据送进去,最后检测到的人脸数量为0。

关于SDK能够获取到的数据可以看官方文档,基本上就是关键点、头部3D-pose、人脸属性等,有些事收费才能获取的,但是关键点和头部3D-pose是免费的。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于OpenCV和Dlib库的C++代码示例,用于姿态估计和人脸检测: ```c++ #include "opencv2/opencv.hpp" #include "dlib/opencv.h" #include "dlib/image_processing.h" #include "dlib/image_processing/frontal_face_detector.h" #include "dlib/image_processing/shape_predictor.h" #include <iostream> #include <fstream> using namespace cv; using namespace dlib; using namespace std; int main() { try { frontal_face_detector detector = get_frontal_face_detector(); shape_predictor predictor; deserialize("shape_predictor_68_face_landmarks.dat") >> predictor; VideoCapture cap(0); if (!cap.isOpened()) { cerr << "Unable to connect to camera" << endl; return 1; } namedWindow("Face Detection and Tracking", WINDOW_NORMAL); setWindowProperty("Face Detection and Tracking", WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN); while (true) { Mat frame; cap >> frame; if (frame.empty()) { cerr << "Unable to capture frame" << endl; break; } cv_image<bgr_pixel> cimg(frame); std::vector<rectangle> faces = detector(cimg); std::vector<full_object_detection> shapes; for (unsigned long i = 0; i < faces.size(); ++i) { full_object_detection shape = predictor(cimg, faces[i]); shapes.push_back(shape); } for (unsigned long i = 0; i < faces.size(); ++i) { rectangle r = faces[i]; rectangle r_scaled(r.left() / 2, r.top() / 2, r.right() / 2, r.bottom() / 2); rectangle frame_rect(0, 0, frame.cols, frame.rows); if (frame_rect.contains(r_scaled.tl()) && frame_rect.contains(r_scaled.br())) { draw_rectangle(frame, r, cv::Scalar(0, 255, 0), 2); full_object_detection shape = shapes[i]; for (unsigned long j = 0; j < shape.num_parts(); ++j) { circle(frame, cv::Point(shape.part(j).x(), shape.part(j).y()), 2, cv::Scalar(0, 0, 255), -1); } // 人脸姿态估计 std::vector<cv::Point3d> model_points; // 3D模型点 model_points.push_back(cv::Point3d(0.0f, 0.0f, 0.0f)); // 鼻尖 model_points.push_back(cv::Point3d(0.0f, -330.0f, -65.0f)); // 下巴 model_points.push_back(cv::Point3d(-225.0f, 170.0f, -135.0f)); // 左眼内角 model_points.push_back(cv::Point3d(225.0f, 170.0f, -135.0f)); // 右眼内角 model_points.push_back(cv::Point3d(-150.0f, -150.0f, -125.0f)); // 左嘴角 model_points.push_back(cv::Point3d(150.0f, -150.0f, -125.0f)); // 右嘴角 std::vector<cv::Point2d> image_points; // 2D图像点 for (unsigned long j = 0; j < shape.num_parts(); ++j) { image_points.push_back(cv::Point2d(shape.part(j).x(), shape.part(j).y())); } cv::Mat camera_matrix = (cv::Mat_<double>(3, 3) << 1.0, 0, frame.cols / 2, 0, 1.0, frame.rows / 2, 0, 0, 1.0); // 内参矩阵 cv::Mat dist_coeffs = cv::Mat::zeros(4, 1, cv::DataType<double>::type); // 4个畸变系数:k1,k2,p1,p2 cv::Mat rotation_vector; // 旋转向量 cv::Mat translation_vector; // 平移向量 cv::solvePnP(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector); // 旋转向量转换为旋转矩阵 cv::Mat rotation_matrix; cv::Rodrigues(rotation_vector, rotation_matrix); // 投影矩阵 cv::Matx34d projection_matrix( rotation_matrix.at<double>(0, 0), rotation_matrix.at<double>(0, 1), rotation_matrix.at<double>(0, 2), translation_vector.at<double>(0), rotation_matrix.at<double>(1, 0), rotation_matrix.at<double>(1, 1), rotation_matrix.at<double>(1, 2), translation_vector.at<double>(1), rotation_matrix.at<double>(2, 0), rotation_matrix.at<double>(2, 1), rotation_matrix.at<double>(2, 2), translation_vector.at<double>(2) ); // 计算欧拉角 cv::Vec3d euler_angles; cv::Matx33d rotation_matrix_ = rotation_matrix; double sy = sqrt(rotation_matrix_(0, 0) * rotation_matrix_(0, 0) + rotation_matrix_(1, 0) * rotation_matrix_(1, 0)); bool singular = sy < 1e-6; if (!singular) { euler_angles[0] = atan2(rotation_matrix_(2, 1), rotation_matrix_(2, 2)); euler_angles[1] = atan2(-rotation_matrix_(2, 0), sy); euler_angles[2] = atan2(rotation_matrix_(1, 0), rotation_matrix_(0, 0)); } else { euler_angles[0] = atan2(-rotation_matrix_(1, 2), rotation_matrix_(1, 1)); euler_angles[1] = atan2(-rotation_matrix_(2, 0), sy); euler_angles[2] = 0; } // 显示欧拉角信息 stringstream ss; ss << "Yaw: " << euler_angles[1] * 180 / CV_PI << " degrees" << endl; ss << "Pitch: " << euler_angles[0] * 180 / CV_PI << " degrees" << endl; ss << "Roll: " << euler_angles[2] * 180 / CV_PI << " degrees" << endl; cv::putText(frame, ss.str(), cv::Point(10, frame.rows - 50), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 0, 255), 2); } } imshow("Face Detection and Tracking", frame); if (waitKey(1) == 27) { break; } } destroyAllWindows(); } catch (exception& e) { cerr << e.what() << endl; } return 0; } ``` 这段代码实现了从摄像头捕捉视频流,实时检测人脸并标出关键点,同时根据人脸关键点进行姿态估计,并在视频中显示欧拉角信息。请注意,由于姿态估计需要3D模型点,因此需要预先定义这些点的坐标。在这个示例中,我们将这些点硬编码为固定值。这段代码还需要使用OpenCV和Dlib库,因此请确保在编译代码之前安装这些库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值