@mostajs/face 1.3.7 → 1.4.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/browser/face-api.LICENSE +21 -0
- package/browser/face-api.js +5009 -0
- package/browser/mosta-face.js +33 -0
- package/llms.txt +118 -0
- package/package.json +11 -8
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* @mostajs/face — helper navigateur autonome (window.MostaFace).
|
|
2
|
+
* À charger APRÈS browser/face-api.js (global `faceapi`). Sur le modèle de
|
|
3
|
+
* @mostajs/qrpanel/browser-scan, mais face-api reste un FICHIER SÉPARÉ vendu dans
|
|
4
|
+
* le module (browser/face-api.js), non concaténé. Aucune dépendance CDN ni build.
|
|
5
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later */
|
|
6
|
+
(function () {
|
|
7
|
+
if (typeof faceapi === 'undefined') { console.warn('[MostaFace] browser/face-api.js doit être chargé avant mosta-face.js'); return; }
|
|
8
|
+
var loaded = false;
|
|
9
|
+
async function loadModels(uri) {
|
|
10
|
+
if (loaded) return;
|
|
11
|
+
uri = uri || '/static/models/face-api';
|
|
12
|
+
await faceapi.nets.tinyFaceDetector.loadFromUri(uri);
|
|
13
|
+
await faceapi.nets.faceLandmark68Net.loadFromUri(uri);
|
|
14
|
+
await faceapi.nets.faceRecognitionNet.loadFromUri(uri);
|
|
15
|
+
loaded = true;
|
|
16
|
+
}
|
|
17
|
+
async function startCamera(video, opts) {
|
|
18
|
+
var stream = await navigator.mediaDevices.getUserMedia({ video: Object.assign({ facingMode: 'user', width: 480, height: 360 }, opts || {}) });
|
|
19
|
+
video.srcObject = stream; await video.play(); return stream;
|
|
20
|
+
}
|
|
21
|
+
async function detect(video) {
|
|
22
|
+
return faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceDescriptor();
|
|
23
|
+
}
|
|
24
|
+
async function captureDescriptor(video) { var d = await detect(video); return d ? Array.from(d.descriptor) : null; }
|
|
25
|
+
function snapshot(video, q) {
|
|
26
|
+
var c = document.createElement('canvas'); c.width = video.videoWidth || 480; c.height = video.videoHeight || 360;
|
|
27
|
+
c.getContext('2d').drawImage(video, 0, 0, c.width, c.height); return c.toDataURL('image/jpeg', q || 0.7);
|
|
28
|
+
}
|
|
29
|
+
window.MostaFace = {
|
|
30
|
+
loadModels: loadModels, startCamera: startCamera, detect: detect,
|
|
31
|
+
captureDescriptor: captureDescriptor, snapshot: snapshot, isLoaded: function () { return loaded; },
|
|
32
|
+
};
|
|
33
|
+
})();
|
package/llms.txt
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @mostajs/face — fiche LLM
|
|
2
|
+
> Reconnaissance faciale réutilisable : détection, extraction de descripteur 128-D, matching 1:N, hooks React et factories de routes API.
|
|
3
|
+
|
|
4
|
+
- Version: 1.3.7 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
|
|
5
|
+
- Chemin: mostajs/mosta-face · Statut audit: complet (dist/)
|
|
6
|
+
|
|
7
|
+
## RÔLE
|
|
8
|
+
Module de reconnaissance faciale de l'écosystème mostajs, bâti sur @vladmandic/face-api. Détecte les visages, extrait le descripteur facial 128 dimensions, compare des descripteurs (distance euclidienne) et effectue du matching 1:N contre une liste de candidats. Fournit des hooks React (accès caméra, détection en boucle), un composant FaceDetector, et des factories de routes API Next.js (recognize, detect). Module 100% autonome : aucune dépendance sur `@mostajs/orm` ni un autre package @mostajs/*. La détection s'exécute côté client (browser) ; le serveur ne fait que le gating de permission et le matching.
|
|
9
|
+
|
|
10
|
+
## INSTALLATION
|
|
11
|
+
npm i @mostajs/face
|
|
12
|
+
# Puis copier les modèles face-api.js dans public/models/face-api/ :
|
|
13
|
+
# tiny_face_detector, face_landmark_68, face_recognition (fichiers .bin + .json
|
|
14
|
+
# depuis node_modules/@vladmandic/face-api/model/)
|
|
15
|
+
|
|
16
|
+
## EXPORTS
|
|
17
|
+
Point d'entrée racine (`.`) :
|
|
18
|
+
- Core face-api: loadModels, isLoaded, detectFace, detectAllFaces, extractDescriptor
|
|
19
|
+
- Matching: compareFaces, findMatch, findAllMatches
|
|
20
|
+
- Utils: descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection
|
|
21
|
+
- Hooks: useCamera, useFaceDetection
|
|
22
|
+
- API factories: createRecognizeHandler, createDetectHandler
|
|
23
|
+
- Composant: FaceDetector (default)
|
|
24
|
+
- Menu: faceMenuContribution
|
|
25
|
+
- Constante: DEFAULT_FACE_SETTINGS
|
|
26
|
+
- Types: MostaFaceConfig, FaceDetectionResult, FaceMatchResult, FaceDescriptor, FaceSettings, FaceDetectorProps, FaceCandidate, RecognizeHandlerConfig, DetectHandlerConfig
|
|
27
|
+
|
|
28
|
+
## EXPORTS PAR SOUS-CHEMIN
|
|
29
|
+
- `./lib/face-api` — loadModels, isLoaded, detectFace, detectAllFaces, extractDescriptor
|
|
30
|
+
- `./lib/face-matcher` — compareFaces, findMatch, findAllMatches
|
|
31
|
+
- `./lib/face-utils` — descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection
|
|
32
|
+
- `./lib/menu` — faceMenuContribution
|
|
33
|
+
- `./hooks/useCamera` — useCamera
|
|
34
|
+
- `./hooks/useFaceDetection` — useFaceDetection
|
|
35
|
+
- `./components/FaceDetector` — composant React (default)
|
|
36
|
+
- `./api/recognize.route` — createRecognizeHandler ; types FaceCandidate, RecognizeHandlerConfig
|
|
37
|
+
- `./api/detect.route` — createDetectHandler ; type DetectHandlerConfig
|
|
38
|
+
- `./types` — tous les types + DEFAULT_FACE_SETTINGS
|
|
39
|
+
- `./register` — register(registry)
|
|
40
|
+
- **ASSETS NAVIGATEUR AUTONOMES** (sur le modèle de `@mostajs/qrpanel/browser-scan`, **sans CDN ni build**) — face-api est **vendu comme fichier source du module** (PAS bundlé/concaténé) :
|
|
41
|
+
- `./browser/face-api.js` — source @vladmandic/face-api (global `faceapi`), copié dans le module (LICENSE: `browser/face-api.LICENSE`).
|
|
42
|
+
- `./browser/mosta-face.js` — helper `window.MostaFace`, fichier SÉPARÉ, à charger APRÈS face-api.js.
|
|
43
|
+
- Chargement app : `<script src="/static/face-api.js"></script><script src="/static/mosta-face.js"></script>` + servir les modèles sous `/static/models/face-api/`.
|
|
44
|
+
- `window.MostaFace`: `loadModels(uri?)`, `startCamera(video, opts?)`, `detect(video)`, `captureDescriptor(video) -> number[]|null`, `snapshot(video, q?) -> dataURL`, `isLoaded()`.
|
|
45
|
+
|
|
46
|
+
## API — SIGNATURES
|
|
47
|
+
loadModels(config?: MostaFaceConfig): Promise<void> // charge face-api.js + 3 modèles
|
|
48
|
+
isLoaded(): boolean
|
|
49
|
+
detectFace(input: HTMLVideoElement|HTMLCanvasElement, config?): Promise<WithFaceLandmarks|null>
|
|
50
|
+
detectAllFaces(input, config?): Promise<WithFaceLandmarks[]>
|
|
51
|
+
extractDescriptor(input, config?): Promise<Float32Array|null> // descripteur 128-D
|
|
52
|
+
compareFaces(d1: FaceDescriptor, d2: FaceDescriptor): number // distance euclidienne
|
|
53
|
+
findMatch<T extends {faceDescriptor:number[]}>(descriptor, candidates: T[], threshold?): FaceMatchResult<T>|null
|
|
54
|
+
findAllMatches<T extends {faceDescriptor:number[]}>(descriptor, candidates, threshold?): FaceMatchResult<T>[] // trié par distance
|
|
55
|
+
descriptorToArray(d: Float32Array): number[] · arrayToDescriptor(arr: number[]): Float32Array
|
|
56
|
+
isValidDescriptor(data: unknown): boolean // vrai si 128 éléments
|
|
57
|
+
drawDetection(canvas, detection|null, videoWidth, videoHeight): void
|
|
58
|
+
useCamera(options?: { facingMode?, width?, height?, autoStart? }): { videoRef, stream, status, error, start, stop, switchCamera }
|
|
59
|
+
useFaceDetection(videoRef, options?: { interval?, autoStart?, extractDescriptor?, config? }): { detection, descriptor, status, modelsLoaded, start, stop }
|
|
60
|
+
createRecognizeHandler(config: RecognizeHandlerConfig): { POST } // POST /api/face/recognize
|
|
61
|
+
createDetectHandler(config?: DetectHandlerConfig): { POST } // POST /api/face/detect (placeholder, gating)
|
|
62
|
+
|
|
63
|
+
## TYPES CLÉS
|
|
64
|
+
MostaFaceConfig { modelsPath?('/models/face-api'), scoreThreshold?(0.5), inputSize?(320), matchThreshold?(0.6) }
|
|
65
|
+
FaceDescriptor = Float32Array | number[]
|
|
66
|
+
FaceDetectionResult { detection:{ score, box:{x,y,width,height} }, landmarks? }
|
|
67
|
+
FaceMatchResult<T> { match: T, distance: number }
|
|
68
|
+
FaceSettings { faceRecognitionEnabled, faceRecognitionThreshold(0.6), faceRequireForCapture, faceAutoVerify }
|
|
69
|
+
DEFAULT_FACE_SETTINGS — valeurs par défaut de FaceSettings
|
|
70
|
+
FaceCandidate { id, faceDescriptor: number[], [key: string]: unknown }
|
|
71
|
+
RecognizeHandlerConfig { getCandidates()=>Promise<FaceCandidate[]>, checkAuth?(req)=>Promise<Response|null>, getThreshold?()=>Promise<number>, isEnabled?()=>Promise<boolean>, publicFields?:string[] }
|
|
72
|
+
DetectHandlerConfig { checkAuth?(req)=>Promise<Response|null>, isEnabled?()=>Promise<boolean> }
|
|
73
|
+
CameraStatus = 'idle'|'requesting'|'active'|'denied'|'error'
|
|
74
|
+
DetectionStatus = 'loading'|'detecting'|'detected'|'noFace'
|
|
75
|
+
|
|
76
|
+
## PATTERN
|
|
77
|
+
```ts
|
|
78
|
+
// Client — détection + extraction
|
|
79
|
+
import { loadModels, extractDescriptor, descriptorToArray } from '@mostajs/face'
|
|
80
|
+
await loadModels({ modelsPath: '/models/face-api' })
|
|
81
|
+
const d = await extractDescriptor(videoEl) // Float32Array 128-D
|
|
82
|
+
const stored = descriptorToArray(d) // number[] pour DB/JSON
|
|
83
|
+
|
|
84
|
+
// Client — hooks React
|
|
85
|
+
import { useCamera, useFaceDetection } from '@mostajs/face'
|
|
86
|
+
const { videoRef, start } = useCamera({ autoStart: true })
|
|
87
|
+
const { descriptor, status } = useFaceDetection(videoRef, { extractDescriptor: true })
|
|
88
|
+
|
|
89
|
+
// Matching 1:N
|
|
90
|
+
import { findMatch } from '@mostajs/face'
|
|
91
|
+
const result = findMatch(descriptor, candidates, 0.6) // null si rien sous le seuil
|
|
92
|
+
|
|
93
|
+
// Serveur — route Next.js
|
|
94
|
+
import { createRecognizeHandler } from '@mostajs/face/api/recognize.route'
|
|
95
|
+
export const { POST } = createRecognizeHandler({
|
|
96
|
+
getCandidates: async () => clientRepo.findAll({ status:'active' }, { select:['firstName','faceDescriptor'] }),
|
|
97
|
+
getThreshold: async () => 0.6,
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## DÉPEND DE
|
|
102
|
+
- @vladmandic/face-api (dependency) — moteur de détection/reconnaissance.
|
|
103
|
+
- Aucun module @mostajs/* requis (module 100% autonome).
|
|
104
|
+
- Peers : react >=18 ; @mostajs/menu (optionnel), @mostajs/socle (>=2.0.0, pour register).
|
|
105
|
+
|
|
106
|
+
## PIÈGES
|
|
107
|
+
- Les modèles face-api.js NE sont PAS embarqués : il faut copier manuellement les 3 modèles (tiny_face_detector, face_landmark_68, face_recognition) dans `public/models/face-api/` (ou modelsPath custom) sinon loadModels échoue.
|
|
108
|
+
- loadModels doit être appelé (et terminé) avant detectFace/extractDescriptor — vérifier isLoaded() ou attendre la promesse.
|
|
109
|
+
- La détection s'exécute CÔTÉ CLIENT (browser, via HTMLVideoElement/HTMLCanvasElement) ; createDetectHandler est un placeholder de gating, il ne détecte pas de visage côté serveur.
|
|
110
|
+
- Le matching utilise la distance euclidienne : seuil PLUS BAS = PLUS strict (défaut 0.6). findMatch renvoie null si aucun candidat n'est sous le seuil.
|
|
111
|
+
- Les descripteurs se stockent en `number[]` (descriptorToArray) pour JSON/DB et se reconvertissent en Float32Array (arrayToDescriptor) avant comparaison.
|
|
112
|
+
- Un descripteur valide a exactement 128 éléments (isValidDescriptor).
|
|
113
|
+
- createRecognizeHandler n'authentifie pas par défaut : fournir une callback `checkAuth` retournant une Response pour refuser, ou null pour laisser passer ; par défaut publicFields exclut faceDescriptor de la réponse.
|
|
114
|
+
|
|
115
|
+
## RÉFÉRENCES
|
|
116
|
+
- README.md (racine du module) — quick start, intégration complète, API reference
|
|
117
|
+
- face.wire.json, wire.json — déclaration de câblage
|
|
118
|
+
- LICENSE (AGPL-3.0-or-later)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/face",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Reusable face recognition module — detection, descriptor extraction, 1:N matching",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -69,14 +69,18 @@
|
|
|
69
69
|
"types": "./dist/register.d.ts",
|
|
70
70
|
"import": "./dist/register.js",
|
|
71
71
|
"default": "./dist/register.js"
|
|
72
|
-
}
|
|
72
|
+
},
|
|
73
|
+
"./browser/face-api.js": "./browser/face-api.js",
|
|
74
|
+
"./browser/mosta-face.js": "./browser/mosta-face.js"
|
|
73
75
|
},
|
|
74
76
|
"files": [
|
|
75
77
|
"dist",
|
|
76
78
|
"wire.json",
|
|
77
79
|
"face.wire.json",
|
|
78
80
|
"LICENSE",
|
|
79
|
-
"README.md"
|
|
81
|
+
"README.md",
|
|
82
|
+
"llms.txt",
|
|
83
|
+
"browser"
|
|
80
84
|
],
|
|
81
85
|
"keywords": [
|
|
82
86
|
"face-recognition",
|
|
@@ -94,10 +98,6 @@
|
|
|
94
98
|
"engines": {
|
|
95
99
|
"node": ">=18.0.0"
|
|
96
100
|
},
|
|
97
|
-
"scripts": {
|
|
98
|
-
"build": "tsc",
|
|
99
|
-
"prepublishOnly": "npm run build"
|
|
100
|
-
},
|
|
101
101
|
"dependencies": {
|
|
102
102
|
"@vladmandic/face-api": "^1.7.0"
|
|
103
103
|
},
|
|
@@ -117,5 +117,8 @@
|
|
|
117
117
|
"@mostajs/menu": {
|
|
118
118
|
"optional": true
|
|
119
119
|
}
|
|
120
|
+
},
|
|
121
|
+
"scripts": {
|
|
122
|
+
"build": "tsc"
|
|
120
123
|
}
|
|
121
|
-
}
|
|
124
|
+
}
|