Linux集成开发指南

版本更新

1.0版本单摄像头RGB检测和识别流程。

主要功能列表

摩观人脸SDK(以下简称SDK)主要提供以下功能:

  • 人脸检测

  • 人脸RGB活体检测

  • 人脸1:N底库搜索,支持十万张人脸底库检索

  • 人脸1:1特征比对

规格信息

底库数量10万

内存需求:以下表格表示业务如果需要做1:N的情况下,人脸SDK额外需要的内存。

平台

1万底库

2万底库

5万底库

10万底库

Linux X86

38 MB

48 MB

84 MB

140 MB

ARM

22 MB

32 MB

67 MB

119 MB

检索速度

平台

1万底库

2万底库

5万底库

10万底库

Linux X86

0.2ms

0.4ms

1.4ms

5.6ms

ARM

20ms

41ms

102ms

204ms

说明

以上指标由最新版SDK运行在真实设备测试所得,但算法性能受实际运行设备、数据等影响,因此数据仅供参考。

耗时信息

平台

人脸检测720p

跟踪速度

RGB活体

RGB质量关键点

RGB特征抽取

NVIDIA Tesla T4

1ms

<1ms

<1ms

1ms

2ms

说明

以上指标由最新版SDK运行在真实设备测试所得,但算法性能受实际运行设备、数据等影响,因此数据仅供参考。

兼容性

  • 本SDK仅支持Linux x64 NVIDIA GPU版本。

  • 推荐使用T4或A10等NVIDIA GPU,驱动版本要求465.19.01及以上。

  • SDK未提供Debug库文件,所以请选择Release模式开发测试。

编程语言

SDK使用C++作为实现语言和对外接口。

模型包

随SDK会有一个模型包,模型包的规则是:设备名_(成人/其他场景)_(是否带口罩)_(是否带红外)

例如,gen_models_hie_adult_none_rgb表示通用PC成人不带口罩RGB活体识别模型,如果需要IR活体或增加戴口罩识别功能,请通过钉钉群(23109592)加入阿里云视觉智能开放平台咨询群联系技术支持人员。

SDK配置

配置文件

SDK包中config/目录下包含sail_face.yaml配置文件,SDK需要该配置文件进行初始化,以及配置各个阶段使用什么模型文件等信息。

代码内配置

除了sail_face.yaml配置文件外,SDK也支持配置信息在代码内以std::istream作为SDK初始化参数输入,详情请参见example_export_linux.cpp代码示例。

主要的类和错误说明

SDK的所有方法都在sail以及sail::face的namespace下,并且每个SDK公开接口都有一个API级别的自动生成文档说明,请在API文档中获取对应的函数说明。

接口类

SDK提供的主要功能在sail_face.h ( sail::face::FaceController) 中,包括提交人脸请求,通过同步接口或者异步接口得到请求的结果,以及对人脸向量特征库的各种操作,例如添加一个特征、删除一个特征、1:N的搜索、1:1的对比等功能,具体的功能说明,请参见对应文档。

说明

接口类的每一个函数都必须检查返回值。

错误码及错误信息

错误码

错误内容

错误描述

0

SF_SUCCESS

成功。

1

SF_ERR_NULL_POINTER

创建人脸特征检索会话失败。

2

SF_ERR_SIZE_WRONG

人脸特征维度不一致。

8

SF_ERR_NO_CONFIG

配置错误。

10

SF_ERR_NO_AUTH

人脸SDK鉴权失败。

11

SF_ERR_FACEDB

人脸向量数据构建或操作失败。

30

SF_ERR_NO_MEM

内存不足。

50

SF_ERR_PIPELINE

人脸算法流水线错误。

90

SF_ERR_UNKNOWN

其他错误。

100

SF_RETRY

人脸算法流水线忙,需要重试。

API使用介绍

FaceController使用方法介绍

本文从设计理念的角度出发,介绍FaceController接口的使用方法,具体每个API的使用和参数详情,请查看每个函数的API文档。

SDK主要包含两种使用方式,分别为同步接口和异步接口。

