更新记录

1.0.6(2026-07-29)

-优化uniappx

1.0.5(2026-07-27)

-新增相机实时人脸识别

1.0.4(2026-07-27)

-优化

查看更多

平台兼容性

uni-app(4.18)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × 5.0 4.18
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × - × - - - -

uni-app x(4.18)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

多语言 暗黑模式 宽屏模式
×

ly028-OffFace 离线人脸检测识别特真提取

真实案例体验

门禁 / 考勤类真实项目落地(ly028-reg):人脸注册、人脸库管理、拍照识别、实时相机流识别

扫码下载真实案例
📱 下载真实案例 APK(ly028-reg)

真实案例截图 1 真实案例截图 2 真实案例截图 3 真实案例截图 4


全功能 Demo 体验

插件官方全功能演示 App,覆盖检测、追踪、流式识别、活体、模型管理等全部 API 与示例页面。

扫码下载全功能 Demo
📱 下载全功能 Demo APK

鸿蒙实测图

iOS 实测图


基于 SeetaFace6 的人脸检测 UTS 原生插件,Android / iOS / HarmonyOS Next 三端完美支持,导出函数完全一致,一套代码三端运行。全离线运行,无需网络连接,数据不出设备。

特性

  • 🔒 全离线 — 本地推理,无需联网,人脸数据不出设备
  • 📱 三端完美一致 — Android / iOS / HarmonyOS Next 导出 API 完全相同,调用端无平台差异
  • 🎯 30+ 个导出函数 — 检测、关键点、特征提取、活体、年龄、性别、口罩、眼睛、姿态、质量、1:1 比对、1:N 识别、人脸库管理
  • 🧩 UTS 插件 — uni-app/uni-app x 原生插件,按需集成

平台兼容

平台 最低版本 架构
Android 7.0 (API 21) arm64-v8a, armeabi-v7a
iOS 12.0 arm64
HarmonyOS Next API 9+ arm64-v8a

集成与调试

HBuilderX 自定义基座运行

  1. 插件市场购买插件后,在 manifest.json → App 原生插件配置中选择 ly028-OffFace
  2. 选择需要使用的模块
  3. 自定义调试基座:菜单栏 → 运行 → 运行到手机或模拟器 → 制作自定义调试基座
  4. 制作完成后,选择自定义基座运行到真机即可调试所有功能

首次使用请确保已关联 uni-app 开发者证书,自定义基座有效期为 7 天,过期后需重新制作。

离线打包

插件为原生代码插件,离线打包需自行集成原生依赖。

插件包体积优化

本插件将模型文件从插件包移出,插件市场包体积从 89MB 降至 38MB

方案 体积
插件市场包(不含模型) ~38 MB
完整功能(含模型) 模型放入项目 static/models/ 后自动加载

方式一:自动发现(推荐)

在项目 static/ 目录下创建 models/ 文件夹,放入 .csta 模型文件,插件初始化时自动发现并加载

YourProject/
├── static/
│   └── models/                    ← 放入 .csta 模型文件
│       ├── face_detector.csta
│       ├── face_recognizer_light.csta
│       ├── face_landmarker_pts5.csta
│       ├── ...
│       └── quality_lbn.csta
├── uni_modules/
│   └── ly028-OffFace/              ← 插件包(不含模型)
import * as OffFace from '@/uni_modules/ly028-OffFace'

// 自动发现 static/models/ 下的模型
OffFace.initFaceEngineAsync(null, null, (res) => {
    const initResult = JSON.parse(res)
    if (initResult.code === 0) {
        console.log('引擎初始化成功')
    }
})

方式二:自定义目录名

模型放在不同目录时,通过 modelAssetDir 参数指定:

OffFace.initFaceEngineAsync(null, null, (res) => {
    // ...
}, 'offface_models')   // 模型放在 static/offface_models/ 下

方式三:远程下载

不放置模型文件,通过 CDN 下载缺失模型(需要 modelBaseUrl 参数):

OffFace.initFaceEngineAsync('https://your-cdn.com/models/', null, (res) => {
    // ...
})

注意:引擎初始化时不下载模型,仅扫描已存在的文件。缺失的大模型需调用 downloadModelAsync() 单独下载:

OffFace.downloadModelAsync(
  'face_recognizer.csta',
  'https://your-cdn.com/models/',
  (progressJson) => {
    const p = JSON.parse(progressJson)
    console.log(p.name, p.pct)  // 持续进度(@UTSJS.keepAlive)
  },
  (resultJson) => {
    const r = JSON.parse(resultJson)
    console.log('download done', r.code)
  }
)

