# FaceDetectSDK
**Repository Path**: librastalker/face-detect-sdk
## Basic Information
- **Project Name**: FaceDetectSDK
- **Description**: 百度前端实战训练营大作业项目,实现了图像人脸框和关键点检测模型的SDK封装,以及基于vue开发的调用人脸检测模型SDK的展示界面。
- **Primary Language**: TypeScript
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 0
- **Created**: 2023-01-28
- **Last Updated**: 2025-12-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 基于Paddle.js WebGL计算方案实现人脸关键点检测SDK
结合PaddleDetection blazeface_1000e人脸检测模型和PaddleHub face_landmark模型实现人脸关键点检测;
模型地址:
- blazeface_1000e https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.5/configs/face_detection
- face_landmark_localization https://github.com/PaddlePaddle/PaddleHub/tree/release/v2.3/modules/image/keypoint_detection/face_landmark_localization
## 参考版本
- Paddle.js 2.2.5 https://github.com/PaddlePaddle/Paddle.js/tree/release/v2.2.5
- PaddleDetection: 2.5 https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.5
- PaddleHub: 2.3 https://github.com/PaddlePaddle/PaddleHub/tree/release/v2.3
## 作业说明
本次作业包含两个文件夹,`facekeypoint`文件夹是本次作业的主要内容,封装了图像->人脸框+关键点 的全流程SDK,`face_detect_demo`文件夹是基于vue开发的展示界面,使用了`facekeypoint`中封装好的SDK来检测图像的人脸框和关键点。
## 作业展示



