大家好,我是 V 哥。
今天给大家介绍在 HarmonyOS 原生鸿蒙开发中,实现人脸识别功能,这个功能在常用的 APP 开发中上镜率还是很高的,在传统的 Android 或 iOS 开发中,通常我们要借助第三方库来实现,而在鸿蒙原生开发中,天然的集成了这个功能,使用起来也超级方便,接下来听 V 哥细细说来。
在鸿蒙 NEXT 中实现人脸识别功能,可通过 CoreVision Kit 的 FaceDetector 模块实现。以下是基于 API 12+ 的实现流程及核心代码示例:
一、开发准备
- 配置权限
在module.json5
中添加以下权限:
json
"requestPermissions": [
{ "name": "ohos.permission.READ_MEDIA" }, // 读取图片权限
{ "name": "ohos.permission.CAMERA" }, // 相机权限
{ "name": "ohos.permission.ACCESS_BIOMETRIC" } // 生物认证权限
]
- 导入依赖
使用鸿蒙 NEXT 的视觉服务接口:
typescript
import { faceDetector } from '@kit.CoreVisionKit';
import image from '@kit.ImageKit'; // 图像处理模块
二、核心实现步骤
1. 初始化人脸检测器
typescript
// 初始化参数配置
let faceDetectConfig: faceDetector.FaceDetectOptions = {
maxFaceNum: 5, // 最大检测人脸数
featureLevel: faceDetector.FeatureLevel.TYPE_FULL, // 检测全部特征
algorithmMode: faceDetector.AlgorithmMode.TYPE_MODE_ACCURATE // 高精度模式
};
// 创建检测器实例
let detector = faceDetector.createFaceDetector(faceDetectConfig);
2. 获取图像数据
通过相机或相册获取图像,转换为 PixelMap
格式:
typescript
// 示例:从相册选择图片并转换为 PixelMap
async function getImagePixelMap() {
let imageSource = image.createImageSource(selectedImageUri);
let decodeOptions = {
desiredSize: { width: 1024, height: 1024 } // 调整尺寸优化性能
};
let pixelMap = await imageSource.createPixelMap(decodeOptions);
return pixelMap;
}
3. 执行人脸检测
typescript
async function detectFaces(pixelMap: image.PixelMap) {
try {
// 执行检测
let faces: faceDetector.Face[] = await detector.detect(pixelMap);
// 处理检测结果
faces.forEach((face: faceDetector.Face) => {
console.log("人脸置信度: " + face.confidence);
console.log("人脸坐标框: " + JSON.stringify(face.rect));
console.log("五官坐标点: " + JSON.stringify(face.landmarks));
});
} catch (error) {
console.error("人脸检测失败: " + error.code + ", 信息: " + error.message);
}
}
4. 身份验证集成
结合生物认证接口验证机主身份:
typescript
import userAuth from '@kit.BiometricAuthenticationKit';
// 检查是否支持人脸识别
let status = userAuth.getAvailableStatus(
userAuth.UserAuthType.FACE,
userAuth.AuthTrustLevel.ATL3
);
if (status.isAvailable) {
// 执行人脸认证
userAuth.executeAuth(
userAuth.UserAuthType.FACE,
userAuth.AuthTrustLevel.ATL3,
(err, result) => {
if (result?.result === userAuth.AuthResult.SUCCESS) {
console.log("身份验证通过");
}
}
);
}
三、以下是一个完整示例代码
- Index.ets 主页面(示例代码是使用元服务项目实现)
typescript
import { authentication } from '@kit.AccountKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN = 0x0000;
export const pathStack: NavPathStack = new NavPathStack();
@Entry
@ComponentV2
struct Index {
build() {
Navigation(pathStack) {
Column({ space: 20 }) {
Button("人脸活体检测-示例")
.width("80%")
.borderRadius(10)
.onClick(() => {
pathStack.pushPath({ name: "live" })
})
}
.height('100%')
.width('100%')
}
.title("元服务")
}
aboutToAppear() {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
this.loginWithHuaweiID();
}
/**
* Sample code for using HUAWEI ID to log in to atomic service.
* According to the Atomic Service Review Guide, when a atomic service has an account system,
* the option to log in with a HUAWEI ID must be provided.
* The following presets the atomic service to use the HUAWEI ID silent login function.
* To enable the atomic service to log in successfully using the HUAWEI ID, please refer
* to the HarmonyOS HUAWEI ID Access Guide to configure the client ID and fingerprint certificate.
*/
private loginWithHuaweiID() {
// Create a login request and set parameters
const loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();
// Whether to forcibly launch the HUAWEI ID login page when the user is not logged in with the HUAWEI ID
loginRequest.forceLogin = false;
// Execute login request
const controller = new authentication.AuthenticationController();
controller.executeRequest(loginRequest).then((data) => {
const loginWithHuaweiIDResponse = data as authentication.LoginWithHuaweiIDResponse;
const authCode = loginWithHuaweiIDResponse.data?.authorizationCode;
// Send authCode to the backend in exchange for unionID, session
}).catch((error: BusinessError) => {
hilog.error(DOMAIN, 'testTag', 'error: %{public}s', JSON.stringify(error));
if (error.code === authentication.AuthenticationErrorCode.ACCOUNT_NOT_LOGGED_IN) {
// HUAWEI ID is not logged in, it is recommended to jump to the login guide page
}
});
}
}
- LiveDetectPage.ets 页面
typescript
import { common, abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { interactiveLiveness } from '@kit.VisionKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
@Builder export function buildPage(){
LiveDetectPage()
}
@Component
export struct LiveDetectPage {
private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
// 权限
private array: Array<Permissions> = ["ohos.permission.CAMERA"];
// 动作个数
@State actionsNum: number = 0;
/**
* 是否是静默模式
* 静默模式(SILENT_MODE):表示静默活体检测模式,暂未支持。
* 交互模式(INTERACTIVE_MODE):表示动作活体检测模式。
*/
@State isSilentMode: string = "INTERACTIVE_MODE";
// 验证完的跳转模式
@State routeMode: string = "replace";
// 验证结果
@State resultInfo: interactiveLiveness.InteractiveLivenessResult = {
livenessType: 0
};
// 验证失败结果
@State failResult: Record<string, number | string> = {
"code": 1008302000,
"message": ""
};
build() {
NavDestination() {
// 层叠布局
Stack({
// 内容对齐方式:顶部对齐
alignContent: Alignment.Top
}) {
// 列容器组件
Column() {
// 行容器组件
Row() {
// 弹性布局:主轴方向为横向,内容对齐方式为起始对齐,垂直方向对齐方式为居中对齐
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
// 文本显示组件
Text("验证完的跳转模式:").fontSize(18).width("25%")
// 弹性布局:主轴方向为横向,内容对齐方式为起始对齐,垂直方向居中对齐
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
// 行容器组件
Row() {
// 单选框组件
Radio({ value: "replace", group: "routeMode" }).checked(true).height(24).width(24)
.onChange((isChecked: boolean) => {
this.routeMode = "replace"
})
Text("replace").fontSize(16)
}.margin({ right: 15 })
Row() {
// 单选框组件
Radio({ value: "back", group: "routeMode" }).checked(false).height(24).width(24)
.onChange((isChecked: boolean) => {
this.routeMode = "back";
})
Text("back").fontSize(16)
}
}.width("75%")
}
}.margin({ bottom: 30 })
// 行容器组件
Row() {
// 弹性布局:主轴方向为横向,内容对齐方式为起始对齐,垂直方向对齐方式为居中对齐
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Text("动作数量:").fontSize(18).width("25%")
TextInput({
placeholder: this.actionsNum != 0 ? this.actionsNum.toString() : "动作数量为3或4个"
}).type(InputType.Number).placeholderFont({
size: 18,
weight: FontWeight.Normal,
family: "HarmonyHeiTi",
style: FontStyle.Normal
}).fontSize(18).fontWeight(FontWeight.Bold).fontFamily("HarmonyHeiTi")
.fontStyle(FontStyle.Normal).width("65%").onChange((value: string) => {
this.actionsNum = Number(value) as interactiveLiveness.ActionsNumber;
})
}
}
}.margin({ left: 24, top: 80 }).zIndex(1)
// 层叠布局组件:内容对齐方式为底部对齐
Stack({ alignContent: Alignment.Bottom }) {
if (this.resultInfo?.mPixelMap) {
// 如果存在mPixelMap,则显示检测的图片
Image(this.resultInfo?.mPixelMap).width(260)
.height(260).align(Alignment.Center)
.margin({ bottom: 260 })
// 圆形遮罩
Circle().width(300).height(300).fillOpacity(0)
.strokeWidth(60).stroke(Color.White)
.margin({ bottom: 250, left: 0 })
}
// 判断检测成功还是失败
Text(this.resultInfo.mPixelMap ? "检测成功" : this.failResult.code != 1008302000 ? "检测失败" : "")
.width("100%").height(26).fontSize(20).fontColor("#000000").fontFamily("HarmonyHeiTi")
.margin({ top: 50 }).textAlign(TextAlign.Center).fontWeight("Medium").margin({ bottom: 240 })
// 如果检测失败,则显示检测失败的原因
if(this.failResult.code != 1008302000) {
Text(this.failResult.message as string)
.width("100%").height(26)
.textAlign(TextAlign.Center).fontFamily("HarmonyHeiTi").fontWeight("Medium")
.margin({ bottom: 200 })
}
// 开始检测的按钮
Button("开始检测").type(ButtonType.Normal).borderRadius(10)
.width('60%').height(40).fontSize(16)
.margin({ bottom: 56 })
.onClick(() => {
this.privateStartDetection();
})
}.height("100%")
}
}.title('活体检测')
.onWillShow(() => {
// 释放结果
this.resultRelease();
// 获取检测的结果
this.getDetectionResultInfo();
})
}
// 跳转到人脸活体检测控件
private privateRouterLibrary() {
// 交互式活体检测配置实例
let routerOptions: interactiveLiveness.InteractiveLivenessConfig = {
// 是否是静默模式
isSilentMode: this.isSilentMode as interactiveLiveness.DetectionMode,
// 路由模式:返回还是替换
routeMode: this.routeMode as interactiveLiveness.RouteRedirectionMode,
// 动作个数
actionsNum: this.actionsNum
}
// 如果可以使用该能力(活体检测能力)
// syscap配置
if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
// 开始活体检测
interactiveLiveness.startLivenessDetection(routerOptions).then((DetectState: boolean) => {
// 如果检测成功,则跳转到下一个页面
hilog.info(0x0001, "LivenessCollectionIndex", `Succeeded in jumping.`);
}).catch((err: BusinessError) => {
// 如果检测失败,则显示检测失败的原因
hilog.error(0x0001, "LivenessCollectionIndex", `Failed to jump. Code:${err.code},message:${err.message}`);
})
} else {
// 如果不可以使用该能力(活体检测能力),则显示不支持该能力的提示
hilog.error(0x0001, "LivenessCollectionIndex", '当前设备不支持活体检测API');
}
}
/**
* 返回从用户获取到的权限列表,遍历该列表,如果包含了ohos.permission.CAMERA权限,则调用privateRouterLibrary方法
*/
private privateStartDetection() {
abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.array).then((res) => {
for (let i = 0; i < res.permissions.length; i++) {
if (res.permissions[i] === "ohos.permission.CAMERA" && res.authResults[i] === 0) {
this.privateRouterLibrary();
}
}
}).catch((err: BusinessError) => {
hilog.error(0x0001, "LivenessCollectionIndex", `Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
})
}
/**
* 获取检测的结果
*/
private getDetectionResultInfo() {
// getInteractiveLivenessResult接口调用完会释放资源
// 如果可以使用活体检测能力(syscap配置)
if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
// 获取活体检测结果
let resultInfo = interactiveLiveness.getInteractiveLivenessResult();
resultInfo.then(data => {
// 获取到结果,则赋值
this.resultInfo = data;
}).catch((err: BusinessError) => {
// 如果发生了异常,则设置失败结果的字段值
this.failResult = { "code": err.code, "message": err.message }
})
} else {
// 当前设备不支持活体检测能力
hilog.error(0x0001, "LivenessCollectionIndex", '该设备不支持活体检测API。');
}
}
/**
* 结果重置
*/
private resultRelease() {
this.resultInfo = {
livenessType: 0
}
this.failResult = {
"code": 1008302000,
"message": ""
}
}
}
注意,需要在module.json5中配置权限。
四、注意事项
- 真机调试
需使用支持人脸识别的真机(如 P50 系列)测试,模拟器不支持)。 - 性能优化
- 降低图像分辨率以提升检测速度。
- 使用
AlgorithmMode.TYPE_MODE_FAST
快速模式平衡性能与精度。
- 特征比对
进阶场景可通过FaceComparator
比对两张人脸相似度。
五、扩展场景
- 活体检测 :通过
VisionKit
监测眨眼、转头动作。 - 暗光增强 :结合
ImageKit
调整图像亮度/对比度强化识别效果。
通过上述步骤,可快速实现基础人脸识别功能,并根据需求扩展至复杂场景。
六、小结一下
以上就是在鸿蒙 NEXT 开发中实现人脸识别功能,也称为活体检测,通过鸿蒙 NEXT 提供的能力,可以轻松实现,兄弟们,可以抓紧试试(请自备真机测试)。学习鸿蒙可以关注威哥写的《鸿蒙 HarmonyOS NEXT 开发之路》卷1,卷2已上市,卷3正在加紧印刷中。如果你还没有拿下鸿蒙认证,快到碗里来(https://developer.huawei.com/consumer/cn/training/classDetail/042cb1cc4d7d44ecbdbd902fd1275dcc)。