同步接口

使用sail::face::FaceController::PutImageSyncV2接口,在函数返回时,会根据创建的请求返回对应的结果,结果类是sale::face::DetectionResult,这个结果会包含所有的检测结果,以及对应的检测结果的属性等信息。

如果需要检测阶段的结果, 可以在初始化之前通过注册sail::face::FaceController::RegisterOnDetectionResultCallback接口来获取所有的检测结果,如果检测结果为空,Callback也会被调用。

同步接口是最传统的模式,但是由于算法流水线会经历多个模型,这种接口的延迟会是所有步骤相加的结果,整体性能会比异步接口低。

异步接口

使用sail::face::FaceController::PutImageAsync接口,可以异步地送入一张图片,函数会立即返回。对于检测以及检索的结果,可以通过注册的回调进行获取。sail::face::FaceController::RegisterOnDetectionResultCallback接口注册的函数会在每次检测结果出来以后被调用,应用可以在这个Callback里面做相关的逻辑。

说明

不要在Callback里做过于耗费时间的操作(比如10ms以上), 如果需要做耗费时间的操作,建议在Callback转发异步请求到其他线程。

异步方式的优势是可以尽早地在不同阶段做UI的反应,减少Camera和UI之间的延迟。例如,可以在检测结果出来后,对屏幕上的人脸做一个矩形框之类的展示。由于这个回调是在检测阶段进行的,所以比同步API能尽早地做反应,因此从体验上显得更加实时。

流程说明

下图展示了SDK内部的工作方式,以及什么时候进行Callback等信息,可以帮您更好地了解如何使用SDK。

image

新建请求

请求的创建主要是通过sail::face::FaceRequestBuilder创建sail::face::FaceRequest请求类,在Builder里必须填写以下信息:

  1. 图像:所有的请求都需要填入RGB的图像,用sail::face::RequestBuilder::RGBImage,当前SDK只支持RGB活体识别,不用输入sail::face::RequestBuilder::InfraredImage。

  2. RequestID:请求ID。异步请求时需要RequestID关联请求,关联操作可以让应用找到输入图像和Callback结果的关系,RequestID通过sail::face::DetectionResult::request_id返回给用户。如果是同步请求,可以任意填写一个RequestID。

  3. SourceType:(sail::face::ImageSourceType)说明请求是摄像头数据(或者连续图片 FromCamera)还是单张图片。如果是摄像头图片,会加入跟踪算法;如果是单张图片则(FromPicture)不会做跟踪。

  4. RequestPurpose:(sail::face::RequestPurpose) 变量主要说明请求的目的,包括以下两种:

    1. sail::face::RequestPurpose::StreamAnalysis:说明请求需要走完整的流程,包括处理流水中的各种过滤、活体验证等,最后进行底库搜索。这是从摄像头采集数据进行分析的最常用的请求类型。

    2. sail::face::RequestPurpose::FeatureExtract:说明请求是用来做特征提取,所以该请求不会被活体验证,最后会返回给用户抽取的Feature。目前只支持返回正常特征点,戴口罩特征暂不支持。通常用来对底库进行入库和修改。

  5. DoFeatureSearch:主要控制是否搜索库。如果要对输入的图片进行检索,查看该图片是否在库里有对应注册的人脸,该值需要设置为true

回调的说明

回调在同步或者异步输入接口执行时都会被调用,因为异步接口没有返回值,所以在异步接口模式中回调是唯一能够实现应用逻辑的过程。在同步接口中,也可以注册回调进行一些操作。

异步回调一般只注册一次,且在FaceController::Init之前。

RegisterOnDetectionResultCallback

此回调是注册在检测模块的结果出来之后被调用,当检测结果为空时,回调会被调用,用以实现某些应用在没有人脸的情况下的一些逻辑。

RegisterFacePolicy

