idmission-web-sdk 2.3.115 → 2.3.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -211,7 +211,7 @@
211
211
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
212
212
  };
213
213
 
214
- var webSdkVersion = '2.3.115';
214
+ var webSdkVersion = '2.3.116';
215
215
 
216
216
  function getPlatform() {
217
217
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -8252,8 +8252,8 @@
8252
8252
 
8253
8253
  var DEFAULT_CDN_URL = 'https://websdk-cdn-dev.idmission.com/assets';
8254
8254
 
8255
- var defaultDocumentDetectorModelPath = "".concat(DEFAULT_CDN_URL, "/models/DocumentDetector/DocumentDetector-20251018_121234.tflite");
8256
- var defaultDocumentDetectorModelHash = 'cTLjb4g6H+a+RHnLaYrvVF8050qv/Ox04rMtxFXBDR6Xv8Pv+X//hsaO3K3jNPog';
8255
+ var defaultDocumentDetectorModelPath = "".concat(DEFAULT_CDN_URL, "/models/DocumentDetector/DocumentDetector-20250815_115859.tflite");
8256
+ var defaultDocumentDetectorModelHash = '64LSaO2ctQsTkGJoZ6Et4v2KH28Fz1ettgkxD+qFg6frxY7ufL9l/lkjw2ljX2cR';
8257
8257
 
8258
8258
  var defaultFocusModelPath = "".concat(DEFAULT_CDN_URL, "/models/Focus/Focus-20241008_102708.tflite");
8259
8259
  var defaultFocusModelHash = 'HTJNLB7QybtPXIIyUI9oNqW40vE5bgSV5V24R1wXLQHknNEVOSyDQDo/QzRgJ8jb';
@@ -8570,8 +8570,8 @@
8570
8570
  singlePage: 'Single page',
8571
8571
  none: 'None'
8572
8572
  };
8573
- var detector = null;
8574
- var detectorSettings = null;
8573
+ var detector$1 = null;
8574
+ var detectorSettings$1 = null;
8575
8575
  function loadDocumentDetector() {
8576
8576
  return __awaiter(this, arguments, void 0, function (modelAssetPath, scoreThreshold) {
8577
8577
  var _a, _b;
@@ -8584,7 +8584,7 @@
8584
8584
  return __generator(this, function (_c) {
8585
8585
  switch (_c.label) {
8586
8586
  case 0:
8587
- if (detector && (detectorSettings === null || detectorSettings === void 0 ? void 0 : detectorSettings.modelAssetPath) === modelAssetPath && (detectorSettings === null || detectorSettings === void 0 ? void 0 : detectorSettings.scoreThreshold) === scoreThreshold) return [2 /*return*/, detector];
8587
+ if (detector$1 && (detectorSettings$1 === null || detectorSettings$1 === void 0 ? void 0 : detectorSettings$1.modelAssetPath) === modelAssetPath && (detectorSettings$1 === null || detectorSettings$1 === void 0 ? void 0 : detectorSettings$1.scoreThreshold) === scoreThreshold) return [2 /*return*/, detector$1];
8588
8588
  closeDocumentDetector();
8589
8589
  return [4 /*yield*/, preloadDocumentDetectorDependencies()];
8590
8590
  case 1:
@@ -8605,20 +8605,20 @@
8605
8605
  runningMode: 'VIDEO'
8606
8606
  }])];
8607
8607
  case 3:
8608
- detector = _c.sent();
8609
- detectorSettings = {
8608
+ detector$1 = _c.sent();
8609
+ detectorSettings$1 = {
8610
8610
  modelAssetPath: modelAssetPath,
8611
8611
  scoreThreshold: scoreThreshold
8612
8612
  };
8613
- return [2 /*return*/, detector];
8613
+ return [2 /*return*/, detector$1];
8614
8614
  }
8615
8615
  });
8616
8616
  });
8617
8617
  }
8618
8618
  function closeDocumentDetector() {
8619
- detector === null || detector === void 0 ? void 0 : detector.close();
8620
- detector = null;
8621
- detectorSettings = null;
8619
+ detector$1 === null || detector$1 === void 0 ? void 0 : detector$1.close();
8620
+ detector$1 = null;
8621
+ detectorSettings$1 = null;
8622
8622
  }
