HarmonyOS鸿蒙Next中采集人脸识别和身份证照片比对是否一个人

HarmonyOS鸿蒙Next中采集人脸识别和身份证照片比对是否一个人 这个如何实现,用哪些api,提供下demo,和实现步骤

7 回复

【解决方案】

开发者您好,可以参考卡证识别获取身份证照片人像,再通过人脸比对进行对比是否为同一个人,示例代码如下:

import { faceComparator } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { CardRecognition, CardRecognitionResult, CardSide, CardType } from '@kit.VisionKit';
import fs from '@ohos.file.fs';

const TAG: string = 'FaceCompareSample';

@Entry
@Component
struct Index {
  @State chooseImage: PixelMap | undefined = undefined;
  @State chooseImage1: PixelMap | undefined = undefined;
  @State dataValues: string = '';
  @State showOCR: boolean = false;

  async aboutToAppear(): Promise<void> {
    const initResult = await faceComparator.init();
    hilog.info(0x0000, TAG, `Face comparator initialization result:${initResult}`);
  }

  async aboutToDisappear(): Promise<void> {
    await faceComparator.release();
    hilog.info(0x0000, TAG, 'Face comparator released successfully');
  }

  build() {
    Column() {
      Image(this.chooseImage)
        .objectFit(ImageFit.Fill)
        .height('30%')
        .accessibilityDescription('默认图片1');
      Image(this.chooseImage1)
        .objectFit(ImageFit.Fill)
        .height('30%')
        .accessibilityDescription('默认图片2');
      Text(this.dataValues)
        .copyOption(CopyOptions.LocalDevice)
        .height('15%')
        .margin(10)
        .width('60%');

      Row() {
        Button('选择图片')
          .type(ButtonType.Capsule)
          .fontColor(Color.White)
          .alignSelf(ItemAlign.Center)
          .width('40%')
          .margin(10)
          .onClick(() => {
            // 拉起图库
            void this.selectImage();
          });

        Button('click me')
          .onClick(() => {
            this.showOCR = true;
          })
          .type(ButtonType.Capsule)
          .fontColor(Color.White)
          .alignSelf(ItemAlign.Center)
          .width('40%')
          .margin(10);

        if (this.showOCR) {
          this.CardOCRPage();
        }
      };

      Button('人脸比对')
        .type(ButtonType.Capsule)
        .fontColor(Color.White)
        .alignSelf(ItemAlign.Center)
        .width('80%')
        .margin(10)
        .onClick(() => {
          if (!this.chooseImage || !this.chooseImage1) {
            hilog.error(0x0000, TAG, 'Failed to choose image');
            return;
          }
          // 调用人脸比对接口
          let visionInfo: faceComparator.VisionInfo = {
            pixelMap: this.chooseImage,
          };
          let visionInfo1: faceComparator.VisionInfo = {
            pixelMap: this.chooseImage1,
          };
          faceComparator.compareFaces(visionInfo, visionInfo1)
            .then((data: faceComparator.FaceCompareResult) => {
              let faceString = 'degree of similarity: ' + this.toPercentage(data.similarity) +
                ((data.isSamePerson) ? '. is' : '. no') + ' same person';
              hilog.info(0x0000, TAG, 'faceString data is ' + faceString);
              this.dataValues = faceString;
            })
            .catch((error: BusinessError) => {
              hilog.error(0x0000, TAG, `Face comparison failed. Code: ${error.code}, message: ${error.message}`);
              this.dataValues = `Error: ${error.message}`;
            });
        });
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }

  @Builder
  CardOCRPage() {
    CardRecognition({
      // 此处为身份证类型
      supportType: CardType.CARD_ID,
      cardSide: CardSide.FRONT,
      cardRecognitionConfig: {
        isPhotoSelectionSupported: true,
        cardContentConfig: { idCard: { isPhotoNeeded: true } }
      },
      onResult: async (params: CardRecognitionResult) => {
        try {
          this.showOCR = false;
          if (params.cardInfo) {
            let photoUri = params.cardInfo.front.photoUri;
            let file = fs.openSync(photoUri, fs.OpenMode.READ_ONLY);
            const imageSource: image.ImageSource = image.createImageSource(file.fd);
            let decodingOptions: image.DecodingOptions = {
              editable: true,
              desiredPixelFormat: 3,
            };
            this.chooseImage1 = await imageSource.createPixelMap(decodingOptions);
            if (file) {
              fs.closeSync(file);
            }
          }
        } catch (error) {
          console.error('Card recognition error:', error);
        }
      }
    })
      .width('100%')
      .height('100%');
  }

  private toPercentage(num: number): string {
    return `${(num * 100).toFixed(2)}%`;
  }

  private async selectImage() {
    let uri = await this.openPhoto();
    if (uri === undefined) {
      hilog.error(0x0000, TAG, 'Failed to get two image uris.');
    }
    this.loadImage(uri);
  }

  private async openPhoto(): Promise<string[]> {
    return new Promise<string[]>((resolve, reject) => {
      let photoPicker: photoAccessHelper.PhotoViewPicker = new photoAccessHelper.PhotoViewPicker();
      photoPicker.select({
        MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
        maxSelectNumber: 1
      }).then(res => {
        resolve(res.photoUris);
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, TAG, `Failed to get photo image uris. code: ${err.code}, message: ${err.message}`);
        reject();
      });
    });
  }