### 展示说明
- 本次作业基于Vue3+antd-vue,仿照百度AI开放平台的人脸检测功能演示区域实现了一个展示界面,该展示界面调用了自己封装好的人脸检测SDK来进行人脸检测,可以访问 http://www.librastalker.top/index.html 查看我的展示界面。
- 本次作业还部署了一个简易版本的测试用的展示界面,网址为[paddlejs mobilenet demo (librastalker.top)](http://www.librastalker.top/demo/index.html),来源于`Paddle.js/packages/paddlejs-models/facedetect/src/demo`,略有修改。
- 展示界面可以通过网络图片URL,本地图片,以及页面的示例图片三种方式加载图片进行人脸检测。
- 展示界面右上角有图片检测和实时检测两个功能切换按钮,但是目前只实现了图片的人脸检测功能。实时检测需要通过摄像头捕获视频流,来对视频中的人脸进行实时检测,但是由于自己的设备性能较低,实时检测时设备经常会卡崩掉,难以测试,所以这部分暂时还没实现视频流人脸检测的功能。
### 注意事项
- 首次进入展示页面时会加载一张默认图片,初始化模型进行检测。模型初始化需要一定时间,初始化时可能会造成卡顿,用户设备性能低的话可能无法顺利进行检测,会出现闪退等情况。
- 使用网络图片URL进行人脸检测时,由于跨域问题可能无法加载图片检测。
## 亮点介绍
- 封装了 图像->人脸框+关键点 的全流程SDK
- 符合 https://ai.baidu.com/sdk 的接口命名
- 代码分层良好,借助webpack模块化开发
- README文档完善
- 代码基本符合ESLint规范,参数类型明确
- 注释完善,函数签名完善,明确说明了函数的作用、参数和返回值的含义
- 函数长短合理,逻辑性合理,遵循高内聚低耦合的设计模式
## 完成情况
### 已完成
- 人脸框检测模型的前后处理、初始化
- 人脸关键点检测模型的前后处理、初始化
- 封装 图像->人脸框+关键点 的全流程SDK
- 符合 https://ai.baidu.com/sdk 的接口命名
### 待完成
- 将人脸识别模型换成blazeface_1000e
> 使用tools/model-check检查模型后发现blazeface_1000e人脸检测模型有4个算子没有完成
> ```
> python tools/model-check/check/check.py --modelPath=packages/paddlejs-converter/origin_model/blazeface_1000e/model.pdmodel --paramPath=packages/paddlejs-converter/origin_model/blazeface_1000e/model.pdiparams
> Model has unsupported op !
> ['exp', 'slice', 'multiclass_nms3', 'stack']
> Model can run in PC WebGL successfully.
> Model can run in WISE WebGL successfully.
> ```
> 实现了一部分slice算子后发现算子开发较难、耗时较长,可能无法全部实现,于是使用了原先`packages/paddlejs-models/facedetect/src/index.ts`中的模型与face_landmark_localization模型进行人脸框和关键点检测。
- E2E验证
## 使用方法
### node开发环境
在终端里运行
```shell
npm install
npm run dev
```
浏览器打开
`http://localhost:8868`
### node生产环境
在终端里运行
```shell
npm install .
```
index.ts
```TypeScript
import {createImage, FaceDetector, interfaces} from 'facekeypoint';
const {ModelConfig, FaceInfo} = interfaces;
const imgPath = 'img/multi_small_face.jpeg';
const face_detect_model_config: ModelConfig = {
modelPath: 'https://paddlejs.cdn.bcebos.com/models/fuse/facedetect_opt/model.json',
mean: [0.407843137, 0.458823529, 0.482352941],
std: [0.5, 0.5, 0.5],
feedShape: {
fc: 3,
fw: 1024,
fh: 1024
}
};
const face_keypoint_model_config: ModelConfig = {
modelPath: 'model/face_landmark_localization/model.json',
feedShape: {
fc: 1,
fw: 60,
fh: 60
}
};
const imgEle = await createImage(imgPath);
const img_bitmap = await createImageBitmap(imgEle);
const faceDetector = new FaceDetector(face_detect_model_config, [['landmark68', face_keypoint_model_config]]);
await faceDetector.init();
const res: FaceInfo = await faceDetector.detect(img_bitmap);
console.log(res);
```
由于内部逻辑使用了DOM,需要使用html-webpack-plugin等在浏览器端执行,暂时无法直接在控制台中执行
### script标签引用
在终端里运行
```shell
npm run build
```
index.html
```HTML
```
index.js
```JavaScript
const {createImage, FaceDetector, interfaces} = paddlejs.facekeypoint;
// const {ModelConfig, FaceInfo} = interfaces;
const imgPath = 'img/multi_small_face.jpeg';
const face_detect_model_config = {
modelPath: 'https://paddlejs.cdn.bcebos.com/models/fuse/facedetect_opt/model.json',
mean: [0.407843137, 0.458823529, 0.482352941],
std: [0.5, 0.5, 0.5],
feedShape: {
fc: 3,
fw: 1024,
fh: 1024
}
};
const face_keypoint_model_config = {
modelPath: 'model/face_landmark_localization/model.json',
feedShape: {
fc: 1,
fw: 60,
fh: 60
}
};
async function run(){
const imgEle = await createImage(imgPath);
const img_bitmap = await createImageBitmap(imgEle);
const faceDetector = new FaceDetector(face_detect_model_config, [['landmark68', face_keypoint_model_config]]);
await faceDetector.init();
const res = await faceDetector.detect(img_bitmap);
console.log(res);
}
run();
```
### 返回说明
| 字段 | 必选 | 类型 | 说明 |
| :--: | :--: | :--: | :--: |
|+location | 是 | array |人脸在图片中的位置 |
|++left | 是 | number |人脸区域离左边界的距离|
|++top | 是 | number |人脸区域离上边界的距离|
|++width | 是 | number |人脸区域的宽度|
|++height | 是 | number |人脸区域的高度|
|+face_probability| 是 | number |人脸置信度,范围【0~1】,代表这是一张人脸的概率,0最小、1最大。|
|+landmark68 | 否 |array |68个特征点位置 face_field包含landmark68时返回|
### 返回示例
```json
[
{
"face_probability": 0.9999551773071289,
"location": {
"left": 189.60514831542966,
"top": 373.5043487548827,
"width": 58.93386840820313,
"height": 76.06300354003906
},
"landmark68": [
{
"x": 189.74958720803258,
"y": 403.02942955493916
},
{
"x": 189.10191151499745,
"y": 412.0189151763915
},
{
"x": 189.1603065729141,
"y": 420.4263811111449
},
...
]
},
{
"face_probability": 0.9996778964996338,
"location": {
"left": 735.6203918457031,
"top": 31.359802246093693,
"width": 53.21441650390626,
"height": 68.90792846679688
},
"landmark68": [
{
"x": 737.0279772281647,
"y": 61.23914527893061
},
{
"x": 736.0815037488937,
"y": 68.29021310806269
},
{
"x": 736.9159166812897,
"y": 74.30404806137079
},
...
]
},
...
]
```
## facekeypoint文件结构
- demo —— 来源于`Paddle.js/packages/paddlejs-models/facedetect/src/demo`,略有修改,展示用
- index.html —— 一个简易的展示界面
- index.ts
- img —— 示例图片,来源于`Paddle.js/packages/paddlejs-models/facedetect/src/img`,无修改,展示用
- multi_small_face.jpeg
- single_big_face.jpeg
- lib —— `npm run build`的输出目录
- index.js —— 编译好的SDK,可以直接使用script标签导入
- LICENSE —— 与Paddle.js的开源协议相同
- model —— 所有的预训练模型
- blazeface_1000e —— 由原始的PaddlePaddle模型转换后的Paddle.js模型
- chunk_1.dat —— 模型参数
- model.json —— 模型结构
- face_landmark_localization —— 由原始的PaddlePaddle模型转换后的Paddle.js模型
- chunk_1.dat —— 模型参数
- model.json —— 模型结构
- origin_model —— 原始PaddlePaddle模型
- blazeface_1000e —— 使用PaddleDetection导出的人脸框检测模型
- infer_cfg.yml
- model.pdiparams
- model.pdiparams.info
- model.pdmodel
- face_landmark_localization —— 使用PaddleHub导出的人脸关键点检测模型
- model.pdiparams
- model.pdmodel
- package.json
- README.md
- src —— 主要代码
- commons
- interface.ts —— 所有的接口
- customOp —— 自己实现的算子
- abs.ts
- modelSDK —— 封装的SDK
- base.ts —— 所有检测类的基类
- facedetector.ts —— 人脸框检测
- landmark68.ts —— 人脸关键点检测
- index.ts —— 入口文件
- tsconfig.json
- unused —— 废稿与半成品
- new_slice_test.ts —— 测试slice算子
- slice.ts —— 自己实现的slice算子
- webpack.dev.js —— 开发环境配置
- webpack.prod.js —— 生产环境配置
### 目录说明
|目录|说明|
|-|-|
|demo|来源于`Paddle.js/packages/paddlejs-models/facedetect/src/demo`,略有修改,展示用|
|img|来源于`Paddle.js/packages/paddlejs-models/facedetect/src/img`,无修改,展示用|
|lib|`npm run build`的输出目录,可以直接使用script标签导入其中的`index.js`,即可使用SDK|
|model|所有的预训练模型|
|src|项目的主要代码|
|unused|废稿与半成品|
## 项目架构
```TypeScript
import {interfaces, createImage, FaceDetector} from 'facekeypoint';
```
### interfaces
包含了所有的接口,属性:
#### all_supported_face_field
在人脸框检测的基础上所有支持的额外检测负载,目前只有一个68关键点检测,后续可以扩展,比如增加表情识别等
```TypeScript
export const all_supported_face_field = {
landmark68: Landmark68
};
```
#### FeedShape
提供给模型的输入的形状
```TypeScript
export interface FeedShape {
// 通道数
fc: number;
// 宽度
fw: number;
// 高度
fh: number;
}
```
#### ModelConfig
模型的可选配置
```TypeScript
export interface ModelConfig {
// 模型的路径,可以为相对路径或网络URL
modelPath?: string;
// 输入的图像转化成0-1之间的浮点数后的三个通道的平均值,预处理时对输入标准化时用到
mean?: number[];
// 输入的图像转化成0-1之间的浮点数后的三个通道的标准差,预处理时对输入标准化时用到
std?: number[];
// 提供给模型的输入的形状
feedShape?: FeedShape; // 因为有的模型里自带了这个参数所以这个参数不是必需的
}
```
#### NaturalSize
输入图像的原始宽度和高度
```TypeScript
export interface NaturalSize {
naturalWidth: number;
naturalHeight: number;
}
```
#### Location
人脸框的位置,左、上、宽、高,单位为像素
```TypeScript
export interface Location {
// 人脸图片的左边界与原始图片的左边界的像素距离,一般为非负数
left: number;
// 人脸图片的上边界与原始图片的上边界的像素距离,一般为非负数
top: number;
// 人脸图片的像素宽度,正数
width: number;
// 人脸图片的像素高度,正数
height: number;
}
```
#### Coordinate
关键点相对于原始图片的坐标,x横坐标,y纵坐标,单位为像素
```TypeScript
export interface Coordinate {
x: number;
y: number;
}
```
#### FaceInfo
模型最终的返回结果,API命名参考:https://ai.baidu.com/sdk
```TypeScript
export interface FaceInfo {
// 人脸框的位置,左、上、宽、高
location: Location;
// 人脸框的置信度
face_probability: number;
// 68个人脸关键点的坐标
landmark68?: Coordinate[];
}
```
### createImage
从图片路径创建图片元素,等待图片加载完成后再resolve,来源于`Paddle.js/packages/paddlejs-models/facedetect/src/index.ts`
**参数**
- **imgPath** 图片的路径,可以是相对路径或网络URL
**返回**
- resolve图片元素的Promise
```TypeScript
function createImage(imgPath: string): Promise {
return new Promise(resolve => {
const image = new Image();
image.crossOrigin = 'anonymous';
image.onload = () => {
resolve(image);
};
image.src = imgPath;
});
}
```
### FaceDetector
人脸检测的核心部分
**属性**
#### modelConfig: *ModelConfig*
由于模型配置只在初始化时用到,因此如果需要更改配置,就直接重新实例化一个类吧。
#### feedShape: *FeedShape*
提供给模型的输入的形状
#### face_field: *[string, ModelSDK][]*
需要使用的额外检测负载,ModelSDK的意思是ModelSDK的子类的实例。
**方法**
#### constructor
初始化 modelConfig 和 face_field
**参数**
- **modelConfig** 模型配置
- **face_field** 需要使用的额外检测负载
**返回**
- 无
```TypeScript
constructor(modelConfig?: ModelConfig, face_field: [string, ModelConfig?][] = []): void;
```
#### init
初始化当前模型和所有的额外负载,在当前模型和所有额外负载都初始化完成后才会resolve
**返回**
- Promise,在当前模型和所有额外负载都初始化完成后才会resolve
```TypeScript
async init(): Promise;
```
#### detect
从图片中检测所有的人脸,并对所有检测出的人脸区域使用检测负载进行再次检测,并添加到结果中
**参数**
- **input** 原始图片,可以通过createImageBitmap()获取
- **opts** 可选参数
- **opts.threshold** 取值范围是0-1,默认0.6,表示只取置信度大于此数值的人脸
- **opts.shrink** 取值范围是0-1,默认0.4,预测前先将图片按此比例缩小,对于占图片比例较大的人脸可以提升检测效果
**返回**
- 每个人脸识别的结果,包含了所有请求负载,返回的格式参考https://ai.baidu.com/sdk
```TypeScript
async detect(
input: ImageBitmap,
opts?
): Promise;
```
**注意:一定要先确保init方法resolve了才能调用detect**
```TypeScript
const faceDetector = new FaceDetector(face_detect_model_config, [['landmark68', face_keypoint_model_config]]);
await faceDetector.init();
const res: FaceInfo = await faceDetector.detect(img_bitmap);
console.log(res);
```
## 参考文献
[1] https://github.com/PaddlePaddle/Paddle.js
[2] https://github.com/PaddlePaddle/PaddleDetection
[3] https://github.com/PaddlePaddle/PaddleHub
[4] https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/getContext
[5] https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/shaderSource
[6] https://github.com/PaddlePaddle/Paddle.js/issues/392
[7] https://stackoverflow.com/questions/29059306/three-js-webgl-invalid-operation-bindtexture-object-not-from-this-context/29067763
[8] https://stackoverflow.com/questions/51582282/error-when-creating-textures-in-webgl-with-the-rgb-format
[9] https://www.webpackjs.com/configuration/output/#outputlibrary
[10] https://arxiv.org/abs/1907.05047