8623
8623
  function useLoadDocumentDetector(_a) {
8624
8624
  var _b = _a.shouldLoadModels,
@@ -8719,12 +8719,12 @@
8719
8719
  return __awaiter(this, void 0, void 0, function () {
8720
8720
  var startedAt, prediction, time, frameWidth, frameHeight;
8721
8721
  return __generator(this, function (_a) {
8722
- if (!detector) return [2 /*return*/, null];
8722
+ if (!detector$1) return [2 /*return*/, null];
8723
8723
  startedAt = performance.now();
8724
8724
  // Detectors can throw errors, for example when using custom URLs that
8725
8725
  // contain a model that doesn't provide the expected output.
8726
8726
  try {
8727
- prediction = detector.detectForVideo(frame, performance.now());
8727
+ prediction = detector$1.detectForVideo(frame, performance.now());
8728
8728
  time = performance.now() - startedAt;
8729
8729
  frameWidth = frame.width;
8730
8730
  frameHeight = frame.height;
@@ -15922,7 +15922,6 @@
15922
15922
  scaledHeight = _c.scaledHeight,
15923
15923
  xOffset = _c.xOffset,
15924
15924
  yOffset = _c.yOffset;
15925
- if (!point) return /*#__PURE__*/React.createElement(React.Fragment, null);
15926
15925
  var left, top;
15927
15926
  if (horizontal) {
15928
15927
  left = point.x / videoWidth * scaledWidth - xOffset;
@@ -21012,6 +21011,136 @@
21012
21011
  var templateObject_1$n, templateObject_2$l;
21013
21012
 
21014
21013
  var defaultSelfieCaptureModelLoadTimeoutMs = 45000;
21014
+ var detector = null;
21015
+ var detectorSettings = null;
21016
+ function loadFaceDetector() {
21017
+ return __awaiter(this, arguments, void 0, function (modelAssetPath) {
21018
+ var _a, _b;
21019
+ if (modelAssetPath === void 0) {
21020
+ modelAssetPath = defaultFaceDetectorModelPath;
21021
+ }
21022
+ return __generator(this, function (_c) {
21023
+ switch (_c.label) {
21024
+ case 0:
21025
+ if (detector && (detectorSettings === null || detectorSettings === void 0 ? void 0 : detectorSettings.modelAssetPath) === modelAssetPath) return [2 /*return*/, detector];
21026
+ closeFaceDetector();
21027
+ return [4 /*yield*/, preloadFaceDetectorDependencies()];
21028
+ case 1:
21029
+ _c.sent();
21030
+ if (modelCapabilities.delegate === 'NONE') {
21031
+ throw new Error('No available delegate for face detector.');
21032
+ }
21033
+ _b = (_a = ya).createFromOptions;
21034
+ return [4 /*yield*/, ao.forVisionTasks(visionTasksBasePath)];
21035
+ case 2:
21036
+ return [4 /*yield*/, _b.apply(_a, [_c.sent(), {
21037
+ // canvas: document.createElement('canvas'),
21038
+ baseOptions: {
21039
+ modelAssetPath: modelAssetPath,
21040
+ delegate: modelCapabilities.delegate
21041
+ },
21042
+ runningMode: 'VIDEO'
21043
+ }])];
21044
+ case 3:
21045
+ detector = _c.sent();
21046
+ detectorSettings = {
21047
+ modelAssetPath: modelAssetPath
21048
+ };
21049
+ return [2 /*return*/, detector];
21050
+ }
21051
+ });
21052
+ });
21053
+ }
21054
+ function closeFaceDetector() {
21055
+ detector === null || detector === void 0 ? void 0 : detector.close();
21056
+ detector = null;
21057
+ detectorSettings = null;
21058
+ }
21059
+ function useLoadFaceDetector(_a) {
21060
+ var onModelError = _a.onModelError,
21061
+ _b = _a.modelLoadTimeoutMs,
21062
+ modelLoadTimeoutMs = _b === void 0 ? defaultSelfieCaptureModelLoadTimeoutMs : _b,
21063
+ videoRef = _a.videoRef;
21064
+ var _c = React.useState('not-started'),
21065
+ modelLoadState = _c[0],
21066
+ setModelLoadState = _c[1];
21067
+ var _d = React.useState(0),
21068
+ modelDownloadProgress = _d[0],
21069
+ setModelDownloadProgress = _d[1];
21070
+ var _e = React.useState(null),
21071
+ modelWarmingStartedAt = _e[0],
21072
+ setModelWarmingStartedAt = _e[1];
21073
+ var _f = React.useState(null),
21074
+ modelError = _f[0],
21075
+ setModelError = _f[1];
21076
+ React.useEffect(function loadModel() {
21077
+ var _this = this;
21078
+ setModelLoadState('downloading');
21079
+ setModelWarmingStartedAt(null);
21080
+ var modelLoadTimeout = setTimeout(function () {
21081
+ setModelError(new Error('Model loading time limit exceeded.'));
21082
+ }, modelLoadTimeoutMs);
21083
+ function handleDownloadProgress(event) {
21084
+ setModelDownloadProgress(progressToPercentage(event.detail));
21085
+ }
21086
+ document.addEventListener('idmission.preloadProgress.faceDetection', handleDownloadProgress);
21087
+ var cancelVideoReady = function cancelVideoReady() {};
21088
+ loadFaceDetector().then(function (model) {
21089
+ return __awaiter(_this, void 0, void 0, function () {
21090
+ var _a, videoReady, cancel, cancelled;
21091
+ return __generator(this, function (_b) {
21092
+ switch (_b.label) {
21093
+ case 0:
21094
+ setModelDownloadProgress(100);
21095
+ clearTimeout(modelLoadTimeout);
21096
+ setModelLoadState('warming');
21097
+ setModelWarmingStartedAt(performance.now());
21098
+ return [4 /*yield*/, testFaceDetectionAgainstKnownImage(model)];
21099
+ case 1:
21100
+ _b.sent();
21101
+ _a = waitForVideoReady(videoRef), videoReady = _a[0], cancel = _a[1];
21102
+ cancelled = false;
21103
+ cancelVideoReady = function cancelVideoReady() {
21104
+ cancelled = true;
21105
+ cancel();
21106
+ };
21107
+ return [4 /*yield*/, videoReady];
21108
+ case 2:
21109
+ _b.sent();
21110
+ if (cancelled) return [2 /*return*/];
21111
+ model.detectForVideo(videoRef.current, performance.now());
21112
+ setModelLoadState('ready');
21113
+ return [2 /*return*/];
21114
+ }
21115
+ });
21116
+ });
21117
+ })["catch"](function (e) {
21118
+ setModelError(e);
21119
+ setModelLoadState('error');
21120
+ })["finally"](function () {
21121
+ clearTimeout(modelLoadTimeout);
21122
+ });
21123
+ return function () {
21124
+ log('unloading face detection model');
21125
+ cancelVideoReady();
21126
+ closeFaceDetector();
21127
+ clearTimeout(modelLoadTimeout);
21128
+ document.removeEventListener('idmission.preloadProgress.faceDetection', handleDownloadProgress);
21129
+ };
21130
+ }, [modelLoadTimeoutMs, videoRef]);
21131
+ React.useEffect(function handleModelError() {
21132
+ if (modelError) onModelError === null || onModelError === void 0 ? void 0 : onModelError(modelError);
21133
+ }, [modelError, onModelError]);
21134
+ return React.useMemo(function () {
21135
+ return {
21136
+ ready: modelLoadState === 'ready',
21137
+ modelLoadState: modelLoadState,
21138
+ modelDownloadProgress: modelDownloadProgress,
21139
+ modelWarmingStartedAt: modelWarmingStartedAt,
21140
+ modelError: modelError
21141
+ };
21142
+ }, [modelLoadState, modelDownloadProgress, modelWarmingStartedAt, modelError]);
21143
+ }
21015
21144
  var lastFaceDetectionAt = 0;
21016
21145
  var lastFaceDetectionTime = 0;
21017
21146
  function setLastFaceDetectionAt(time) {
@@ -21061,68 +21190,24 @@
21061
21190
  if (lastNNosePairs.length > framesNeeded - 1) lastNNosePairs.length = framesNeeded - 1;
21062
21191
  }
21063
21192
  }
21064
- function isFaceDetection(detection) {
21065
- return detection.boundingBox && detection.categories.some(function (c) {
21066
- return c.categoryName === 'Primary face' || c.categoryName === 'Secondary face';
21067
- });
21068
- }
21069
- function isNoseDetection(detection) {
21070
- return detection.categories.some(function (c) {
21071
- return c.categoryName === 'Nose';
21193
+ function makeFaceDetectorPrediction(imageData) {
21194
+ if (!detector) return null;
21195
+ var prediction = detector.detectForVideo(imageData, performance.now());
21196
+ var faces = prediction.detections.map(function (d) {
21197
+ return {
21198
+ box: convertBoundingBox(d.boundingBox),
21199
+ keypoints: d.keypoints.map(function (k) {
21200
+ var _a;
21201
+ return _assign(_assign({}, k), {
21202
+ x: k.x * imageData.width,
21203
+ y: k.y * imageData.height,
21204
+ name: (_a = k.label) !== null && _a !== void 0 ? _a : ''
21205
+ });
21206
+ })
21207
+ };
21072
21208
  });
21073
- }
21074
- function makeFacePredictionWithDocumentDetector(frame) {
21075
- return __awaiter(this, void 0, void 0, function () {
21076
- var prediction, faceDetections, noseDetections, _i, _a, detection, faces, _b, faceDetections_1, d, faceBox, nose, _c, noseDetections_1, n, noseBox, cX, cY;
21077
- return __generator(this, function (_d) {
21078
- switch (_d.label) {
21079
- case 0:
21080
- return [4 /*yield*/, makeDocumentDetectorPrediction(frame)];
21081
- case 1:
21082
- prediction = _d.sent();
21083
- if (!prediction) return [2 /*return*/, null];
21084
- faceDetections = [];
21085
- noseDetections = [];
21086
- for (_i = 0, _a = prediction.detections; _i < _a.length; _i++) {
21087
- detection = _a[_i];
21088
- if (isNoseDetection(detection)) {
21089
- noseDetections.push(detection);
21090
- } else if (isFaceDetection(detection)) {
21091
- faceDetections.push(detection);
21092
- }
21093
- }
21094
- faces = [];
21095
- for (_b = 0, faceDetections_1 = faceDetections; _b < faceDetections_1.length; _b++) {
21096
- d = faceDetections_1[_b];
21097
- faceBox = d.boundingBox;
21098
- if (!faceBox) continue;
21099
- nose = null;
21100
- for (_c = 0, noseDetections_1 = noseDetections; _c < noseDetections_1.length; _c++) {
21101
- n = noseDetections_1[_c];
21102
- noseBox = n.boundingBox;
21103
- if (!noseBox) continue;
21104
- cX = noseBox.originX + noseBox.width / 2;
21105
- cY = noseBox.originY + noseBox.height / 2;
21106
- if (cX >= faceBox.originX && cX <= faceBox.originX + faceBox.width && cY >= faceBox.originY && cY <= faceBox.originY + faceBox.height) {
21107
- nose = {
21108
- x: cX,
21109
- y: cY,
21110
- name: 'nose'
21111
- };
21112
- break;
21113
- }
21114
- }
21115
- faces.push({
21116
- box: convertBoundingBox(d.boundingBox),
21117
- keypoints: [null, null, nose, null, null]
21118
- });
21119
- }
21120
- debug('faces', faces);
21121
- return [2 /*return*/, _assign(_assign({}, prediction), {
21122
- faces: faces
21123
- })];
21124
- }
21125
- });
21209
+ return _assign(_assign({}, prediction), {
21210
+ faces: faces
21126
21211
  });
21127
21212
  }
21128
21213
  function processFaceDetectorPrediction(_a) {
@@ -21150,9 +21235,13 @@
21150
21235
  // this represents the edge that the centroid of the face should not cross -- 12.5% of video width
21151
21236
  yCentroidBoundary = _h === void 0 ? 0.125 : _h,
21152
21237
  // this represents the edge that the centroid of the face should not cross -- 12.5% of video height
21153
- _j = _a.noseTrackingThreshold,
21238
+ _j = _a.foreheadRatio,
21154
21239
  // this represents the edge that the centroid of the face should not cross -- 12.5% of video height
21155
- noseTrackingThreshold = _j === void 0 ? 0.2 : _j,
21240
+ foreheadRatio = _j === void 0 ? 0.275 : _j,
21241
+ // we found that the bounding box ends at the brow and misses the forehead. this ratio represents how much we should extend the box to include the forehead.
21242
+ _k = _a.noseTrackingThreshold,
21243
+ // we found that the bounding box ends at the brow and misses the forehead. this ratio represents how much we should extend the box to include the forehead.
21244
+ noseTrackingThreshold = _k === void 0 ? 0.2 : _k,
21156
21245
  // this represents the maximum distance that the nose can be from the center of the face box -- 20% of the face box width or height
21157
21246
  minCaptureBrightnessThreshold = _a.minCaptureBrightnessThreshold,
21158
21247
  minCaptureRangeThreshold = _a.minCaptureRangeThreshold,
@@ -21179,10 +21268,10 @@
21179
21268
  var frameCX = videoWidth / 2;
21180
21269
  var frameCY = videoHeight / 2;
21181
21270
  // calculate head bounding box, with forehead extension
21182
- // const foreheadSize = face.box.height * foreheadRatio
21271
+ var foreheadSize = face.box.height * foreheadRatio;
21183
21272
  var headXMin = face.box.xMin;
21184
21273
  var headXMax = face.box.xMax;
21185
- var headYMin = face.box.yMin; // - foreheadSize
21274
+ var headYMin = face.box.yMin - foreheadSize;
21186
21275
  var headYMax = face.box.yMax;
21187
21276
  // calculate head centroids
21188
21277
  var headCX = (headXMin + headXMax) / 2;
@@ -21238,6 +21327,26 @@
21238
21327
  faceVisibilityTooLow: faceVisibilityTooLow
21239
21328
  };
21240
21329
  }
21330
+ function testFaceDetectionAgainstKnownImage(detector) {
21331
+ return new Promise(function (resolve, reject) {
21332
+ var img = new Image();
21333
+ img.crossOrigin = 'anonymous';
21334
+ img.onload = function () {
21335
+ var prediction = detector.detectForVideo(img, performance.now());
21336
+ if (prediction.detections.length > 0) {
21337
+ debug('face detection test result', prediction.detections);
21338
+ resolve(void 0);
21339
+ } else {
21340
+ warn('face detection test failed');
21341
+ reject(new Error('testFaceDetectionAgainstKnownImage failed to predict'));
21342
+ }
21343
+ };
21344
+ img.onerror = function () {
21345
+ return reject(new Error('testFaceDetectionAgainstKnownImage failed to load image'));
21346
+ };
21347
+ img.src = "".concat(DEFAULT_CDN_URL, "/head-test.jpg");
21348
+ });
21349
+ }
21241
21350
 
21242
21351
  function detectBrightnessAndContrast(frame, brightnessAverager) {
21243
21352
  var ctx = frame.getContext('2d');
@@ -21389,27 +21498,16 @@
21389
21498
  var canvasRef = React.useRef(null);
21390
21499
  var onPredictionHandler = React.useRef();
21391
21500
  var addToAverage = useRunningAvg(5).addToAverage;
21392
- var _f = useLoadDocumentDetector({
21393
- videoRef: videoRef,
21501
+ var _f = useLoadFaceDetector({
21394
21502
  onModelError: onModelError,
21395
- modelLoadTimeoutMs: modelLoadTimeoutMs
21503
+ modelLoadTimeoutMs: modelLoadTimeoutMs,
21504
+ videoRef: videoRef
21396
21505
  }),
21397
21506
  ready = _f.ready,
21398
21507
  modelLoadState = _f.modelLoadState,
21399
21508
  modelDownloadProgress = _f.modelDownloadProgress,
21400
21509
  modelWarmingStartedAt = _f.modelWarmingStartedAt,
21401
21510
  modelError = _f.modelError;
21402
- // const {
21403
- // ready,
21404
- // modelLoadState,
21405
- // modelDownloadProgress,
21406
- // modelWarmingStartedAt,
21407
- // modelError,
21408
- // } = useLoadFaceDetector({
21409
- // onModelError,
21410
- // modelLoadTimeoutMs,
21411
- // videoRef,
21412
- // })
21413
21511
  var _g = useFrameLoop(React.useCallback(function () {
21414
21512
  return __awaiter(_this, void 0, void 0, function () {
21415
21513
  var vw, vh, ctx, thresholdsProvided, brightnessResults, brightness, range, variance, prediction, processed, e_1;
@@ -21423,21 +21521,19 @@
21423
21521
  ctx = canvasRef.current.getContext('2d');
21424
21522
  canvasRef.current.width = vw;
21425
21523
  canvasRef.current.height = vh;
21426
- if (!(ctx && videoRef.current.readyState === 4)) return [3 /*break*/, 5];
21524
+ if (!(ctx && videoRef.current.readyState === 4)) return [3 /*break*/, 4];
21427
21525
  ctx.translate(vw, 0);
21428
21526
  ctx.scale(-1, 1);
21429
21527
  ctx.drawImage(videoRef.current, 0, 0, vw, vh);
21430
21528
  _c.label = 1;
21431
21529
  case 1:
21432
- _c.trys.push([1, 4,, 5]);
21530
+ _c.trys.push([1, 3,, 4]);
21433
21531
  thresholdsProvided = minCaptureBrightnessThreshold !== undefined || minCaptureRangeThreshold !== undefined || minCaptureVarianceThreshold !== undefined;
21434
21532
  brightnessResults = thresholdsProvided ? detectBrightnessAndContrast(canvasRef.current, addToAverage) : undefined;
21435
21533
  brightness = brightnessResults === null || brightnessResults === void 0 ? void 0 : brightnessResults.brightness;
21436
21534
  range = brightnessResults === null || brightnessResults === void 0 ? void 0 : brightnessResults.range;
21437
21535
  variance = brightnessResults === null || brightnessResults === void 0 ? void 0 : brightnessResults.variance;
21438
- return [4 /*yield*/, makeFacePredictionWithDocumentDetector(canvasRef.current)];
21439
- case 2:
21440
- prediction = _c.sent();
21536
+ prediction = makeFaceDetectorPrediction(canvasRef.current);
21441
21537
  processed = processFaceDetectorPrediction({
21442
21538
  faces: (_a = prediction === null || prediction === void 0 ? void 0 : prediction.faces) !== null && _a !== void 0 ? _a : [],
21443
21539
  videoWidth: vw,
@@ -21453,15 +21549,15 @@
21453
21549
  setLastFaceDetectionAt(new Date().getTime());
21454
21550
  // setLastPrediction(processed)
21455
21551
  return [4 /*yield*/, (_b = onPredictionHandler.current) === null || _b === void 0 ? void 0 : _b.call(onPredictionHandler, processed)];
21456
- case 3:
21552
+ case 2:
21457
21553
  // setLastPrediction(processed)
21458
21554
  _c.sent();
21459
- return [3 /*break*/, 5];
21460
- case 4:
21555
+ return [3 /*break*/, 4];
21556
+ case 3:
21461
21557
  e_1 = _c.sent();
21462
21558
  error('caught face detection error', e_1);
21463
- return [3 /*break*/, 5];
21464
- case 5:
21559
+ return [3 /*break*/, 4];
21560
+ case 4:
21465
21561
  return [2 /*return*/];
21466
21562
  }
21467
21563
  });
@@ -24766,8 +24862,7 @@
24766
24862
  var rightEdge = videoWidth * (1 - headTrackingBoundaryPercentage);
24767
24863
  var topEdge = videoHeight * headTrackingBoundaryPercentage;
24768
24864
  var bottomEdge = videoHeight * (1 - headTrackingBoundaryPercentage);
24769
- var nose = face === null || face === void 0 ? void 0 : face.keypoints[2];
24770
- var nearBoundary = !!face && !!nose && (headTrackingBoundaryType === 'nose' ? nose.x < leftEdge || nose.x > rightEdge || nose.y < topEdge || nose.y > bottomEdge : (face === null || face === void 0 ? void 0 : face.box.xMin) < leftEdge || (face === null || face === void 0 ? void 0 : face.box.xMax) > rightEdge || (face === null || face === void 0 ? void 0 : face.box.yMin) < topEdge || (face === null || face === void 0 ? void 0 : face.box.yMax) > bottomEdge);
24865
+ var nearBoundary = !!face && (headTrackingBoundaryType === 'nose' ? face.keypoints[2].x < leftEdge || face.keypoints[2].x > rightEdge || face.keypoints[2].y < topEdge || face.keypoints[2].y > bottomEdge : (face === null || face === void 0 ? void 0 : face.box.xMin) < leftEdge || (face === null || face === void 0 ? void 0 : face.box.xMax) > rightEdge || (face === null || face === void 0 ? void 0 : face.box.yMin) < topEdge || (face === null || face === void 0 ? void 0 : face.box.yMax) > bottomEdge);
24771
24866
  setLastFace(face);
24772
24867
  setHeadTrackingSatisfied(!!face && !nearBoundary);
24773
24868
  setNumFramesWithoutFaces(face ? 0 : function (n) {