  private loadImage(names: string[]) {
    setTimeout(async () => {
      let imageSource: image.ImageSource | undefined = undefined;
      let fileSource: fileIo.File;
      try {
        fileSource = await fileIo.open(names[0], fileIo.OpenMode.READ_ONLY);
        imageSource = image.createImageSource(fileSource.fd);
        this.chooseImage = await imageSource.createPixelMap();
      } catch (error) {
        hilog.error(0x0000, TAG, `Failed to open file. Error: ${error}`);
      }
    }, 100
    );
  }
}

更多关于HarmonyOS鸿蒙Next中采集人脸识别和身份证照片比对是否一个人的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


没有直接对比两张照片是同一个人的api吧。没见过啊。。。。

一些第三方服务商提供这样的对比功能,而且都是付费的~

人脸活体检测

官方Demo:

import { common, abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { interactiveLiveness } from '@kit.VisionKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
struct LivenessCollectionIndex{
private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
private array: Array<Permissions> = ["ohos.permission.CAMERA"];
@State actionsNum: number = 0;
@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() {
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(() => {
this.routeMode = "replace"
})
Text("replace")
.fontSize(16)
}
.margin({ right: 15 })

Row() {
Radio({ value: "back", group: "routeMode" }).checked(false)
.height(24)
.width(24)
.onChange(() => {
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) {
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)
.fontSize(16)
.fontColor(Color.Gray)
.textAlign(TextAlign.Center)
.fontFamily("HarmonyHeiTi")
.fontWeight("Medium")
.margin({ bottom: 200 })
}

Button("开始检测", { type: ButtonType.Normal, stateEffect: true })
.width(192)
.height(40)
.fontSize(16)
.backgroundColor(0x317aff)
.borderRadius(20)
.margin({
bottom: 56
})
.onClick(() => {
this.startDetection();
})
}
.height("100%")
}
}
}

onPageShow() {
this.resultRelease();
this.getDetectionResultInfo();
}

// 跳转到人脸活体检测控件
private routerLibrary() {
let routerOptions: interactiveLiveness.InteractiveLivenessConfig = {
isSilentMode: this.isSilentMode as interactiveLiveness.DetectionMode,
routeMode: this.routeMode as interactiveLiveness.RouteRedirectionMode,
actionsNum: this.actionsNum
}

if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
interactiveLiveness.startLivenessDetection(routerOptions).then(() => {
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", 'this api is not supported on this device');
}
}

// 校验CAMERA权限
private startDetection() {
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.routerLibrary();
}
}
}).catch((err: BusinessError) => {
hilog.error(0x0001, "LivenessCollectionIndex", `Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
})
}

// 获取验证结果
private getDetectionResultInfo() {
// getInteractiveLivenessResult接口调用完会释放资源
if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
interactiveLiveness.getInteractiveLivenessResult().then(data => {
this.resultInfo = data;
}).catch((err: BusinessError) => {
this.failResult = {
"code": err.code,
"message": err.message
}
})
} else {
hilog.error(0x0001, "LivenessCollectionIndex", 'this api is not supported on this device');
}
}

// result release
private resultRelease() {
this.resultInfo = {
livenessType: 0
}
this.failResult = {
"code": 1008302000,
"message": ""
}
}
}

活体检测只能证明你是个活物,并不能进行身份认证是不是本人,

申请更新6.0.0.328sp22体验

鸿蒙Next中的人脸识别与身份证比对功能,主要通过系统内置的AI能力实现。系统提供人脸检测、特征提取等接口,可获取人脸关键点与特征向量。身份证照片需通过OCR技术提取人像区域,同样转换为特征向量。比对时计算两个特征向量的相似度,通常使用余弦相似度或欧氏距离。系统安全机制确保数据在设备端处理,不涉及云端传输。整个过程依赖鸿蒙的多媒体和AI框架,无需Java或C语言介入。

在HarmonyOS Next中实现人脸与身份证照片的比对,主要通过图像处理和AI能力完成。以下是关键API和实现思路:

核心API:

  1. 图像处理:使用@ohos.multimedia.image获取和转换图片数据
  2. AI能力:通过@ohos.ai.FaceRecognition进行人脸特征提取
  3. 特征比对:计算两个特征向量的相似度

实现步骤:

  1. 准备阶段
import { FaceRecognition } from '@ohos.ai.FaceRecognition';
import { image } from '@ohos.multimedia.image';
  1. 人脸检测与特征提取
// 初始化人脸识别实例
let faceRecognition = new FaceRecognition();

// 从摄像头或图片源获取人脸图像
// 提取人脸特征向量
let faceFeature = await faceRecognition.extractFaceFeature(imagePixelMap);
  1. 身份证照片处理
// 加载身份证照片
// 提取身份证人像特征
let idCardFeature = await faceRecognition.extractFaceFeature(idCardPixelMap);
  1. 特征比对
// 计算特征相似度
let similarity = faceRecognition.compareFaceFeature(faceFeature, idCardFeature);

// 设置阈值判断是否为同一人
const THRESHOLD = 0.8; // 典型阈值
let isSamePerson = similarity >= THRESHOLD;

注意事项:

  • 需要申请ohos.permission.CAMERA权限
  • 身份证照片需确保人脸区域清晰可见
  • 实际部署需考虑光照、角度等影响因素
  • 特征提取前建议进行人脸对齐预处理

完整实现需结合具体业务场景调整参数和处理流程。

回到顶部