版本更新
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。
新建请求
请求的创建主要是通过sail::face::FaceRequestBuilder创建sail::face::FaceRequest请求类,在Builder里必须填写以下信息:
图像:所有的请求都需要填入RGB的图像,用sail::face::RequestBuilder::RGBImage,当前SDK只支持RGB活体识别,不用输入sail::face::RequestBuilder::InfraredImage。
RequestID:请求ID。异步请求时需要RequestID关联请求,关联操作可以让应用找到输入图像和Callback结果的关系,RequestID通过sail::face::DetectionResult::request_id返回给用户。如果是同步请求,可以任意填写一个RequestID。
SourceType:(sail::face::ImageSourceType)说明请求是摄像头数据(或者连续图片 FromCamera)还是单张图片。如果是摄像头图片,会加入跟踪算法;如果是单张图片则(FromPicture)不会做跟踪。
RequestPurpose:(sail::face::RequestPurpose) 变量主要说明请求的目的,包括以下两种:
sail::face::RequestPurpose::StreamAnalysis:说明请求需要走完整的流程,包括处理流水中的各种过滤、活体验证等,最后进行底库搜索。这是从摄像头采集数据进行分析的最常用的请求类型。
sail::face::RequestPurpose::FeatureExtract:说明请求是用来做特征提取,所以该请求不会被活体验证,最后会返回给用户抽取的Feature。目前只支持返回正常特征点,戴口罩特征暂不支持。通常用来对底库进行入库和修改。
DoFeatureSearch:主要控制是否搜索库。如果要对输入的图片进行检索,查看该图片是否在库里有对应注册的人脸,该值需要设置为
true
。
回调的说明
回调在同步或者异步输入接口执行时都会被调用,因为异步接口没有返回值,所以在异步接口模式中回调是唯一能够实现应用逻辑的过程。在同步接口中,也可以注册回调进行一些操作。
异步回调一般只注册一次,且在FaceController::Init之前。
RegisterOnDetectionResultCallback
此回调是注册在检测模块的结果出来之后被调用,当检测结果为空时,回调会被调用,用以实现某些应用在没有人脸的情况下的一些逻辑。
RegisterFacePolicy
用来注册人脸选择的策略回调,可以通过继承SailFacePolicy策略类实现,目前可以定制两种策略:
DetectionSelection和DoExtractAndSearch:DetectionSelection用来根据所有检测框的人脸框坐标、人脸质量、角度、landmark置信度等信息选择检测结果集合;DoExtractAndSearch用于对选择的检测结果集合确认是否执行后续流程。默认所有人脸都会进入后续流程,被选择的人脸后续会进行向量抽取以及向量查询等步骤。
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));
场景举例
初始建库入库
对图片抽取Feature(sail::face::FaceController::PutImageSyncV2),图片的存储和获取的Feature存储需要用户程序搞定,比如sqlite之类的数据库存储,需要注意的是,入库需要使用SourceType(sail::face::ImageSource::FromPicture)以及RequestPurpose::FeatureExtract,有以下影响:
该请求不会有跟踪,即不会被上一个请求影响。
不会检查活体的分数。
说明如果用户已经对图片进行过一次处理,可以将这些人脸特征存到自己管理的数据库里,每次初始化人脸SDK之后,对自己管理的人脸特征执行步骤2的AddFeature,不需要调用sail::face::FaceController::PutImageSyncV2之类的接口进行处理。
将抽取出来的Feature,调用sail::face::FaceController::AddFeature接口进行加入向量库中,同时需要把sail::face::FaceController::AddFeature返回的ID进行存储。
循环步骤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; }
人脸检索,和入库操作类似,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; } }
对特征库做删除
具体操作步骤如下:
用户通过自己的应用逻辑得到要删除的
unique_id
(即在AddFeature里面提供的ID),这个ID需要用户应用做存储。调用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运行步骤
解压sail-face-linux-sdk-x86-hie-xxxxxx_release.tgz。
修改SDK配置:
配置文件
config/sail_face.yaml
,common.model_dir
为模型文件夹gen_models_hie_adult_none_rgb
的路径,根据实际情况做修改(如果模型目录和运行的bin在同一目录,则可改为./gen_models_hie_adult_none_rgb
)。除了修改配置文件外,也可以参考example_export_linux.cpp的几个config_face_xxx_template配置修改
common.model_dir
,目前默认模型目录和bin在同一目录下。
申请License Key,具体操作,请参见SDK包中的文档说明。
编译测试
sample code cd example && mkdir build && cd build && cmake .. && make -j12
生成example_export_linux。执行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)
解决。