用来注册人脸选择的策略回调,可以通过继承SailFacePolicy策略类实现,目前可以定制两种策略:

  1. DetectionSelection和DoExtractAndSearch:DetectionSelection用来根据所有检测框的人脸框坐标、人脸质量、角度、landmark置信度等信息选择检测结果集合;DoExtractAndSearch用于对选择的检测结果集合确认是否执行后续流程。默认所有人脸都会进入后续流程,被选择的人脸后续会进行向量抽取以及向量查询等步骤。

  2. LivenessSelection:对所有检测进行过滤。

RegisterOnFaceCompareResultCallback

此回调会在进行人脸选择、活体测试、向量抽取、向量库搜索后完成后续结果回调,即同步API得到的返回值的结果。

人脸向量库

人脸向量库是基于内存的,在启动时清空,需要在每次进程启动时,把存在用户数据库(比如sqlite)的向量和UID重新添加到向量库中。向量库包括正常的人脸库(全脸)和口罩的人脸库(半脸,本版本暂不支持)。

需要注意的是,在添加或者删除特征向量操作后, 如果想让搜索结果生效, 需要调用 sail::face::FaceController::RebuildFaceDBIndex重新创建。用户可以选择多次调用添加或者删除操作,最终调用一次sail::face::FaceController::RebuildFaceDBIndex这种模式来进行批量操作。

向量的添加和查询是以用户输入的Key为标识符,这个Key需要用户保证唯一。

以下API接口保证线程安全:

  • AddFeature:添加一个特征向量到向量库中并和一个Key进行关联。

  • RemoveFeature:从向量库中删除一个Key和对应的向量。

  • RebuildFaceDBIndex:重新构建索引,添加和删除向量后,需要调用RebuildFaceDBIndex使搜索API生效。

  • SearchFeature:从向量库中找出N个和目标向量最相似的向量和Key,并按照相似程度高低进行排序。这个数据结构是QueryResult如果对库进行了修改,需要调用SearchFeature进行修改。

  • CompareFeature:输入两个向量,进行向量的比较,会返回向量的比较结果和搜索使用同一个结构体。

说明

以上API接口的第一个参数是向量数据库的指针,该指针可以通过sail::face::FaceController::GetNormalFaceDB()获取正常的向量数据库。

口罩

本版本暂不支持该功能,如有业务需要,请通过钉钉群(23109592)加入阿里云视觉智能开放平台咨询群联系技术支持人员。

使用示例

初始化

FaceController controller;
controller.Init(g_model_folder, auth_store)

获取图片

获取图像的方式有两种:

  • 通过硬件API获取到Camera的图像,通常是YUV格式,其次将YUV转换成sail::HardwareImage的类,可能会进行内存拷贝,更多信息,请参见example_export_linux.cpp里的GetSailImageColorRefFromYuv实现。

  • JPEG、JPG等图片格式的文件,可以通过SDK自带的DecodeJpegFile函数进行读取解码成为 HardwareImage;PNG、JPG或其他格式,请参考example_export_linux.cpp里的GetSailImageColorRef实现(依赖OpenCV)。

输入图片请求

构建输入图片请求,代码示例如下:

FaceRequestBuilder builder;
DetectionResult result;
auto request_ptr = builder.RGBImage(copied_image_ref)
                           .RequestID(request_id++)
                           .SourceType(sail::face::ImageSourceType::FromCamera)
                           .build();

同步请求

通过调用controller.PutImageSyncV2()进行同步送入图片,该图片的分析和搜索结果会在函数结束返回,如果有异常,会通过错误码返回,更多详细信息可通过日志查看。

auto result = controller_.PutImageSyncV2(std::move(request_ptr), err_code);

异步请求

通过调用controller.PutImageASync()进行异步请求,同时在注册的回调中进行事件处理。

controller_.PutImageAsync(std::move(request_ptr));

场景举例

