1import { UnavailabilityError } from 'expo-modules-core';
2
3import ExpoFaceDetector from './ExpoFaceDetector';
4
5declare const global: any;
6
7// @docsMissing
8export type Point = { x: number; y: number };
9
10// @needsAudit
11export type FaceFeature = {
12  /**
13   * An object containing face bounds.
14   */
15  bounds: FaceFeatureBounds;
16  /**
17   * Probability that the face is smiling. Returned only if detection classifications property is
18   * set to `FaceDetectorClassifications.all`.
19   */
20  smilingProbability?: number;
21  /**
22   * Position of the left ear in image coordinates. Returned only if detection classifications
23   * property is set to `FaceDetectorLandmarks.all`.
24   */
25  leftEarPosition?: Point;
26  /**
27   * Position of the right ear in image coordinates. Returned only if detection classifications
28   * property is set to `FaceDetectorLandmarks.all`.
29   */
30  rightEarPosition?: Point;
31  /**
32   * Position of the left eye in image coordinates. Returned only if detection classifications
33   * property is set to `FaceDetectorLandmarks.all`.
34   */
35  leftEyePosition?: Point;
36  /**
37   * Probability that the left eye is open. Returned only if detection classifications property is
38   * set to `FaceDetectorClassifications.all`.
39   */
40  leftEyeOpenProbability?: number;
41  /**
42   * Position of the right eye in image coordinates. Returned only if detection classifications
43   * property is set to `FaceDetectorLandmarks.all`.
44   */
45  rightEyePosition?: Point;
46  /**
47   * Probability that the right eye is open. Returned only if detection classifications property is
48   * set to `FaceDetectorClassifications.all`.
49   */
50  rightEyeOpenProbability?: number;
51  /**
52   * Position of the left cheek in image coordinates. Returned only if detection classifications
53   * property is set to `FaceDetectorLandmarks.all`.
54   */
55  leftCheekPosition?: Point;
56  /**
57   * Position of the right cheek in image coordinates. Returned only if detection classifications
58   * property is set to `FaceDetectorLandmarks.all`.
59   */
60  rightCheekPosition?: Point;
61  /**
62   * Position of the left edge of the mouth in image coordinates. Returned only if detection
63   * classifications property is set to `FaceDetectorLandmarks.all`.
64   */
65  leftMouthPosition?: Point;
66  /**
67   * Position of the center of the mouth in image coordinates. Returned only if detection
68   * classifications property is set to `FaceDetectorLandmarks.all`.
69   */
70  mouthPosition?: Point;
71  /**
72   * Position of the right edge of the mouth in image coordinates. Returned only if detection
73   * classifications property is set to `FaceDetectorLandmarks.all`.
74   */
75  rightMouthPosition?: Point;
76  /**
77   * Position of the bottom edge of the mouth in image coordinates. Returned only if detection
78   * classifications property is set to `FaceDetectorLandmarks.all`.
79   */
80  bottomMouthPosition?: Point;
81  /**
82   * Position of the nose base in image coordinates. Returned only if detection classifications
83   * property is set to `FaceDetectorLandmarks.all`.
84   */
85  noseBasePosition?: Point;
86  /**
87   * Yaw angle of the face (heading, turning head left or right).
88   */
89  yawAngle?: number;
90  /**
91   * Roll angle of the face (bank).
92   */
93  rollAngle?: number;
94  /**
95   * A face identifier (used for tracking, if the same face appears on consecutive frames it will
96   * have the same `faceID`).
97   */
98  faceID?: number;
99};
100
101// @needsAudit
102export type FaceFeatureBounds = {
103  /**
104   * Size of the square containing the face in image coordinates,
105   */
106  size: {
107    width: number;
108    height: number;
109  };
110  /**
111   * Position of the top left corner of a square containing the face in image coordinates,
112   */
113  origin: Point;
114};
115
116// @docsMissing
117export enum FaceDetectorMode {
118  fast = 1,
119  accurate = 2,
120}
121
122// @docsMissing
123export enum FaceDetectorLandmarks {
124  none = 1,
125  all = 2,
126}
127
128// @docsMissing
129export enum FaceDetectorClassifications {
130  none = 1,
131  all = 2,
132}
133
134// @needsAudit
135export type Image = {
136  /**
137   * URI of the image.
138   */
139  uri: string;
140  /**
141   * Width of the image in pixels.
142   */
143  width: number;
144  /**
145   * Height of the image in pixels.
146   */
147  height: number;
148  /**
149   * Orientation of the image (value conforms to the EXIF orientation tag standard).
150   */
151  orientation: number;
152};
153
154// @needsAudit
155/**
156 * In order to configure detector's behavior modules pass a settings object which is then
157 * interpreted by this module.
158 */
159export type DetectionOptions = {
160  /**
161   * Whether to detect faces in fast or accurate mode. Use `FaceDetector.FaceDetectorMode.{fast, accurate}`.
162   */
163  mode?: FaceDetectorMode;
164  /**
165   * Whether to detect and return landmarks positions on the face (ears, eyes, mouth, cheeks, nose).
166   * Use `FaceDetector.FaceDetectorLandmarks.{all, none}`.
167   */
168  detectLandmarks?: FaceDetectorLandmarks;
169  /**
170   * Whether to run additional classifications on detected faces (smiling probability, open eye
171   * probabilities). Use `FaceDetector.FaceDetectorClassifications.{all, none}`.
172   */
173  runClassifications?: FaceDetectorClassifications;
174  /**
175   * Minimal interval in milliseconds between two face detection events being submitted to JS.
176   * Use, when you expect lots of faces for long time and are afraid of JS Bridge being overloaded.
177   * @default 0
178   */
179  minDetectionInterval?: number;
180  /**
181   * Flag to enable tracking of faces between frames. If true, each face will be returned with
182   * `faceID` attribute which should be consistent across frames.
183   * @default false
184   */
185  tracking?: boolean;
186};
187
188// @needsAudit
189export type DetectionResult = {
190  /**
191   * Array of faces objects.
192   */
193  faces: FaceFeature[];
194  // @docsMissing
195  image: Image;
196};
197// @needsAudit
198/**
199 * Detect faces on a picture.
200 * @param uri `file://` URI to the image.
201 * @param options A map of detection options.
202 * @return Returns a Promise which fulfils with [`DetectionResult`](#detectionresult) object.
203 */
204export async function detectFacesAsync(
205  uri: string,
206  options: DetectionOptions = {}
207): Promise<DetectionResult> {
208  if (!ExpoFaceDetector || !ExpoFaceDetector.detectFaces) {
209    if (global.expo?.modules?.ExponentConstants?.appOwnership === 'expo') {
210      console.warn(
211        [
212          "ExpoFaceDetector has been removed from Expo Go. To use this functionality, you'll have to create a development build or prebuild using npx expo run:android|ios commands.",
213          'Learn more: https://expo.fyi/face-detector-removed',
214          'Learn more about development builds: https://docs.expo.dev/develop/development-builds/create-a-build/',
215          'Learn more about prebuild: https://docs.expo.dev/workflow/prebuild/',
216        ].join('\n\n')
217      );
218    }
219    throw new UnavailabilityError('expo-face-detector', 'detectFaces');
220  }
221  return await ExpoFaceDetector.detectFaces({ ...options, uri });
222}
223