// 2. 异步检测人脸 OffFace.detectFileAsync('/path/to/image.jpg', (res) => { const result = JSON.parse(res) if (result.code === 0 && result.data.faceCount > 0) { console.log('检测到', result.data.faceCount, '张人脸') console.log('第一个人脸:', result.data.faces[0]) } })

// 3. 释放引擎 OffFace.releaseEngine()


## 全量 API 参考

### 引擎生命周期

| API | 说明 |
|-----|------|
| `initFaceEngineAsync(modelBaseUrl?, modelPath?, callback, modelAssetDir?)` | 初始化引擎,结果通过 callback 返回。`modelAssetDir` 指定 `static/` 下的模型目录名,默认自动发现 `models/` |
| `releaseEngine()` | 释放引擎 |
| `setAppLanguage(language)` | 设置语言(`zh-Hans` / `en`) |

### 人脸检测

| API | 说明 |
|-----|------|
| `detectFace(imageData, width, height, channels)` | 从 RGBA 像素数组检测 |
| `detectWithOptions(imageData, w, h, ch, minSize, threshold, flags)` | 全功能检测(flags 按位组合) |
| `detectFileAsync(filePath, callback)` | 异步文件检测,结果通过 callback 返回 |
| `detectFileWithOptionsAsync(filePath, options, callback)` | 带选项异步文件检测 |
| `detectTestImage(width, height)` | 测试图像检测 |

**flags 位定义**:

| 位 | 功能 |
|----|------|
| 1 | 关键点 |
| 2 | 年龄(需额外模型) |
| 4 | 性别 |
| 8 | 口罩 |
| 16 | 眼睛状态 |
| 32 | 姿态角 |
| 64 | 活体 |
| 128 | 质量评估 |

### 活体检测

| API | 说明 |
|-----|------|
| `detectFileLivenessAsync(filePath, callback)` | 异步活体检测 |
| `checkLivenessReadyAsync(callback)` | 异步检查活体模型是否就绪 |

返回 `liveness` 值:`real`(真实)、`spoof`(伪造)、`fuzzy`(模糊)、`detecting`(检测中)

### 人脸跟踪(FaceTracker)

基于 SeetaFace6 `FaceTracker` 的视频流人脸跟踪,适用于摄像头逐帧场景。三端(Android / iOS / HarmonyOS)API 完全一致。

| API | 说明 |
|-----|------|
| `initTracker(width, height, minFaceSize?, threshold?)` | 初始化跟踪器 |
| `trackAsync(options, callback)` | 异步逐帧跟踪(推荐),详见下方 |

`trackAsync` 接收 `TrackFrameOptions` 参数,通过 callback 返回跟踪结果:

