face-validator-sdk 1.1.0 → 1.2.0
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.
- package/README.md +120 -117
- package/dist/face-validator-sdk.cjs.js +2 -1
- package/dist/face-validator-sdk.cjs.js.LICENSE.txt +9 -0
- package/dist/face-validator-sdk.cjs.js.map +1 -1
- package/dist/face-validator-sdk.esm.js +2 -1
- package/dist/face-validator-sdk.esm.js.LICENSE.txt +9 -0
- package/dist/face-validator-sdk.esm.js.map +1 -1
- package/dist/face-validator-sdk.umd.js +2 -1
- package/dist/face-validator-sdk.umd.js.LICENSE.txt +9 -0
- package/dist/face-validator-sdk.umd.js.map +1 -1
- package/dist/types/ReactSelfieCapture.d.ts +29 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Real-time selfie validation SDK with face detection, powered by **MediaPipe**. Detects faces, hands, and validates pose, lighting, and occlusions in real-time.
|
|
4
4
|
|
|
5
|
-
🎭 **[Live Demo](https://face-validator-sdk.vercel.app)**
|
|
6
|
-
|
|
7
|
-
[](https://github.com/rwmsousa/face-validator-sdk/actions/workflows/ci.yml)
|
|
8
|
-
[](https://github.com/rwmsousa/face-validator-sdk/actions/workflows/deploy-vercel.yml)
|
|
5
|
+
🎭 **[Live Demo](https://face-validator-sdk.vercel.app)**
|
|
9
6
|
|
|
10
7
|
## ✨ Features
|
|
11
8
|
|
|
@@ -29,15 +26,17 @@ Real-time selfie validation SDK with face detection, powered by **MediaPipe**. D
|
|
|
29
26
|
- 📦 **Multiple builds**: ESM, CJS, UMD
|
|
30
27
|
- 🚀 **GPU accelerated**: Powered by MediaPipe with GPU support
|
|
31
28
|
|
|
32
|
-
## 📦 Installation
|
|
29
|
+
## 📦 Installation (Core SDK)
|
|
30
|
+
|
|
31
|
+
For any web application (React, Angular, Vue, vanilla JS, Java backend with JS frontend, etc.) that wants to use the **core validator**:
|
|
33
32
|
|
|
34
33
|
```bash
|
|
35
|
-
npm install face-validator-sdk
|
|
34
|
+
npm install face-validator-sdk
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
> The SDK declares `@mediapipe/tasks-vision` (^0.10.15) as a regular dependency, so it is installed automatically when you install `face-validator-sdk`.
|
|
39
38
|
|
|
40
|
-
## 🚀 Quick Start
|
|
39
|
+
## 🚀 Quick Start (Core API)
|
|
41
40
|
|
|
42
41
|
```typescript
|
|
43
42
|
import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
|
|
@@ -122,35 +121,131 @@ interface FaceValidatorOptions {
|
|
|
122
121
|
}
|
|
123
122
|
```
|
|
124
123
|
|
|
125
|
-
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🧩 React Component: `ReactSelfieCapture`
|
|
127
|
+
|
|
128
|
+
If you are building a **React** application and want a **ready‑to‑use selfie capture UI**, the SDK exposes an optional React component that encapsulates:
|
|
126
129
|
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
- Camera access (`getUserMedia`)
|
|
131
|
+
- Validation loop (`FaceValidator`)
|
|
132
|
+
- Overlay drawing (oval + feedback)
|
|
133
|
+
- Preview step (photo + buttons)
|
|
134
|
+
- i18n for pt-BR, en, es
|
|
129
135
|
|
|
130
|
-
###
|
|
136
|
+
### Installation (React project)
|
|
137
|
+
|
|
138
|
+
Your React app should already have `react` and `react-dom` installed. Then:
|
|
131
139
|
|
|
132
140
|
```bash
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
cd face-validator-sdk
|
|
141
|
+
npm install face-validator-sdk
|
|
142
|
+
```
|
|
136
143
|
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
> `@mediapipe/tasks-vision` is installed automatically as a dependency of the SDK.
|
|
145
|
+
> `react` and `react-dom` are declared as **peerDependencies** – they are **not** bundled inside the package.
|
|
146
|
+
> Non‑React applications should use the **core `FaceValidator` API** shown above instead of `ReactSelfieCapture`.
|
|
147
|
+
|
|
148
|
+
### Basic usage
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { ReactSelfieCapture } from 'face-validator-sdk';
|
|
152
|
+
|
|
153
|
+
function SelfieExample() {
|
|
154
|
+
const handleCapture = (imageBase64: string | null) => {
|
|
155
|
+
if (imageBase64) {
|
|
156
|
+
// send to API, store, etc.
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<ReactSelfieCapture
|
|
162
|
+
locale={navigator.language} // 'pt-BR' | 'en' | 'es' (auto-normalized)
|
|
163
|
+
onCapture={handleCapture}
|
|
164
|
+
onDismiss={() => console.log('Modal closed')}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
139
169
|
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
### Props
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
type SupportedLocale = 'pt-BR' | 'en' | 'es';
|
|
174
|
+
|
|
175
|
+
type SelfieCaptureStyles = {
|
|
176
|
+
container?: React.CSSProperties;
|
|
177
|
+
media?: React.CSSProperties;
|
|
178
|
+
messageBanner?: React.CSSProperties;
|
|
179
|
+
primaryButton?: React.CSSProperties;
|
|
180
|
+
secondaryButton?: React.CSSProperties;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
type SelfieCaptureUILabelOverrides = Partial<{
|
|
184
|
+
previewQuestion: string;
|
|
185
|
+
savePhoto: string;
|
|
186
|
+
tryAgain: string;
|
|
187
|
+
cancel: string;
|
|
188
|
+
}>;
|
|
189
|
+
|
|
190
|
+
interface ReactSelfieCaptureProps {
|
|
191
|
+
onCapture: (image: string | null) => void; // base64 data URL or null on cancel
|
|
192
|
+
onDismiss?: () => void;
|
|
193
|
+
|
|
194
|
+
// Behaviour
|
|
195
|
+
locale?: SupportedLocale | string; // Default: 'pt-BR' (auto-normalized)
|
|
196
|
+
videoWidth?: number; // Default: 512
|
|
197
|
+
videoHeight?: number; // Default: 384
|
|
198
|
+
debugMode?: boolean; // Default: false
|
|
199
|
+
modelPath?: string; // Optional MediaPipe WASM path; if omitted, uses internal CDN default
|
|
200
|
+
|
|
201
|
+
// Visual customization (inline styles)
|
|
202
|
+
styles?: SelfieCaptureStyles;
|
|
203
|
+
|
|
204
|
+
// Optional UI labels override (per-locale defaults exist)
|
|
205
|
+
labels?: SelfieCaptureUILabelOverrides;
|
|
206
|
+
}
|
|
142
207
|
```
|
|
143
208
|
|
|
144
|
-
###
|
|
209
|
+
### Labels and i18n
|
|
145
210
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
211
|
+
By default, the component renders UI labels in **Portuguese (pt-BR)**, **English (en)** or **Spanish (es)**:
|
|
212
|
+
|
|
213
|
+
- Preview question (“O que você achou?” / “What do you think?” / “¿Qué te pareció?”)
|
|
214
|
+
- Buttons (“Salvar foto”, “Tentar novamente”, “Cancelar”, etc.)
|
|
215
|
+
|
|
216
|
+
You can override any of these without having to set up an external i18n layer:
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
<ReactSelfieCapture
|
|
220
|
+
locale="pt-BR"
|
|
221
|
+
onCapture={handleCapture}
|
|
222
|
+
labels={{
|
|
223
|
+
previewQuestion: 'Confira sua selfie antes de salvar',
|
|
224
|
+
savePhoto: 'Confirmar selfie',
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
```
|
|
150
228
|
|
|
151
|
-
|
|
229
|
+
### Styling
|
|
230
|
+
|
|
231
|
+
The component ships with a sensible default layout, but you can tweak it via the `styles` prop:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
<ReactSelfieCapture
|
|
235
|
+
onCapture={handleCapture}
|
|
236
|
+
styles={{
|
|
237
|
+
container: { borderRadius: 24 },
|
|
238
|
+
media: { borderRadius: 16 },
|
|
239
|
+
messageBanner: { backgroundColor: '#f0f9ff', color: '#0369a1' },
|
|
240
|
+
primaryButton: { backgroundColor: '#2563eb', borderColor: '#2563eb' },
|
|
241
|
+
secondaryButton: { borderRadius: 9999 },
|
|
242
|
+
}}
|
|
243
|
+
/>;
|
|
152
244
|
```
|
|
153
245
|
|
|
246
|
+
> Tip: you can wrap `ReactSelfieCapture` in your own modal/dialog and pass `onDismiss` to close it from the **Cancel** button.
|
|
247
|
+
|
|
248
|
+
|
|
154
249
|
## 🏗️ Architecture
|
|
155
250
|
|
|
156
251
|
### MediaPipe Integration
|
|
@@ -160,28 +255,6 @@ The SDK uses two MediaPipe models running in parallel:
|
|
|
160
255
|
1. **FaceLandmarker**: 478 facial landmarks + face detection
|
|
161
256
|
2. **HandLandmarker**: 21 hand landmarks per hand
|
|
162
257
|
|
|
163
|
-
```
|
|
164
|
-
┌─────────────────────────────────────────┐
|
|
165
|
-
│ FaceValidator │
|
|
166
|
-
├─────────────────────────────────────────┤
|
|
167
|
-
│ ┌─────────────────┐ ┌──────────────┐ │
|
|
168
|
-
│ │ FaceLandmarker │ │ HandLandmarker│ │
|
|
169
|
-
│ │ (478 points) │ │ (21 pts/hand) │ │
|
|
170
|
-
│ └─────────────────┘ └──────────────┘ │
|
|
171
|
-
│ ↓ ↓ │
|
|
172
|
-
│ ┌──────────────────────────────────┐ │
|
|
173
|
-
│ │ Validation Pipeline │ │
|
|
174
|
-
│ │ 1. Distance │ │
|
|
175
|
-
│ │ 2. Centering │ │
|
|
176
|
-
│ │ 3. Face geometry │ │
|
|
177
|
-
│ │ 4. Head pose │ │
|
|
178
|
-
│ │ 5. Hand proximity ⭐NEW │ │
|
|
179
|
-
│ │ 6. Illumination │ │
|
|
180
|
-
│ │ 7. Stability │ │
|
|
181
|
-
│ └──────────────────────────────────┘ │
|
|
182
|
-
└─────────────────────────────────────────┘
|
|
183
|
-
```
|
|
184
|
-
|
|
185
258
|
## 📚 Why MediaPipe?
|
|
186
259
|
|
|
187
260
|
Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
|
|
@@ -195,64 +268,6 @@ Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
|
|
|
195
268
|
| Accuracy | ~60-70% | ✅ **~90-95%** |
|
|
196
269
|
| Model size | ~8MB | ~15MB |
|
|
197
270
|
|
|
198
|
-
## 🔧 Development
|
|
199
|
-
|
|
200
|
-
### Scripts
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
npm run dev # Start local dev server (webpack)
|
|
204
|
-
npm run build # Build SDK (CJS, ESM, UMD)
|
|
205
|
-
npm run build:demo # Build production demo
|
|
206
|
-
npm run lint # Run ESLint
|
|
207
|
-
npm run format # Format code with Prettier
|
|
208
|
-
npm run test # Run tests (Jest)
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Project Structure
|
|
212
|
-
|
|
213
|
-
```
|
|
214
|
-
face-validator-sdk/
|
|
215
|
-
├── src/
|
|
216
|
-
│ ├── FaceValidator.ts # Main validator class
|
|
217
|
-
│ ├── types.ts # TypeScript types
|
|
218
|
-
│ ├── utils.ts # Validation functions
|
|
219
|
-
│ ├── i18n.ts # Internationalization
|
|
220
|
-
│ └── index.ts # Public API exports
|
|
221
|
-
├── demo/
|
|
222
|
-
│ ├── demo.ts # Local dev demo
|
|
223
|
-
│ ├── demo-standalone.ts # Production demo
|
|
224
|
-
│ └── public/
|
|
225
|
-
│ └── index.html # Demo HTML
|
|
226
|
-
├── dist/ # SDK build output
|
|
227
|
-
│ ├── face-validator-sdk.esm.js
|
|
228
|
-
│ ├── face-validator-sdk.cjs.js
|
|
229
|
-
│ ├── face-validator-sdk.umd.js
|
|
230
|
-
│ └── types/ # TypeScript declarations
|
|
231
|
-
├── .github/
|
|
232
|
-
│ └── workflows/
|
|
233
|
-
│ ├── ci.yml # CI/CD pipeline
|
|
234
|
-
│ └── deploy-vercel.yml # Vercel deployment
|
|
235
|
-
└── vercel.json # Vercel configuration
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
## 🚀 Deployment
|
|
239
|
-
|
|
240
|
-
### Vercel (Automatic)
|
|
241
|
-
|
|
242
|
-
1. Connect repository to Vercel
|
|
243
|
-
2. Add secrets to GitHub:
|
|
244
|
-
- `VERCEL_TOKEN`
|
|
245
|
-
- `VERCEL_ORG_ID`
|
|
246
|
-
- `VERCEL_PROJECT_ID`
|
|
247
|
-
3. Push to `main` branch → auto-deploy
|
|
248
|
-
|
|
249
|
-
### Manual Deployment
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
npm run build:demo
|
|
253
|
-
# Deploy demo/dist/ to any static host
|
|
254
|
-
```
|
|
255
|
-
|
|
256
271
|
## 🤝 Contributing
|
|
257
272
|
|
|
258
273
|
Contributions are welcome! Please:
|
|
@@ -263,17 +278,6 @@ Contributions are welcome! Please:
|
|
|
263
278
|
4. Push to branch: `git push origin feature/amazing-feature`
|
|
264
279
|
5. Open a Pull Request
|
|
265
280
|
|
|
266
|
-
### Commit Convention
|
|
267
|
-
|
|
268
|
-
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
|
269
|
-
|
|
270
|
-
- `feat:` New feature
|
|
271
|
-
- `fix:` Bug fix
|
|
272
|
-
- `docs:` Documentation changes
|
|
273
|
-
- `chore:` Maintenance tasks
|
|
274
|
-
- `refactor:` Code refactoring
|
|
275
|
-
- `test:` Add/update tests
|
|
276
|
-
|
|
277
281
|
## 📄 License
|
|
278
282
|
|
|
279
283
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -291,4 +295,3 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
291
295
|
|
|
292
296
|
---
|
|
293
297
|
|
|
294
|
-
Made with ❤️ using MediaPipe
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
(()=>{"use strict";var e={d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{FaceValidator:()=>T,ValidationStatus:()=>a,default:()=>S,getLoadingModelsMessage:()=>l,getMessage:()=>r,getValidationMessages:()=>s});const n=require("@mediapipe/tasks-vision");var a;!function(e){e.INITIALIZING="INITIALIZING",e.NO_FACE_DETECTED="NO_FACE_DETECTED",e.FACE_DETECTED="FACE_DETECTED",e.TOO_CLOSE="TOO_CLOSE",e.TOO_FAR="TOO_FAR",e.OFF_CENTER="OFF_CENTER",e.FACE_OBSTRUCTED="FACE_OBSTRUCTED",e.HEAD_NOT_STRAIGHT="HEAD_NOT_STRAIGHT",e.MULTIPLE_FACES="MULTIPLE_FACES",e.POOR_ILLUMINATION="POOR_ILLUMINATION",e.NOT_NEUTRAL_EXPRESSION="NOT_NEUTRAL_EXPRESSION",e.DARK_GLASSES="DARK_GLASSES",e.STAY_STILL="STAY_STILL",e.CAPTURING="CAPTURING",e.SUCCESS="SUCCESS",e.ERROR="ERROR"}(a||(a={}));const i={"pt-BR":{[a.INITIALIZING]:"Inicializando câmera e detector...",[a.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[a.FACE_DETECTED]:"Analisando...",[a.TOO_CLOSE]:"Afaste-se um pouco",[a.TOO_FAR]:"Aproxime-se da câmera",[a.OFF_CENTER]:"Centralize o rosto no centro do oval",[a.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[a.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[a.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[a.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação e centralize seu rosto no centro do oval.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[a.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[a.STAY_STILL]:"Fique imóvel para capturar a foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"Captura realizada!",[a.ERROR]:"Ocorreu um erro."},en:{[a.INITIALIZING]:"Initializing camera and detector...",[a.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[a.FACE_DETECTED]:"Analyzing...",[a.TOO_CLOSE]:"Move back a little",[a.TOO_FAR]:"Move closer to the camera",[a.OFF_CENTER]:"Center your face in the center of the oval",[a.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[a.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[a.MULTIPLE_FACES]:"Keep only one person in the frame.",[a.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[a.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[a.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[a.STAY_STILL]:"Stay still to capture the photo",[a.CAPTURING]:"Capturing...",[a.SUCCESS]:"Capture complete!",[a.ERROR]:"An error occurred."},es:{[a.INITIALIZING]:"Inicializando cámara y detector...",[a.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[a.FACE_DETECTED]:"Analizando...",[a.TOO_CLOSE]:"Aléjese un poco",[a.TOO_FAR]:"Acérquese a la cámara",[a.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[a.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[a.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[a.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[a.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[a.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[a.STAY_STILL]:"Permanezca quieto para capturar la foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"¡Captura realizada!",[a.ERROR]:"Ocurrió un error."}},o={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function s(e){return Object.assign({},i[e])}function r(e,t){var n;return null!==(n=i[t][e])&&void 0!==n?n:o[t]}function l(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function c(e){const t=e.data;let n=0;for(let e=0;e<t.length;e+=4)n+=.2126*t[e]+.7152*t[e+1]+.0722*t[e+2];return n/(t.length/4)}const h=[33,133,159,145],d=[263,362,386,374],u=[61,291,0,17,39,269,270,409],m=.34;var E=function(e,t,n,a){return new(n||(n=Promise))(function(i,o){function s(e){try{l(a.next(e))}catch(e){o(e)}}function r(e){try{l(a.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(s,r)}l((a=a.apply(e,t||[])).next())})};const g={overlayCanvasElement:void 0,videoWidth:512,videoHeight:384,minDetectionConfidence:.4,minIlluminationThreshold:50,minFaceSizeFactor:.15,maxFaceSizeFactor:.75,stabilizationTimeThreshold:1e3,stabilityMovementThreshold:5,minFaceVisibilityScore:.4,maxHeadTiltDegrees:30,maxHandFaceDistance:.15,debugMode:!1,locale:"en",customMessages:{}};class T{constructor(e){this.faceLandmarker=null,this.handLandmarker=null,this.animationFrameId=null,this.lastDetection=null,this.stableSince=null,this.isCapturing=!1,this.options=this.resolveOptions(e),this.setStatus(a.INITIALIZING),this.init()}resolveOptions(e){const t=e.modelPath||"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";return Object.assign(Object.assign(Object.assign({},g),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{}})}init(){return E(this,void 0,void 0,function*(){try{const e=l(this.options.locale);this.setStatus(a.INITIALIZING,void 0,e);const t=yield n.FilesetResolver.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield n.FaceLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numFaces:2,minFaceDetectionConfidence:this.options.minDetectionConfidence,minFacePresenceConfidence:this.options.minFaceVisibilityScore,minTrackingConfidence:this.options.minFaceVisibilityScore}),this.handLandmarker=yield n.HandLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5}),this.startDetectionLoop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:r(e,this.options.locale))}setStatus(e,t,n){const i=this.getMessageForStatus(e,n);this.options.onStatusUpdate(e,i),e===a.ERROR&&t&&this.options.onError(e,t)}startDetectionLoop(){const e=this.options.videoElement,t=this.options.videoWidth||640,n=this.options.videoHeight||480,i=()=>E(this,void 0,void 0,function*(){var o;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const i=performance.now();let s=a.NO_FACE_DETECTED,r=null,l=[];const E=this.faceLandmarker.detectForVideo(e,i),g=this.handLandmarker.detectForVideo(e,i);if(g.landmarks&&g.landmarks.length>0&&(l=g.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=g.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),E.faceLandmarks&&E.faceLandmarks.length>1){s=a.MULTIPLE_FACES,this.stableSince=null;const e=E.faceLandmarks[0],t=(null===(o=E.faceBlendshapes)||void 0===o?void 0:o[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:i})}else if(E.faceLandmarks&&1===E.faceLandmarks.length){const o=E.faceLandmarks[0],g=this.estimateBoundingBox(o);r={boundingBox:g,landmarks:o,timestamp:i};const T=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(g,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==T)s="TOO_CLOSE"===T?a.TOO_CLOSE:a.TOO_FAR,this.stableSince=null;else{const E=o[4],T=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*m);return i*i+o*o<=1}(E.x,E.y,t,n);if(function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*m,r=e.xMin*t,l=(e.xMin+e.width)*t,c=e.yMin*n,h=(e.yMin+e.height)*n,d=((r+l)/2-a)/o,u=((c+h)/2-i)/s;if(d*d+u*u>1)return!1;const E=[{x:r,y:c},{x:l,y:c},{x:r,y:h},{x:l,y:h}];for(const e of E){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1.2&&0}}(g,t,n),T)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=u.map(t=>e[t]),i=a.reduce((e,t)=>e+t.y,0)/a.length,o=Math.min(...a.map(e=>e.y)),s=Math.max(...a.map(e=>e.y))-o,r=t.height;return!(i<n.y-.01||i-n.y<.06*r||s<.02*r)}(o,g))if(function(e,t=25){if(e.length<478)return!1;const n=e[h[0]],a=e[d[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],c=Math.abs(n.y-a.y),u=Math.abs(n.x-a.x);if(u<.01)return!1;const m=c/u;if(Math.atan(m)*(180/Math.PI)>t)return!1;const E=(n.x+a.x)/2,g=i.x-E,T=Math.abs(n.x-a.x);if(T<.01)return!1;const S=Math.abs(g)/T;if(Math.atan(S)*(180/Math.PI)>t)return!1;if(!function(e){if(e.length<478)return!1;const t=e[234],n=e[454],a=e[4],i=Math.abs(t.x-a.x),o=Math.abs(n.x-a.x);return!((i>.01&&o>.01?Math.max(i,o)/Math.min(i,o):1)>1.4||void 0!==t.z&&void 0!==n.z&&Math.abs(t.z-n.z)>.05)}(e))return!1;const f=(n.y+a.y)/2,O=(o.y+s.y)/2,p=r.y-l.y;if(p<.1)return!1;if(l.y>f+.02)return!1;if(f>i.y+.02)return!1;if(i.y>O+.02)return!1;if(O>=r.y)return!1;const I=(f-l.y)/p,y=(i.y-f)/p,C=(O-i.y)/p,A=(r.y-O)/p;return!(I<.06||I>.38||y<.03||y>.3||C<.02||C>.25||A<.04||A>.38)}(o,this.options.maxHeadTiltDegrees))if(l.length>0&&function(e,t,n=.15){const a=t.xMin+t.width/2,i=t.yMin+t.height/2;for(const t of e.landmarks){const e=t.x-a,o=t.y-i;if(Math.sqrt(e*e+o*o)<n)return!0}return!1}(l[0],g,this.options.maxHandFaceDistance))s=a.FACE_OBSTRUCTED,this.stableSince=null;else if(function(e){if(e.length<478)return!1;const t=e[159],n=e[144],a=e[386],i=e[373],o=Math.abs(t.y-n.y),s=Math.abs(a.y-i.y);if(o<.01||s<.01)return!1;const r=e[13],l=e[14];if(Math.abs(r.y-l.y)>.025)return!1;const c=e[61],h=e[291],d=e[4];return!((c.y+h.y)/2-d.y<.05)}(o))if(function(e,t){if(t.length<478)return!1;try{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)return!1;const i=e.videoWidth,o=e.videoHeight,s=[t[33],t[133],t[159],t[144],t[145]],r=[t[263],t[362],t[386],t[373],t[374]],l=e=>{const t=e.map(e=>e.x*i),n=e.map(e=>e.y*o),a=Math.max(0,Math.min(...t)-5),s=Math.min(i,Math.max(...t)+5),r=Math.max(0,Math.min(...n)-5);return{x:a,y:r,width:s-a,height:Math.min(o,Math.max(...n)+5)-r}},h=t=>(n.width=t.width,n.height=t.height,a.drawImage(e,t.x,t.y,t.width,t.height,0,0,t.width,t.height),c(a.getImageData(0,0,t.width,t.height))),d=l(s),u=l(r);return(h(d)+h(u))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,o))s=a.DARK_GLASSES,this.stableSince=null;else{const o=document.createElement("canvas"),l=g.xMin*e.videoWidth,h=g.yMin*e.videoHeight,d=g.width*e.videoWidth,u=g.height*e.videoHeight;o.width=d,o.height=u;const m=o.getContext("2d",{willReadFrequently:!0});if(m){m.drawImage(e,l,h,d,u,0,0,d,u);c(m.getImageData(0,0,o.width,o.height))<this.options.minIlluminationThreshold?(s=a.POOR_ILLUMINATION,this.stableSince=null):function(e,t,n=5,a=512,i=384){if(!e||!t)return!1;const o=(e.boundingBox.xMin+e.boundingBox.width/2)*a,s=(e.boundingBox.yMin+e.boundingBox.height/2)*i,r=(t.boundingBox.xMin+t.boundingBox.width/2)*a,l=(t.boundingBox.yMin+t.boundingBox.height/2)*i,c=Math.abs(o-r),h=Math.abs(s-l),d=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,u=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return c<=n&&h<=n&&d<=2*n&&u<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=i),s=i-this.stableSince>=this.options.stabilizationTimeThreshold?a.CAPTURING:a.STAY_STILL):(this.stableSince=null,s=a.STAY_STILL)}else s=a.FACE_DETECTED,this.stableSince=null}else s=a.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=a.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=a.FACE_OBSTRUCTED,this.stableSince=null;else s=a.OFF_CENTER,this.stableSince=null}}else this.lastDetection=null,this.stableSince=null;if(this.lastDetection=r,this.setStatus(s),this.options.overlayCanvasElement&&function(e,t,n,i,o){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,c=r/2,u=l/2;s.clearRect(0,0,r,l);const E=.2*r,g=l*m;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(c,u,E,g,0,0,2*Math.PI),s.closePath(),s.globalCompositeOperation="destination-out",s.fill(),s.restore(),s.strokeStyle="rgba(255, 255, 255, 0.9)",s.lineWidth=3,s.beginPath(),s.ellipse(c,u,E,g,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(c-6,u),s.lineTo(c+6,u),s.moveTo(c,u-6),s.lineTo(c,u+6),s.stroke(),t&&i){const e=i.landmarks;if(e.length>=478){const t=e[10],i=e[152],o=e[234],c=e[454],u=e.map(e=>e.x),m=e.map(e=>e.y),E=Math.min(...u),g=Math.max(...u),T=Math.min(...m),S=g-E,f=Math.max(...m)-T,O=.08,p=(E-S*O)*r,I=(T-f*O)*l,y=S*(1+2*O)*r,C=f*(1+2*O)*l;let A="red";n===a.STAY_STILL||n===a.CAPTURING?A="lime":n===a.FACE_DETECTED&&(A="yellow"),s.strokeStyle=A,s.lineWidth=3,s.strokeRect(p,I,y,C);const _=e[4];e[h[0]],e[d[0]],s.fillStyle="cyan",s.beginPath(),s.arc(_.x*r,_.y*l,5,0,2*Math.PI),s.fill(),s.fillStyle="magenta",s.beginPath(),s.arc(t.x*r,t.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="lime",s.beginPath(),s.arc(i.x*r,i.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="yellow",[e[33],e[133],e[159],e[144],e[145]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="yellow",[e[263],e[362],e[386],e[373],e[374]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="purple",s.beginPath(),s.arc(o.x*r,o.y*l,3,0,2*Math.PI),s.fill(),s.beginPath(),s.arc(c.x*r,c.y*l,3,0,2*Math.PI),s.fill()}}t&&o&&o.length>0&&o.forEach(e=>{s.fillStyle="orange",e.landmarks.forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()})})}(this.options.overlayCanvasElement,this.options.debugMode||!1,s,r||void 0,l.length>0?l:void 0),s===a.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(a.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}this.animationFrameId=requestAnimationFrame(i)}else this.animationFrameId=requestAnimationFrame(i)});this.animationFrameId=requestAnimationFrame(i)}estimateBoundingBox(e){const t=e.map(e=>e.x),n=e.map(e=>e.y),a=Math.min(...t),i=Math.max(...t),o=Math.min(...n);return{xMin:a,yMin:o,width:i-a,height:Math.max(...n)-o}}captureImage(){return E(this,void 0,void 0,function*(){const e=this.options.videoElement,t=document.createElement("canvas");t.width=e.videoWidth,t.height=e.videoHeight;const n=t.getContext("2d");n?(n.drawImage(e,0,0,t.width,t.height),t.toBlob(e=>{e?this.options.onCaptureSuccess(e):this.setStatus(a.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(a.ERROR,new Error("Failed to get canvas context"))})}stop(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.faceLandmarker&&this.faceLandmarker.close(),this.handLandmarker&&this.handLandmarker.close()}}const S=T;module.exports=t})();
|
|
1
|
+
/*! For license information please see face-validator-sdk.cjs.js.LICENSE.txt */
|
|
2
|
+
(()=>{"use strict";var e={20(e,t,n){var a=n(953),i=Symbol.for("react.element"),o=Symbol.for("react.fragment"),s=Object.prototype.hasOwnProperty,r=a.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var a,o={},c=null,d=null;for(a in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)s.call(t,a)&&!l.hasOwnProperty(a)&&(o[a]=t[a]);if(e&&e.defaultProps)for(a in t=e.defaultProps)void 0===o[a]&&(o[a]=t[a]);return{$$typeof:i,type:e,key:c,ref:d,props:o,_owner:r.current}}t.Fragment=o,t.jsx=c,t.jsxs=c},848(e,t,n){e.exports=n(20)},953(e){e.exports=require("react")}},t={};function n(a){var i=t[a];if(void 0!==i)return i.exports;var o=t[a]={exports:{}};return e[a](o,o.exports,n),o.exports}n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};n.r(a),n.d(a,{FaceValidator:()=>S,ReactSelfieCapture:()=>O,ValidationStatus:()=>o,default:()=>v,getLoadingModelsMessage:()=>d,getMessage:()=>c,getValidationMessages:()=>l});const i=require("@mediapipe/tasks-vision");var o;!function(e){e.INITIALIZING="INITIALIZING",e.NO_FACE_DETECTED="NO_FACE_DETECTED",e.FACE_DETECTED="FACE_DETECTED",e.TOO_CLOSE="TOO_CLOSE",e.TOO_FAR="TOO_FAR",e.OFF_CENTER="OFF_CENTER",e.FACE_OBSTRUCTED="FACE_OBSTRUCTED",e.HEAD_NOT_STRAIGHT="HEAD_NOT_STRAIGHT",e.MULTIPLE_FACES="MULTIPLE_FACES",e.POOR_ILLUMINATION="POOR_ILLUMINATION",e.NOT_NEUTRAL_EXPRESSION="NOT_NEUTRAL_EXPRESSION",e.DARK_GLASSES="DARK_GLASSES",e.STAY_STILL="STAY_STILL",e.CAPTURING="CAPTURING",e.SUCCESS="SUCCESS",e.ERROR="ERROR"}(o||(o={}));const s={"pt-BR":{[o.INITIALIZING]:"Inicializando câmera e detector...",[o.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[o.FACE_DETECTED]:"Analisando...",[o.TOO_CLOSE]:"Afaste-se um pouco",[o.TOO_FAR]:"Aproxime-se da câmera",[o.OFF_CENTER]:"Centralize o rosto no centro do oval",[o.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[o.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[o.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[o.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação.",[o.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[o.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[o.STAY_STILL]:"Fique imóvel para capturar a foto",[o.CAPTURING]:"Capturando...",[o.SUCCESS]:"Captura realizada!",[o.ERROR]:"Ocorreu um erro."},en:{[o.INITIALIZING]:"Initializing camera and detector...",[o.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[o.FACE_DETECTED]:"Analyzing...",[o.TOO_CLOSE]:"Move back a little",[o.TOO_FAR]:"Move closer to the camera",[o.OFF_CENTER]:"Center your face in the center of the oval",[o.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[o.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[o.MULTIPLE_FACES]:"Keep only one person in the frame.",[o.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[o.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[o.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[o.STAY_STILL]:"Stay still to capture the photo",[o.CAPTURING]:"Capturing...",[o.SUCCESS]:"Capture complete!",[o.ERROR]:"An error occurred."},es:{[o.INITIALIZING]:"Inicializando cámara y detector...",[o.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[o.FACE_DETECTED]:"Analizando...",[o.TOO_CLOSE]:"Aléjese un poco",[o.TOO_FAR]:"Acérquese a la cámara",[o.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[o.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[o.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[o.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[o.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[o.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[o.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[o.STAY_STILL]:"Permanezca quieto para capturar la foto",[o.CAPTURING]:"Capturando...",[o.SUCCESS]:"¡Captura realizada!",[o.ERROR]:"Ocurrió un error."}},r={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function l(e){return Object.assign({},s[e])}function c(e,t){var n;return null!==(n=s[t][e])&&void 0!==n?n:r[t]}function d(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function h(e){const t=e.data;let n=0;for(let e=0;e<t.length;e+=4)n+=.2126*t[e]+.7152*t[e+1]+.0722*t[e+2];return n/(t.length/4)}const u=[33,133,159,145],m=[263,362,386,374],f=[61,291,0,17,39,269,270,409],g=.34;var E=function(e,t,n,a){return new(n||(n=Promise))(function(i,o){function s(e){try{l(a.next(e))}catch(e){o(e)}}function r(e){try{l(a.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(s,r)}l((a=a.apply(e,t||[])).next())})};const p={overlayCanvasElement:void 0,videoWidth:512,videoHeight:384,minDetectionConfidence:.4,minIlluminationThreshold:50,minFaceSizeFactor:.15,maxFaceSizeFactor:.75,stabilizationTimeThreshold:1e3,stabilityMovementThreshold:5,minFaceVisibilityScore:.4,maxHeadTiltDegrees:30,maxHandFaceDistance:.15,debugMode:!1,locale:"en",customMessages:{}};class S{constructor(e){this.faceLandmarker=null,this.handLandmarker=null,this.animationFrameId=null,this.lastDetection=null,this.stableSince=null,this.isCapturing=!1,this.options=this.resolveOptions(e),this.setStatus(o.INITIALIZING),this.init()}resolveOptions(e){const t=e.modelPath||"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";return Object.assign(Object.assign(Object.assign({},p),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{}})}init(){return E(this,void 0,void 0,function*(){try{const e=d(this.options.locale);this.setStatus(o.INITIALIZING,void 0,e);const t=yield i.FilesetResolver.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield i.FaceLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numFaces:2,minFaceDetectionConfidence:this.options.minDetectionConfidence,minFacePresenceConfidence:this.options.minFaceVisibilityScore,minTrackingConfidence:this.options.minFaceVisibilityScore}),this.handLandmarker=yield i.HandLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5}),this.startDetectionLoop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(o.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:c(e,this.options.locale))}setStatus(e,t,n){const a=this.getMessageForStatus(e,n);this.options.onStatusUpdate(e,a),e===o.ERROR&&t&&this.options.onError(e,t)}startDetectionLoop(){const e=this.options.videoElement,t=this.options.videoWidth||640,n=this.options.videoHeight||480,a=()=>E(this,void 0,void 0,function*(){var i;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const a=performance.now();let s=o.NO_FACE_DETECTED,r=null,l=[];const c=this.faceLandmarker.detectForVideo(e,a),d=this.handLandmarker.detectForVideo(e,a);if(d.landmarks&&d.landmarks.length>0&&(l=d.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=d.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),c.faceLandmarks&&c.faceLandmarks.length>1){s=o.MULTIPLE_FACES,this.stableSince=null;const e=c.faceLandmarks[0],t=(null===(i=c.faceBlendshapes)||void 0===i?void 0:i[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:a})}else if(c.faceLandmarks&&1===c.faceLandmarks.length){const i=c.faceLandmarks[0],d=this.estimateBoundingBox(i);r={boundingBox:d,landmarks:i,timestamp:a};const E=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(d,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==E)s="TOO_CLOSE"===E?o.TOO_CLOSE:o.TOO_FAR,this.stableSince=null;else{const c=i[4],E=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*g);return i*i+o*o<=.6}(c.x,c.y,t,n),p=function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*g,r=e.xMin*t,l=(e.xMin+e.width)*t,c=e.yMin*n,d=(e.yMin+e.height)*n,h=((r+l)/2-a)/o,u=((c+d)/2-i)/s;if(h*h+u*u>1)return!1;const m=[{x:r,y:c},{x:l,y:c},{x:r,y:d},{x:l,y:d}];let f=0;for(const e of m){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1&&f++}return 0===f}(d,t,n);if(E&&p)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=f.map(t=>e[t]),i=a.reduce((e,t)=>e+t.y,0)/a.length,o=Math.min(...a.map(e=>e.y)),s=Math.max(...a.map(e=>e.y))-o,r=t.height;return!(i<n.y-.01||i-n.y<.06*r||s<.02*r)}(i,d))if(function(e,t=25){if(e.length<478)return!1;const n=e[u[0]],a=e[m[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],c=Math.abs(n.y-a.y),d=Math.abs(n.x-a.x);if(d<.01)return!1;const h=c/d;if(Math.atan(h)*(180/Math.PI)>t)return!1;const f=(n.x+a.x)/2,g=i.x-f,E=Math.abs(n.x-a.x);if(E<.01)return!1;const p=Math.abs(g)/E;if(Math.atan(p)*(180/Math.PI)>t)return!1;if(!function(e){if(e.length<478)return!1;const t=e[234],n=e[454],a=e[4],i=Math.abs(t.x-a.x),o=Math.abs(n.x-a.x);return!((i>.01&&o>.01?Math.max(i,o)/Math.min(i,o):1)>1.4||void 0!==t.z&&void 0!==n.z&&Math.abs(t.z-n.z)>.05)}(e))return!1;const S=(n.y+a.y)/2,y=(o.y+s.y)/2,T=r.y-l.y;if(T<.1)return!1;if(l.y>S+.02)return!1;if(S>i.y+.02)return!1;if(i.y>y+.02)return!1;if(y>=r.y)return!1;const b=(S-l.y)/T,O=(i.y-S)/T,v=(y-i.y)/T,I=(r.y-y)/T;return!(b<.06||b>.38||O<.03||O>.3||v<.02||v>.25||I<.04||I>.38)}(i,this.options.maxHeadTiltDegrees))if(l.length>0&&function(e,t,n=.15){const a=t.xMin+t.width/2,i=t.yMin+t.height/2;for(const t of e.landmarks){const e=t.x-a,o=t.y-i;if(Math.sqrt(e*e+o*o)<n)return!0}return!1}(l[0],d,this.options.maxHandFaceDistance))s=o.FACE_OBSTRUCTED,this.stableSince=null;else if(function(e){if(e.length<478)return!1;const t=e[159],n=e[144],a=e[386],i=e[373],o=Math.abs(t.y-n.y),s=Math.abs(a.y-i.y);if(o<.01||s<.01)return!1;const r=e[13],l=e[14];if(Math.abs(r.y-l.y)>.025)return!1;const c=e[61],d=e[291],h=e[4];return!((c.y+d.y)/2-h.y<.05)}(i))if(function(e,t){if(t.length<478)return!1;try{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)return!1;const i=e.videoWidth,o=e.videoHeight,s=[t[33],t[133],t[159],t[144],t[145]],r=[t[263],t[362],t[386],t[373],t[374]],l=e=>{const t=e.map(e=>e.x*i),n=e.map(e=>e.y*o),a=Math.max(0,Math.min(...t)-5),s=Math.min(i,Math.max(...t)+5),r=Math.max(0,Math.min(...n)-5);return{x:a,y:r,width:s-a,height:Math.min(o,Math.max(...n)+5)-r}},c=t=>(n.width=t.width,n.height=t.height,a.drawImage(e,t.x,t.y,t.width,t.height,0,0,t.width,t.height),h(a.getImageData(0,0,t.width,t.height))),d=l(s),u=l(r);return(c(d)+c(u))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,i))s=o.DARK_GLASSES,this.stableSince=null;else{const i=document.createElement("canvas"),l=d.xMin*e.videoWidth,c=d.yMin*e.videoHeight,u=d.width*e.videoWidth,m=d.height*e.videoHeight;i.width=u,i.height=m;const f=i.getContext("2d",{willReadFrequently:!0});if(f){f.drawImage(e,l,c,u,m,0,0,u,m);h(f.getImageData(0,0,i.width,i.height))<this.options.minIlluminationThreshold?(s=o.POOR_ILLUMINATION,this.stableSince=null):function(e,t,n=5,a=512,i=384){if(!e||!t)return!1;const o=(e.boundingBox.xMin+e.boundingBox.width/2)*a,s=(e.boundingBox.yMin+e.boundingBox.height/2)*i,r=(t.boundingBox.xMin+t.boundingBox.width/2)*a,l=(t.boundingBox.yMin+t.boundingBox.height/2)*i,c=Math.abs(o-r),d=Math.abs(s-l),h=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,u=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return c<=n&&d<=n&&h<=2*n&&u<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=a),s=a-this.stableSince>=this.options.stabilizationTimeThreshold?o.CAPTURING:o.STAY_STILL):(this.stableSince=null,s=o.STAY_STILL)}else s=o.FACE_DETECTED,this.stableSince=null}else s=o.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=o.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=o.FACE_OBSTRUCTED,this.stableSince=null;else s=o.OFF_CENTER,this.stableSince=null}}else this.lastDetection=null,this.stableSince=null;if(this.lastDetection=r,this.setStatus(s),this.options.overlayCanvasElement&&function(e,t,n,a,i){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,c=r/2,d=l/2;s.clearRect(0,0,r,l);const h=.2*r,f=l*g;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(c,d,h,f,0,0,2*Math.PI),s.closePath(),s.globalCompositeOperation="destination-out",s.fill(),s.restore(),s.strokeStyle="rgba(255, 255, 255, 0.9)",s.lineWidth=3,s.beginPath(),s.ellipse(c,d,h,f,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(c-6,d),s.lineTo(c+6,d),s.moveTo(c,d-6),s.lineTo(c,d+6),s.stroke(),t&&a){const e=a.landmarks;if(e.length>=478){const t=e[10],a=e[152],i=e[234],c=e[454],d=e.map(e=>e.x),h=e.map(e=>e.y),f=Math.min(...d),g=Math.max(...d),E=Math.min(...h),p=g-f,S=Math.max(...h)-E,y=.08,T=(f-p*y)*r,b=(E-S*y)*l,O=p*(1+2*y)*r,v=S*(1+2*y)*l;let I="red";n===o.STAY_STILL||n===o.CAPTURING?I="lime":n===o.FACE_DETECTED&&(I="yellow"),s.strokeStyle=I,s.lineWidth=3,s.strokeRect(T,b,O,v);const C=e[4];e[u[0]],e[m[0]],s.fillStyle="cyan",s.beginPath(),s.arc(C.x*r,C.y*l,5,0,2*Math.PI),s.fill(),s.fillStyle="magenta",s.beginPath(),s.arc(t.x*r,t.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="lime",s.beginPath(),s.arc(a.x*r,a.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="yellow",[e[33],e[133],e[159],e[144],e[145]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="yellow",[e[263],e[362],e[386],e[373],e[374]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="purple",s.beginPath(),s.arc(i.x*r,i.y*l,3,0,2*Math.PI),s.fill(),s.beginPath(),s.arc(c.x*r,c.y*l,3,0,2*Math.PI),s.fill()}}t&&i&&i.length>0&&i.forEach(e=>{s.fillStyle="orange",e.landmarks.forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()})})}(this.options.overlayCanvasElement,this.options.debugMode||!1,s,r||void 0,l.length>0?l:void 0),s===o.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(o.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(o.ERROR,t)}this.animationFrameId=requestAnimationFrame(a)}else this.animationFrameId=requestAnimationFrame(a)});this.animationFrameId=requestAnimationFrame(a)}estimateBoundingBox(e){const t=e.map(e=>e.x),n=e.map(e=>e.y),a=Math.min(...t),i=Math.max(...t),o=Math.min(...n);return{xMin:a,yMin:o,width:i-a,height:Math.max(...n)-o}}captureImage(){return E(this,void 0,void 0,function*(){const e=this.options.videoElement,t=document.createElement("canvas");t.width=e.videoWidth,t.height=e.videoHeight;const n=t.getContext("2d");n?(n.drawImage(e,0,0,t.width,t.height),t.toBlob(e=>{e?this.options.onCaptureSuccess(e):this.setStatus(o.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(o.ERROR,new Error("Failed to get canvas context"))})}stop(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.faceLandmarker&&this.faceLandmarker.close(),this.handLandmarker&&this.handLandmarker.close()}}var y=n(848),T=n(953);const b={"pt-BR":{previewQuestion:"O que você achou?",savePhoto:"Salvar foto",tryAgain:"Tentar novamente",cancel:"Cancelar"},en:{previewQuestion:"What do you think?",savePhoto:"Save photo",tryAgain:"Try again",cancel:"Cancel"},es:{previewQuestion:"¿Qué te pareció?",savePhoto:"Guardar foto",tryAgain:"Intentar de nuevo",cancel:"Cancelar"}},O=({onCapture:e,onDismiss:t,locale:n,videoWidth:a=512,videoHeight:i=384,debugMode:s=!1,modelPath:r,styles:l,labels:d})=>{const h=(e=>{if(!e)return"pt-BR";const t=e.toLowerCase();return t.startsWith("en")?"en":t.startsWith("es")?"es":"pt-BR"})(n),u=b[h],m=Object.assign(Object.assign({},u),null!=d?d:{}),f=(0,T.useRef)(null),g=(0,T.useRef)(null),E=(0,T.useRef)(null),[p,O]=(0,T.useState)(o.INITIALIZING),[v,I]=(0,T.useState)(c(o.INITIALIZING,h)),[C,R]=(0,T.useState)(!0),[_,x]=(0,T.useState)(null),A=(0,T.useCallback)((e,t)=>{O(e),I(t)},[]),M=(0,T.useCallback)(e=>{var t;O(o.SUCCESS),I(c(o.SUCCESS,h)),(t=e,new Promise((e,n)=>{const a=new FileReader;a.onloadend=()=>e(a.result),a.onerror=n,a.readAsDataURL(t)})).then(e=>{x(e),R(!1)})},[h]),L=(0,T.useCallback)(()=>{e(null!=_?_:null),null==t||t()},[e,t,_]),F=(0,T.useCallback)(()=>{x(null),O(o.INITIALIZING),I(c(o.INITIALIZING,h)),R(!0)},[h]),N=(0,T.useCallback)((e,t)=>{I(t.message),O(o.ERROR)},[]);(0,T.useEffect)(()=>{const e=f.current;if(!C)return E.current&&(E.current.stop(),E.current=null),void(e&&e.srcObject&&(e.srcObject.getTracks().forEach(e=>e.stop()),e.srcObject=null));if(!e)return;let t=!1;return(n=void 0,l=void 0,c=void 0,d=function*(){var n;try{if(!e.srcObject){const n=yield navigator.mediaDevices.getUserMedia({video:{width:a,height:i}});if(t)return;e.srcObject=n,yield e.play()}const o=g.current;if(o){const t=e.videoWidth||a,n=e.videoHeight||i;o.width=t,o.height=n}if(E.current||t)return;const l={videoElement:e,overlayCanvasElement:null!==(n=g.current)&&void 0!==n?n:void 0,locale:h,debugMode:s,onStatusUpdate:A,onCaptureSuccess:M,onError:N,videoWidth:a,videoHeight:i};r&&(l.modelPath=r);const c=new S(l);E.current=c}catch(e){if(t)return;I(e.message),O(o.ERROR)}},new(c||(c=Promise))(function(e,t){function a(e){try{o(d.next(e))}catch(e){t(e)}}function i(e){try{o(d.throw(e))}catch(e){t(e)}}function o(t){var n;t.done?e(t.value):(n=t.value,n instanceof c?n:new c(function(e){e(n)})).then(a,i)}o((d=d.apply(n,l||[])).next())})).catch(()=>{}),()=>{t=!0,E.current&&(E.current.stop(),E.current=null),e&&e.srcObject&&(e.srcObject.getTracks().forEach(e=>e.stop()),e.srcObject=null)};var n,l,c,d},[C,h,a,i,s,r,A,M,N]);const P=!C&&Boolean(_),k=C,w=Object.assign({width:"100%",maxWidth:640,boxSizing:"border-box"},null==l?void 0:l.container),D=Object.assign({textAlign:"center",display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,padding:"10px 12px",marginBottom:10,borderRadius:10,fontWeight:600,marginTop:30,height:52,boxSizing:"border-box"},null==l?void 0:l.messageBanner),j=Object.assign(Object.assign({},D),{backgroundColor:"#fff7df",color:"#b67219"});k&&(p===o.SUCCESS?(j.backgroundColor="#e0ffdf",j.color="#26c026"):p===o.ERROR&&(j.backgroundColor="#ffdfdf",j.color="#c02626"));const U=Object.assign({width:"100%",maxWidth:512,height:384,margin:"0 auto",borderRadius:10,overflow:"hidden",position:"relative",backgroundColor:"#1a1a1a"},null==l?void 0:l.media),B={display:"block",width:"100%",padding:"8px 12px",borderRadius:4,border:"1px solid #d9d9d9",backgroundColor:"#ffffff",cursor:"pointer",fontSize:14},G=Object.assign(Object.assign(Object.assign({},B),{backgroundColor:"#1dbe32",color:"#ffffff",borderColor:"#1dbe32"}),null==l?void 0:l.primaryButton),H=Object.assign(Object.assign({},B),null==l?void 0:l.secondaryButton);return(0,y.jsxs)("div",{style:w,children:[(0,y.jsx)("div",{style:j,children:P?m.previewQuestion:v}),(0,y.jsx)("div",{style:U,children:k?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)("video",{ref:f,autoPlay:!0,playsInline:!0,muted:!0,style:{width:"100%",height:"100%",objectFit:"cover",transform:"scaleX(-1)"}}),(0,y.jsx)("canvas",{ref:g,style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none",transform:"scaleX(-1)"}})]}):P&&_?(0,y.jsx)("img",{src:_,alt:"Selfie preview",style:{display:"block",width:"100%",height:"100%",objectFit:"cover",transform:"scaleX(-1)",backgroundColor:"transparent",border:"none"}}):(0,y.jsx)("div",{"aria-hidden":!0})}),(0,y.jsx)("div",{style:{marginTop:10},children:P?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)("button",{type:"button",onClick:F,style:H,children:m.tryAgain}),(0,y.jsx)("div",{style:{height:8}}),(0,y.jsx)("button",{type:"button",onClick:L,disabled:!_,style:Object.assign(Object.assign({},G),{opacity:_?1:.6,cursor:_?"pointer":"not-allowed"}),children:m.savePhoto})]}):(0,y.jsx)("button",{type:"button",onClick:()=>{null==t||t(),e(null),R(!1)},style:H,children:m.cancel})})]})},v=S;module.exports=a})();
|
|
2
3
|
//# sourceMappingURL=face-validator-sdk.cjs.js.map
|