初始建库入库

  1. 对图片抽取Feature(sail::face::FaceController::PutImageSyncV2),图片的存储和获取的Feature存储需要用户程序搞定,比如sqlite之类的数据库存储,需要注意的是,入库需要使用SourceType(sail::face::ImageSource::FromPicture)以及RequestPurpose::FeatureExtract,有以下影响:

    1. 该请求不会有跟踪,即不会被上一个请求影响。

    2. 不会检查活体的分数。

    说明

    如果用户已经对图片进行过一次处理,可以将这些人脸特征存到自己管理的数据库里,每次初始化人脸SDK之后,对自己管理的人脸特征执行步骤2的AddFeature,不需要调用sail::face::FaceController::PutImageSyncV2之类的接口进行处理。

  2. 将抽取出来的Feature,调用sail::face::FaceController::AddFeature接口进行加入向量库中,同时需要把sail::face::FaceController::AddFeature返回的ID进行存储。

  3. 循环步骤1和步骤2,直到所有的入库图片、特征都进行入库, 调用sail::face::FaceController::RebuildFaceDBIndex重建索引。( 重建索引可以在每次sail::face::FaceController::AddFeature之后都调用,但是批量处理可以只最后调用一次。)

    说明

    添加步骤也可以用来做单个Feature的添加。

    假设有一批JPEG格式的图片需要处理,user_id是用户指定的每个图片的唯一ID,示例代码如下:

    ASSERT_EQ(g_controller.Init(g_config_path, g_auth_store), SF_SUCCESS);
    
    for (auto f : file_paths_of_jpeg) {
      auto request_ptr = build_request_from_jpg(f, 1, false); // 这是一个helper函数, 
      SFErrorCode err_code;
      auto result = g_controller.PutImageSyncV2(std::move(request_ptr), err_code)
      ASSERT_EQ(err_code, SF_SUCCESS);
      if(result->detections.size() > 0){
        std::shared_ptr<FaceFeature> feature = result->detections.begin()->feature;
        g_controller.AddFeature(controller_.GetNormalFaceDB(), user_id++, *feature); // 用户需要保证user_id的唯一性, 这里只是示例.
    }
    ASSERT_EQ(g_controller.RebuildFaceDBIndex(controller_.GetNormalFaceDB()), SF_SUCCESS); 
    
    /// --- Helper function reference code.
    std::unique_ptr<FaceRequest> build_request_from_jpg(string img_path, uint64_t id=1, bool do_search=true){
       HardwareImageColorRef bgr_image_ref = DecodeJpegFile(img_path, GetPlatformMemAllocator()); 
        if(bgr_image_ref->GetSize()[0]<64 || bgr_image_ref->GetSize()[1]<64){
            std::cout<<"Img too small(low 64). size: "
                     <<bgr_image_ref->GetSize()[0]
                     <<bgr_image_ref->GetSize()[1]<<std::endl;
            return nullptr;
        }
    
        FaceRequestBuilder builder;
        auto request_ptr = builder.RGBImage(bgr_image_ref)
                                  .SourceType(ImageSourceType::FromPicture)
                                  .RequestPurpose(RequestPurpose::FeatureExtract)
                                  .RequestID(id)
                                  .DoFeatureSearch(do_search)
                                  .build();
        return request_ptr;
    }
  4. 人脸检索,和入库操作类似,RequestPurpose需要从RequestPurpose::FeatureExtract改成RequestPurpose::StreamAnalysis。示例代码如下:

    // 检索人脸
    auto request_ptr = build_request_from_jpg(test_img);
    request_ptr->purpose_ = RequestPurpose::StreamAnalysis;
    if (request_ptr == nullptr) {
      std::cout << "build_request_from_jpg error";
      return;
    }
    
    result = g_controller.PutImageSyncV2(std::move(request_ptr), err_code);
    if (err_code != SF_SUCCESS) {
      std::cout << "PutImageSyncV2 erro: " << static_cast<int>(err_code)
                << std::endl;
      return;
    }
    std::cout << "result size: " << result->GetFaceDetBoxes().size() << std::endl;
    for (auto& det : result->GetFaceDetBoxes()) {
      if (det->GetSearchResult() && det->GetSearchResult()->items_.size() != 0) {
        auto item = det->GetSearchResult()->items_[0];
        std::cout << "score: " << item.score_ << ", id: " << item.key_
                  << ", is_same_person: " << item.same_person_
                  << ", live: " << det->GetLiveness();
        std::cout << std::endl;
      }
    }

对特征库做删除

具体操作步骤如下:

  1. 用户通过自己的应用逻辑得到要删除的unique_id(即在AddFeature里面提供的ID),这个ID需要用户应用做存储。

  2. 调用sail::face::FaceController::RemoveFeature()接口进行删除,再调用sail::face::FaceController::RebuildFaceDBIndex()让删除向量生效。

SDK包解压以及Demo代码运行

发布包内容

sail-face-linux-sdk-x86-hie-eaee3e5_release.tgz:SDK的软件Package,里面包含SDK的so、C++ header、文档、示例、配置文件以及模型文件夹gen_models_hie_adult_none_rgb。

Demo运行步骤

  1. 解压sail-face-linux-sdk-x86-hie-xxxxxx_release.tgz。

  2. 修改SDK配置:

    1. 配置文件config/sail_face.yamlcommon.model_dir为模型文件夹gen_models_hie_adult_none_rgb的路径,根据实际情况做修改(如果模型目录和运行的bin在同一目录,则可改为./gen_models_hie_adult_none_rgb)。

    2. 除了修改配置文件外,也可以参考example_export_linux.cpp的几个config_face_xxx_template配置修改common.model_dir,目前默认模型目录和bin在同一目录下。

  3. 申请License Key,具体操作,请参见SDK包中的文档说明。

  4. 编译测试sample code cd example && mkdir build && cd build && cmake .. && make -j12生成example_export_linux。

  5. 执行example_export_linux。需要准备两张JPG格式的图像,可以是同一个人,或者是不同人进行对比。

    说明

    如果用带配置文件sail_face.yaml的命令运行,配置文件需要存在-c指定的目录(此处是config),同时,配置文件里的信息要跟example_export_linux.cpp里的config_face_xxx_template配置一致。

功能

方法一

方法二

人脸1:1

./example_export_linux -b 第一个JPG图片 -t 第二张JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m compare

./example_export_linux -c config -b 第一个JPG图片 -t 第二张JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m compare

人脸识别

./example_export_linux -b 第一个JPG图片 -t 第二张JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m reco

./example_export_linux -c config -b 第一个JPG图片 -t 第二张JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m reco

人脸检测

./example_export_linux -b 第一个JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m detect_only

./example_export_linux -c config -b 第一个JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m detect_only

人脸检测活体

./example_export_linux -b 第一个JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m detect_liveness

./example_export_linux -c config -b 第一个JPG图片 -l 鉴权license文件 -u 鉴权project_uudi(可以不输入) -m detect_liveness

FAQ

1. 初始化失败(Load config error)如何处理?

检查是否将安装包的config/sail_face.yaml文件放入初始化参数的路径中,并且配置文件里的common.model_dir是否正确配置模型文件目录。

2. 初始化失败(算法无模型)如何处理?

如果出现了类似的错误Algo SDK Init failed,大多数情况是因为模型的配置和模型的包不匹配导致某些文件缺失。

3. 如何申请授权License?

Linux x86 SDK License请根据网页引导信息完成鉴权文件申请并下载到本地。

4. 是否可以只运行某些功能,而不用某些节点?

总的来说,流水线是可以裁剪的,没有做成统一的方式,可以分场景进行裁剪。

5. 口罩Feature和正常Feature可以对比吗?

口罩Feature和正常Feature是两个空间的向量,无可比性。目前该版本只支持正常人脸功能。

6. 如何判断是否戴口罩?

目前该版本只支持正常人脸功能,暂不支持口罩功能,如有业务需要,请通过钉钉群(23109592)加入阿里云视觉智能开放平台咨询群联系技术支持人员。

7. 编译问题

如果编译出现类似undefined reference to `sail::face::FaceController::Init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<sail::face::AuthInfoStore>)' 问题,可能是由于GCC版本较高引起的,可以在CMakeLists.txt里添加add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) 解决。