```typescript
import { trackAsync } from '@/uni_modules/ly028-OffFace'

trackAsync({
    data: base64ImageData,   // Base64 帧数据
    width: 1920,             // 图像宽度
    height: 1080,            // 图像高度
    frameNo: frameNo,        // 帧序号(递增)
    format: 'nv12',          // 'nv12'(默认)|'rgba'|'nv21'|'i420'|'bgr'
    detailMode: 1,           // 0=基础跟踪|1=含详细属性
}, (result: string) => {
    const data = JSON.parse(result)
    console.log('人脸数:', data.faceCount)
})

TrackFrameOptions:

字段 类型 必填 默认值 说明
data string 每帧图像的像素缓冲区,Base64 编码字符串。来源:由 ly028-Camera 插件或其他摄像头方案采集到的一帧完整像素数据,编码为 Base64 后传入
width number 图像宽度(像素)
height number 图像高度(像素)
frameNo number 帧序号(逐帧递增)
format string "nv12" 像素格式,应与 data 解码前的原始像素格式一致(nv12 / rgba / nv21 / i420 / bgr)。推荐 NV12,iOS 摄像头原生输出格式,无需额外转换
detailMode number 0 0=基础跟踪 1=含详细属性

data 的原始像素格式由 format 参数描述,解码时需要匹配。例如 format: "nv12" 时,Base64 解码后的字节应为 NV12 排列的 YUV 数据。三端统一由 ly028-Camera 插件提供 Base64 帧数据,调用方无需关心内部编码细节。

跟踪配置:

API 说明
trackerSetMinFaceSize(size) 最小人脸尺寸(默认 80)
trackerSetThreshold(th) 检测阈值(默认 0.9)
trackerSetVideoStable(stable) 视频稳定模式,减少 PID 跳变
trackerReset() 重置跟踪器(PID 重新分配)
stopTrackAsync() 解除 trackAsync 背压

流式人脸库识别(Stream Identify)

相机逐帧喂入 → 与人脸库比对 → 命中时回调一次。相机开/关由应用自行控制。

API 说明
startStreamIdentifyAsync(options, onHit, ?) 开始识别会话
streamIdentifyFrameAsync(frame) 喂入一帧(fire-and-forget,禁止 await)
stopStreamIdentify() 结束会话

StreamIdentifyOptions:

字段 类型 默认 说明
threshold number 0.6 相似度阈值
topN number 1 Top-N,范围 1..10
skipEvery number 1 每 N 帧识别 1 次
minFaceSize number 引擎默认 最小人脸尺寸
detectThreshold number 0.9 检测阈值

StreamIdentifyFrame:

字段 类型 默认 说明
data string Base64 帧数据(配合 ly028-Camera)
width number 图像宽度
height number 图像高度
frameNo number 可选,帧序号
format string "nv12" nv12 / rgba / nv21 / i420 / bgr

回调说明:

  • onHit(result) — 识别命中时回调一次,JSON 信封内 datauserIdsimilarity
  • onProgress(rectsJson)(可选)— 人脸框数组 JSON,可传给 updateFaceRects 绘制 overlay
  • 未命中时不触发 onHit
import {
  startStreamIdentifyAsync,
  streamIdentifyFrameAsync,
  stopStreamIdentify
} from '@/uni_modules/ly028-OffFace'

// 1. 开始会话
startStreamIdentifyAsync(
  { threshold: 0.6, topN: 1, skipEvery: 1 },
  (result) => {
    const env = JSON.parse(result)
    // 命中:处理 userId / similarity,然后结束
    stopStreamIdentify()
  },
  (rectsJson) => {
    // 可选:更新相机人脸框 overlay
    const rects = JSON.parse(rectsJson)
    // updateFaceRects(rects)
  }
)

// 2. 相机 onFrame 内喂帧(不要 await)
streamIdentifyFrameAsync({
  data: base64Frame,
  width,
  height,
  format: 'nv12'
})

// 3. 用户关闭相机或离开页面时
stopStreamIdentify()

使用须知:

  • 需先初始化引擎与人脸库,并至少注册一张脸
  • streamIdentifyFrameAsync 为 fire-and-forget,不要在回调里 await
  • 推荐相机参数:format: 'nv12'maxFps: 5(与跟踪示例一致)
  • 命中后请调用 stopStreamIdentify() 并关闭相机

流式识别用于实时查人脸库;trackAsync 用于实时跟踪出框与属性,两者可独立使用。

跟踪结果(detailMode: 1 时)

{
  "faceCount": 2,
  "faces": [{
    "PID": 1,
    "rect": {"x": 100, "y": 200, "width": 150, "height": 180},
    "score": 0.95,
    "frameNo": 42,
    "step": 3,
    "landmarks": [{"x": 120, "y": 250}, {"x": 180, "y": 250}],
    "age": 25,
    "gender": "male",
    "mask": false,
    "maskScore": 0.02,
    "eyeLeft": "open",
    "eyeRight": "open",
    "yaw": 5.2, "pitch": 3.1, "roll": -1.8,
    "quality": {
      "brightness": {"level": "high", "score": 0.92},
      "clarity": {"level": "high", "score": 0.88},
      "integrity": {"level": "high", "score": 0.95},
      "pose": {"level": "high", "score": 0.90},
      "resolution": {"level": "medium", "score": 0.65},
      "pose_ex": {"level": "high", "score": 0.91}
    }
  }]
}

智能降频detailMode: 1 时属性检测每 3 帧执行一次完整管道,中间帧按 PID 合并最近属性结果,避免逐帧全量推理的性能开销。 空闲复位:距上次 track 超过 5 秒自动重置跟踪器状态。 反压:上一帧未处理完时新帧直接丢弃,避免队列积压。

模块管理

API 说明
getModelStatusAsync(callback) 异步获取模型下载状态
getMissingModels() 获取缺失模型列表
downloadModelAsync(name, baseUrl, , onComplete) 异步下载模型(@UTSJS.keepAlive 持续进度回调)
getLoadStatus() 获取模块加载状态
getModelLoadStatusByFilesAsync(callback) 异步按文件查加载状态
ensureModuleLoaded(moduleName) 确保模块已加载
ensureModulesLoaded(names) 批量确保加载
loadModule(name, modelDir) 手动加载模块

人脸识别(FaceDatabase)

API 说明
initFaceDatabaseAsync(modelType, callback) 异步初始化人脸库(light / std)
switchFaceDatabaseAsync(modelType, callback) 异步切换活跃模型
getFaceDatabaseStatusAsync(callback) 异步获取双库状态

特征提取

API 说明
extractFeatureAsync(imageData, width, height, landmarks, callback) 异步提取人脸特征向量

人脸注册

API 说明
registerFaceAsync(imageData, w, h, landmarks, userId, callback) 异步注册人脸
registerFaceByFileAsync(filePath, landmarks, userId?, callback) 从文件注册(空 userId 自动生成)
updateFaceByFileAsync(filePath, landmarks, userId, callback) 更新已注册人脸

1:N 识别

API 说明
identifyFaceAsync(imageData, w, h, landmarks, callback) 异步最佳匹配识别
identifyFaceByFileAsync(filePath, landmarks, callback) 从文件识别
identifyTopNAsync(imageData, w, h, landmarks, n, callback) 异步 Top-N 识别
identifyTopNByFileAsync(filePath, landmarks, topN, callback) 从文件 Top-N

人脸管理

API 说明
deleteFaceAsync(userId, callback) 异步删除人脸
clearFacesAsync(callback) 异步清空所有人脸
getFaceCountAsync(callback) 异步获取人脸数
exportFeaturesAsync(callback) 异步导出所有人脸特征
compareFacesAsync(feature1, feature2, callback) 异步 1:1 特征比对

配置

API 说明
setMinFaceSize(size) 最小人脸像素(默认 40)
setDetectThreshold(threshold) 检测阈值(默认 0.9)

返回值格式

所有 API 返回统一 JSON 信封:

// 成功
{"code": 0, "data": {...}}

// 失败
{"code": 1, "subCode": 500, "msg": "错误描述"}

检测结果结构

{
  "code": 0,
  "data": {
    "faceCount": 1,
    "imageWidth": 1920,
    "imageHeight": 1080,
    "faces": [{
      "rect": {"x": 100, "y": 200, "width": 300, "height": 400},
      "score": 0.98,
      "landmarks": [{"x": 150, "y": 250}, ...],
      "age": 25,
      "gender": "male",
      "mask": false,
      "maskScore": 0.02,
      "eyeLeft": "open",
      "eyeRight": "open",
      "yaw": -5.3, "pitch": 2.1, "roll": 1.0,
      "liveness": "real",
      "quality": {
        "brightness": {"level": "high", "score": 0.95},
        "clarity": {"level": "medium", "score": 0.65},
        "integrity": {"level": "high", "score": 0.90}
      }
    }]
  }
}

识别结果结构

{"code": 0, "data": {"found": true, "userId": "user_abc123", "similarity": 0.87}}

Top-N 结果结构

{"code": 0, "data": {"count": 3, "topN": 5, "results": [
  {"userId": "user_abc", "similarity": 0.87},
  {"userId": "user_def", "similarity": 0.72}
], "duplicates": [], "duplicateThreshold": 0.6}}

注意事项

  1. 插件包不包含模型文件,需将 .csta 文件放入项目 static/models/(或其他指定目录),初始化时自动加载
  2. 引擎只能初始化一次,释放后可再次初始化
  3. 人脸库初始化选择light(512 维,轻量)、std(1024 维,需下载 face_recognizer.csta 约 98MB)
  4. 活体检测需要 fas_first.cstafas_second.csta 两个模型
  5. 年龄检测为热加载模块,需额外下载 age_predictor.csta(约 32MB)
  6. 并发检测内部有互斥锁,同一时间只允许一次检测
  7. 建议图片最大尺寸 1280px,超尺寸自动缩放

隐私声明

  • 本插件不采集任何用户数据
  • 所有人脸检测与识别在设备端离线完成
  • 无需网络权限,不上传任何图像或特征数据

许可

  • 插件市场购买后获得使用授权
  • 禁止反编译、二次分发

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android: CAMERA, READ_EXTERNAL_STORAGE(读取相册图片);iOS: 相机/相册(由前端 uni.chooseImage 触发);鸿蒙: 位置信息(读取相册图片)

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据。所有人脸检测与识别在设备端离线完成,无需网络权限,不上传任何图像或特征数据。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: