Files
2026-09-24 16:25:22 +08:00

226 lines
7.6 KiB
Plaintext

import { AVCaptureSession, AVCaptureDevice, AVMediaType, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureDeviceInput, AVCaptureInput, AVCaptureVideoDataOutput, AVCaptureConnection, AVCaptureVideoPreviewLayer, AVLayerVideoGravity, AVCaptureVideoOrientation } from 'AVFoundation';
import { PHPhotoLibrary, PHAuthorizationStatus } from 'Photos';
import { CMSampleBuffer, CMSampleBufferGetFormatDescription, CMFormatDescription, CMVideoFormatDescriptionGetDimensions } from 'CoreMedia';
import { DispatchQueue } from "Dispatch"
import { UIView, UIImage, UIApplication, UIImageView, UILabel, UIViewController, UIFontDescriptor, UIFont, NSTextAlignment, UITapGestureRecognizer, UIImagePickerController, UIImagePickerControllerDelegate, UINavigationControllerDelegate } from 'UIKit';
import { CGRect, CGFloat, CGPoint } from 'CoreFoundation';
import { Face, FaceContour, FaceDetector, FaceDetectorOptions,FaceContourType,FaceLandmarkType, FaceLandmark,FaceDetectorPerformanceMode,FaceDetectorClassificationMode,FaceDetectorLandmarkMode } from "MLKitFaceDetection"
import { VisionImage } from "MLKit"
import { NotificationCenter, NSNotification, Notification } from 'Foundation';
import { Selector } from 'ObjectiveC';
import { Alignment } from 'ARKit';
import { CABasicAnimation } from 'QuartzCore';
import { Int,Float } from 'Swift';
import { XFACE_CHECK_OPTS, XFACE_EVENT_SUCCESS, XFACE_TEST_EYE_TYPE, XFACE_FACE_Direction, XFACE_EVENT_CHECK_SUCCESS } from "../interface.uts"
import { XFACE_CHECK_OPTS_REAL, getDefaultConfig } from "../libs/config.uts"
const checkFaceResult = (orimg:UIImage, faces : Array<Face>, callBack : (result : XFACE_EVENT_SUCCESS[]) => void) => {
const cfaces = [] as XFACE_EVENT_SUCCESS[]
// @ts-ignore
for (item in faces) {
// @ts-ignore
const face = item as Face;
let rightEye = { openNum: 0, isOpen: false } as XFACE_TEST_EYE_TYPE;
let leftEye = { openNum: 0, isOpen: false } as XFACE_TEST_EYE_TYPE;
// 眼睛
let rightEyeMark = face.landmark(ofType = FaceLandmarkType.rightEye)
let leftEyeMark = face.landmark(ofType = FaceLandmarkType.leftEye)
// 鼻子
let noseMark = face.landmark(ofType = FaceLandmarkType.noseBase)
// 耳朵
let rightEarMark = face.landmark(ofType = FaceLandmarkType.rightEar)
let leftEarMark = face.landmark(ofType = FaceLandmarkType.leftEar)
let rightEyeNum = face.rightEyeOpenProbability
let leftEyeNum = face.leftEyeOpenProbability
let isSmile = face.smilingProbability
let faceId = face.trackingID
if (rightEyeNum != null) {
rightEye.openNum = new Number(rightEyeNum) + 0;
rightEye.isOpen = new Number(rightEyeNum) > 0.7
}
if (leftEyeNum != null) {
leftEye.openNum = new Number(leftEyeNum)+ 0;
leftEye.isOpen = new Number(leftEyeNum) > 0.7
}
// 检测当前左,右,下,上
let faceRightLeft : XFACE_FACE_Direction = "center"
let faceUpDown : XFACE_FACE_Direction = "center"
let eulerX = face.headEulerAngleX; // 获取头部左右旋转角度
let eulerY = face.headEulerAngleY; // 获取头部上下俯仰角度
let eulerZ = face.headEulerAngleZ; // 获取头部上下俯仰角度
// 嘴巴
let leftMouth = face.landmark(ofType = FaceLandmarkType.mouthLeft)
let rightMouth = face.landmark(ofType = FaceLandmarkType.mouthRight)
let bottomMouth = face.landmark(ofType = FaceLandmarkType.mouthBottom)
let isMouthOpened = false
let ANGLE_THRESHOLD = 30; // 容差值
let ANGLE_HEAD_THRESHOLD = 0; // 容差值
let ANGLE_EYE_THRESHOLD = 6; // 眨眼的容差.
let allHeadBody = false;
if (Math.abs(Number(eulerY)) > ANGLE_THRESHOLD) { // 确保头部没有明显上下俯仰
faceRightLeft = eulerY > 0 ? 'left' : 'right';
}
if (Math.abs(Number(eulerX)) >= ANGLE_HEAD_THRESHOLD) { // 确保头部没有明显上下俯仰
faceUpDown = eulerY > 0 ? 'down' : 'up';
}
if (leftMouth != null && rightMouth != null && bottomMouth != null) {
let mouthWidth = rightMouth!.position.x - leftMouth!.position.x
let mouthHeight = bottomMouth!.position.y - ((leftMouth!.position.y + rightMouth!.position.y) / 2)
if (mouthHeight / mouthWidth > 0.4 && faceRightLeft == 'center') {
isMouthOpened = true;
} else {
isMouthOpened = false;
}
}
if(
leftMouth != null &&
rightMouth != null &&
bottomMouth != null &&
noseMark !=null&&
rightEarMark !=null&&
leftEarMark !=null&&
rightEyeMark !=null&&
leftEyeMark !=null
){
allHeadBody = true;
}
cfaces.push({
imgWidth: orimg.size.width,
imgHeight: orimg.size.height,
isSmile: isSmile == null ? false : (isSmile >= 0.8),
rightEye,
leftEye,
faceRightLeft,
faceUpDown,
isMouthOpened,
allHeadBody,
faceId:faceId!=null?(faceId+0):null
})
}
callBack(cfaces)
}
export function checkFaceByImage(config : XFACE_CHECK_OPTS | null = null){
const cfg = getDefaultConfig(config)
let options_s = FaceDetectorOptions()
options_s.performanceMode = FaceDetectorPerformanceMode.accurate
options_s.landmarkMode = FaceDetectorLandmarkMode.all
options_s.classificationMode = FaceDetectorClassificationMode.all
// @ts-ignore
uni.chooseImage({
count: 1,
// @ts-ignore
success(evt : ChooseImageSuccess) {
if (evt.tempFilePaths.length == 0) return;
const imgs = evt.tempFilePaths[0] as string;
const decodedPath = UTSiOS.convert2AbsFullPath(imgs)
let img = new UIImage(contentsOfFile = decodedPath);
let image = new VisionImage(image = img!);
let faceDetector = FaceDetector.faceDetector(options = options_s)
faceDetector.process(image as VisionImage,completion = (faces:Array<Face>|null,error:any|null)=>{
if(error != null||faces==null||(faces?.length??0)==0){
cfg.success({
images:[],
videoPath:"",
isPass:false
})
return;
}
const _faces = faces!;
checkFaceResult(img!, _faces, (result : XFACE_EVENT_SUCCESS[]) => {
DispatchQueue.main.async(execute = () : void => {
cfg.enter(result,JSON.stringify(result)!)
cfg.success({
isPass: true,
images: [decodedPath] as string[],
videoPath: ""
})
})
})
// for(item in faces!){
// const face = item as Face
// let leftEye = face.landmark(ofType = FaceLandmarkType.leftEye)
// if(leftEye!=null){
// let leftEyePosition = leftEye!.position
// console.log(leftEyePosition.x,'----',img!.size.width)
// }
// }
})
},
fail() {
cfg.fail("解析错误")
}
})
}
/**
* 提供外部图片地址检测当前图片是否存在人脸
*/
export function checkFaceByImageFromFilePath(config : XFACE_CHECK_OPTS) {
const cfg = getDefaultConfig(config)
const imgs = cfg.url;
const decodedPath = UTSiOS.convert2AbsFullPath(imgs!)
let img = new UIImage(contentsOfFile = decodedPath);
let image = new VisionImage(image = img!);
let options_s = FaceDetectorOptions()
options_s.performanceMode = FaceDetectorPerformanceMode.accurate
options_s.landmarkMode = FaceDetectorLandmarkMode.all
options_s.classificationMode = FaceDetectorClassificationMode.all
let faceDetector = FaceDetector.faceDetector(options = options_s)
faceDetector.process(image as VisionImage,completion = (faces:Array<Face>|null,error:any|null)=>{
if(error != null||faces==null||(faces?.length??0)==0){
cfg.success({
images:[],
videoPath:"",
isPass:false
})
return;
}
const _faces = faces!;
checkFaceResult(img!, _faces, (result : XFACE_EVENT_SUCCESS[]) => {
DispatchQueue.main.async(execute = () : void => {
cfg.enter(result,JSON.stringify(result)!)
cfg.success({
isPass: true,
images: [decodedPath] as string[],
videoPath: ""
})
})
})
})
}