@utamuratov/ngx-face-id 0.5.0 → 0.6.1

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.
@@ -4,7 +4,7 @@ import { isPlatformBrowser } from '@angular/common';
4
4
  import * as faceapi from 'face-api.js';
5
5
 
6
6
  class LivenessService {
7
- step = signal('BLINK', ...(ngDevMode ? [{ debugName: "step" }] : []));
7
+ step = signal('START', ...(ngDevMode ? [{ debugName: "step" }] : []));
8
8
  faceInsideStatus = signal('OUTSIDE_OVAL', ...(ngDevMode ? [{ debugName: "faceInsideStatus" }] : []));
9
9
  currentStep = computed(() => this.step(), ...(ngDevMode ? [{ debugName: "currentStep" }] : []));
10
10
  currentFaceInsideStatus = computed(() => this.faceInsideStatus(), ...(ngDevMode ? [{ debugName: "currentFaceInsideStatus" }] : []));
@@ -22,7 +22,7 @@ class LivenessService {
22
22
  }
23
23
  reset() {
24
24
  this.faceInsideStatus.set('OUTSIDE_OVAL');
25
- this.step.set('BLINK');
25
+ this.step.set('START');
26
26
  this.blinkCount = 0;
27
27
  this.prevBlinkAvg = 0;
28
28
  this.prevNoseX = undefined;
@@ -101,33 +101,41 @@ class LivenessService {
101
101
  }
102
102
  // ---------- MAIN CHECK ----------
103
103
  async process(video) {
104
+ const step = this.step();
104
105
  const result = await faceapi
105
106
  .detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())
106
107
  .withFaceLandmarks();
107
108
  if (!result) {
108
- this.faceInsideStatus.set('NO_FACE');
109
+ if (step === 'START') {
110
+ this.faceInsideStatus.set('NO_FACE');
111
+ }
109
112
  return false;
110
113
  }
111
- // 👇 OVAL CHECK
112
- const insideOval = this.isFaceInsideOval(result.detection.box, video);
113
- if (!insideOval) {
114
- if (this.step() === 'HOLD') {
115
- this.holdStart = Date.now();
114
+ if (step === 'START') {
115
+ // 👇 OVAL CHECK
116
+ const insideOval = this.isFaceInsideOval(result.detection.box, video);
117
+ if (!insideOval) {
118
+ if (this.step() === 'HOLD') {
119
+ this.holdStart = Date.now();
120
+ }
121
+ return false;
116
122
  }
117
- return false;
118
123
  }
119
124
  const lm = result.landmarks;
120
- if (this.step() === 'BLINK' && this.isBlink(lm)) {
125
+ if (step === 'START') {
126
+ this.step.set('BLINK');
127
+ }
128
+ else if (step === 'BLINK' && this.isBlink(lm)) {
121
129
  this.step.set('MOUTH');
122
130
  }
123
- else if (this.step() === 'MOUTH' && this.isMouthOpen(lm)) {
131
+ else if (step === 'MOUTH' && this.isMouthOpen(lm)) {
124
132
  this.step.set('HEAD');
125
133
  }
126
- else if (this.step() === 'HEAD' && this.isHeadMoved(lm)) {
134
+ else if (step === 'HEAD' && this.isHeadMoved(lm)) {
127
135
  this.step.set('HOLD');
128
136
  this.holdStart = Date.now();
129
137
  }
130
- else if (this.step() === 'HOLD') {
138
+ else if (step === 'HOLD') {
131
139
  if (!this.holdStart)
132
140
  this.holdStart = Date.now();
133
141
  if (Date.now() - this.holdStart > 3000) {
@@ -149,6 +157,8 @@ class NgxFaceLiveness {
149
157
  liveness;
150
158
  platformId = inject(PLATFORM_ID);
151
159
  isBrowser = isPlatformBrowser(this.platformId);
160
+ loadingResourses = signal(true, ...(ngDevMode ? [{ debugName: "loadingResourses" }] : []));
161
+ loadingMessage = signal('Kamera yuklanmoqda...', ...(ngDevMode ? [{ debugName: "loadingMessage" }] : []));
152
162
  videoRef;
153
163
  intervalId;
154
164
  capturedImage = output();
@@ -169,13 +179,13 @@ class NgxFaceLiveness {
169
179
  stepText = computed(() => {
170
180
  const currentFaceStatus = this.currentFaceInsideStatus();
171
181
  if (currentFaceStatus === 'NO_FACE') {
172
- return '❌ Yuz aniqlanmadi, iltimos, kameraga qarang';
182
+ return '❌ Yuz aniqlanmadi, kameraga qarang';
173
183
  }
174
184
  if (currentFaceStatus === 'OUTSIDE_OVAL') {
175
- return '📐 Iltimos, yuzingizni oval ichiga joylashtiring';
185
+ return '📐 Yuzingizni oval ichiga joylashtiring';
176
186
  }
177
187
  if (currentFaceStatus === 'COME_CLOSE') {
178
- return '🔍 Iltimos, kameraga yaqinroq turing';
188
+ return '🔍 Kameraga yaqinroq turing';
179
189
  }
180
190
  if (currentFaceStatus === 'VERY_CLOSE') {
181
191
  return '📷 Juda yaqin, kamerani orqaroq oling';
@@ -210,6 +220,7 @@ class NgxFaceLiveness {
210
220
  const devices = await navigator.mediaDevices.enumerateDevices();
211
221
  const videoDevices = devices.filter((d) => d.kind === 'videoinput');
212
222
  if (!videoDevices.length) {
223
+ this.loadingMessage.set('Kamera qurilmasi topilmadi!');
213
224
  throw new Error('No video devices found');
214
225
  }
215
226
  // USB yoki default camera tanlash
@@ -219,6 +230,7 @@ class NgxFaceLiveness {
219
230
  }, // birinchi topilgan kamera
220
231
  });
221
232
  this.videoRef.nativeElement.srcObject = stream;
233
+ this.loadingResourses.set(false);
222
234
  }
223
235
  catch (err) {
224
236
  console.error('Cannot access camera:', err);
@@ -270,7 +282,7 @@ class NgxFaceLiveness {
270
282
  }
271
283
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NgxFaceLiveness, deps: [{ token: LivenessService }], target: i0.ɵɵFactoryTarget.Component });
272
284
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: NgxFaceLiveness, isStandalone: true, selector: "ngx-face-liveness", outputs: { capturedImage: "capturedImage" }, viewQueries: [{ propertyName: "videoRef", first: true, predicate: ["video"], descendants: true }], ngImport: i0, template: `
273
- <div class="camera-wrapper">
285
+ <div class="camera-wrapper" [hidden]="loadingResourses()">
274
286
  @if (capturesImageSrc()) {
275
287
  <img [src]="capturesImageSrc()" alt="Captured Image" class="captured-image" />
276
288
  }
@@ -282,12 +294,17 @@ class NgxFaceLiveness {
282
294
  <p class="hint">{{ stepText() }}</p>
283
295
  </div>
284
296
  </div>
285
- `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;justify-content:center;height:100vh;padding:1rem}.camera-wrapper{width:100%;max-width:640px;min-height:480px;overflow:hidden;position:relative;border-radius:12px}@media(max-width:400px){.camera-wrapper{min-height:240px}}.camera-video{width:100%;height:auto;max-height:100%;object-fit:contain;transform:scaleX(-1)}.overlay{position:absolute;z-index:3;inset:0;pointer-events:none;display:flex;flex-direction:column;align-items:center;justify-content:center}.oval-mask{height:90%;aspect-ratio:5/6;border-radius:50%;border:4px dashed #65a0f8;background:transparent;box-shadow:0 0 0 9999px #0009}.hint{margin-top:12px;color:#fff;font-size:14px;text-align:center;position:absolute;top:5px;background-color:#0000007a;padding:2px 4px;border-radius:5px}.captured-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;border-radius:12px;z-index:2;transform:scaleX(-1)}\n"] });
297
+ @if (loadingResourses()) {
298
+ <div class="loading-text-container">
299
+ <p class="loading-text">{{ loadingMessage() }}</p>
300
+ </div>
301
+ }
302
+ `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;justify-content:center;height:100%;padding:1rem}.camera-wrapper{width:100%;max-width:640px;overflow:hidden;position:relative;border-radius:12px;border-radius:1rem;background:#65a0f8}.camera-video{width:100%;height:auto;max-height:100%;object-fit:contain;transform:scaleX(-1)}.overlay{position:absolute;z-index:3;inset:0;pointer-events:none;display:flex;flex-direction:column;align-items:center;justify-content:center}.oval-mask{height:90%;aspect-ratio:5/6;border-radius:50%;border:4px dashed #65a0f8;background:transparent;box-shadow:0 0 0 9999px #0009}.hint{color:#fff;font-size:18px;font-weight:500;line-height:1.3;width:250px;text-align:center;position:absolute;top:5px;background-color:#0000007a;padding:2px 4px;border-radius:5px}.captured-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;border-radius:12px;z-index:2;transform:scaleX(-1)}.loading-text-container{min-height:400px;display:flex;align-items:center;justify-content:center}.loading-text{font-size:18px;font-weight:500}\n"] });
286
303
  }
287
304
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NgxFaceLiveness, decorators: [{
288
305
  type: Component,
289
306
  args: [{ selector: 'ngx-face-liveness', template: `
290
- <div class="camera-wrapper">
307
+ <div class="camera-wrapper" [hidden]="loadingResourses()">
291
308
  @if (capturesImageSrc()) {
292
309
  <img [src]="capturesImageSrc()" alt="Captured Image" class="captured-image" />
293
310
  }
@@ -299,7 +316,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
299
316
  <p class="hint">{{ stepText() }}</p>
300
317
  </div>
301
318
  </div>
302
- `, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;justify-content:center;height:100vh;padding:1rem}.camera-wrapper{width:100%;max-width:640px;min-height:480px;overflow:hidden;position:relative;border-radius:12px}@media(max-width:400px){.camera-wrapper{min-height:240px}}.camera-video{width:100%;height:auto;max-height:100%;object-fit:contain;transform:scaleX(-1)}.overlay{position:absolute;z-index:3;inset:0;pointer-events:none;display:flex;flex-direction:column;align-items:center;justify-content:center}.oval-mask{height:90%;aspect-ratio:5/6;border-radius:50%;border:4px dashed #65a0f8;background:transparent;box-shadow:0 0 0 9999px #0009}.hint{margin-top:12px;color:#fff;font-size:14px;text-align:center;position:absolute;top:5px;background-color:#0000007a;padding:2px 4px;border-radius:5px}.captured-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;border-radius:12px;z-index:2;transform:scaleX(-1)}\n"] }]
319
+ @if (loadingResourses()) {
320
+ <div class="loading-text-container">
321
+ <p class="loading-text">{{ loadingMessage() }}</p>
322
+ </div>
323
+ }
324
+ `, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;justify-content:center;height:100%;padding:1rem}.camera-wrapper{width:100%;max-width:640px;overflow:hidden;position:relative;border-radius:12px;border-radius:1rem;background:#65a0f8}.camera-video{width:100%;height:auto;max-height:100%;object-fit:contain;transform:scaleX(-1)}.overlay{position:absolute;z-index:3;inset:0;pointer-events:none;display:flex;flex-direction:column;align-items:center;justify-content:center}.oval-mask{height:90%;aspect-ratio:5/6;border-radius:50%;border:4px dashed #65a0f8;background:transparent;box-shadow:0 0 0 9999px #0009}.hint{color:#fff;font-size:18px;font-weight:500;line-height:1.3;width:250px;text-align:center;position:absolute;top:5px;background-color:#0000007a;padding:2px 4px;border-radius:5px}.captured-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;border-radius:12px;z-index:2;transform:scaleX(-1)}.loading-text-container{min-height:400px;display:flex;align-items:center;justify-content:center}.loading-text{font-size:18px;font-weight:500}\n"] }]
303
325
  }], ctorParameters: () => [{ type: LivenessService }], propDecorators: { videoRef: [{
304
326
  type: ViewChild,
305
327
  args: ['video']
@@ -1 +1 @@
1
- {"version":3,"file":"utamuratov-ngx-face-id.mjs","sources":["../../../projects/ngx-face-id/src/lib/face-liveness/liveness.service.ts","../../../projects/ngx-face-id/src/lib/face-liveness/face-liveness.ts","../../../projects/ngx-face-id/src/public-api.ts","../../../projects/ngx-face-id/src/utamuratov-ngx-face-id.ts"],"sourcesContent":["import { computed, Injectable, signal } from '@angular/core';\nimport * as faceapi from 'face-api.js';\n\ntype Step = 'BLINK' | 'MOUTH' | 'HEAD' | 'HOLD' | 'DONE';\n\ntype FaceInside = 'NO_FACE' | 'OUTSIDE_OVAL' | 'COME_CLOSE' | 'VERY_CLOSE' | 'VALID';\n\n@Injectable({ providedIn: 'root' })\nexport class LivenessService {\n private step = signal<Step>('BLINK');\n private faceInsideStatus = signal<FaceInside>('OUTSIDE_OVAL');\n currentStep = computed(() => this.step());\n currentFaceInsideStatus = computed(() => this.faceInsideStatus());\n\n private prevBlinkAvg = 0;\n private blinkCount = 0;\n private prevNoseX?: number;\n private holdStart?: number;\n\n async loadModels() {\n const MODEL_URL = '/models';\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),\n faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),\n faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL),\n ]);\n }\n\n reset() {\n this.faceInsideStatus.set('OUTSIDE_OVAL');\n this.step.set('BLINK');\n this.blinkCount = 0;\n this.prevBlinkAvg = 0;\n this.prevNoseX = undefined;\n this.holdStart = undefined;\n }\n\n // ---------- HELPERS ----------\n\n private isFaceInsideOval(box: faceapi.Box, video: HTMLVideoElement): boolean {\n const vw = video.videoWidth;\n const vh = video.videoHeight;\n\n // Oval markazi\n const cx = vw / 2;\n const cy = vh / 2;\n\n // Oval radius\n const ry = vh * 0.45; // height = 90%\n const rx = ry * (5 / 6); // aspect-ratio = 5/6\n\n // Mirror x for CSS scaleX(-1)\n const mirrorX = (x: number) => vw - x;\n\n // Face edge points\n const points = [\n { x: mirrorX(box.x), y: box.y + box.height / 2 }, // left\n { x: mirrorX(box.x + box.width), y: box.y + box.height / 2 }, // right\n { x: mirrorX(box.x + box.width / 2), y: box.y }, // top\n { x: mirrorX(box.x + box.width / 2), y: box.y + box.height }, // bottom\n ];\n\n const inside = points.every(\n (p) => Math.pow((p.x - cx) / rx, 2) + Math.pow((p.y - cy) / ry, 2) <= 1,\n );\n\n if (!inside) {\n this.faceInsideStatus.set('OUTSIDE_OVAL');\n return false;\n }\n\n // Fill ratio\n const faceArea = box.width * box.height;\n const ovalArea = Math.PI * rx * ry;\n const fillRatio = faceArea / ovalArea;\n\n if (fillRatio < 0.42) {\n this.faceInsideStatus.set('COME_CLOSE');\n return false;\n }\n\n if (fillRatio > 0.6) {\n this.faceInsideStatus.set('VERY_CLOSE');\n return false;\n }\n\n this.faceInsideStatus.set('VALID');\n return true;\n }\n\n private dist(a: any, b: any) {\n return Math.hypot(a.x - b.x, a.y - b.y);\n }\n\n private eyeEAR(eye: faceapi.Point[]) {\n return (\n (this.dist(eye[1], eye[5]) + this.dist(eye[2], eye[4])) / (2 * this.dist(eye[0], eye[3]))\n );\n }\n\n private isBlink(lm: faceapi.FaceLandmarks68) {\n const left = this.eyeEAR(lm.getLeftEye());\n const right = this.eyeEAR(lm.getRightEye());\n const avg = (left + right) / 2;\n\n if (this.prevBlinkAvg === 0 || Math.abs(avg - this.prevBlinkAvg) < 0.01) {\n this.prevBlinkAvg = avg;\n return false;\n }\n\n this.prevBlinkAvg = avg;\n this.blinkCount++;\n return this.blinkCount > 2;\n }\n\n private isMouthOpen(lm: faceapi.FaceLandmarks68) {\n const mouth = lm.getMouth();\n return this.dist(mouth[13], mouth[19]) / this.dist(mouth[0], mouth[6]) > 0.4;\n }\n\n private isHeadMoved(lm: faceapi.FaceLandmarks68) {\n const nose = lm.getNose()[3];\n if (!this.prevNoseX) {\n this.prevNoseX = nose.x;\n return false;\n }\n const moved = Math.abs(nose.x - this.prevNoseX) > 30;\n\n this.prevNoseX = nose.x;\n return moved;\n }\n\n // ---------- MAIN CHECK ----------\n\n async process(video: HTMLVideoElement): Promise<boolean> {\n const result = await faceapi\n .detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())\n .withFaceLandmarks();\n\n if (!result) {\n this.faceInsideStatus.set('NO_FACE');\n return false;\n }\n\n // 👇 OVAL CHECK\n const insideOval = this.isFaceInsideOval(result.detection.box, video);\n\n if (!insideOval) {\n if (this.step() === 'HOLD') {\n this.holdStart = Date.now();\n }\n return false;\n }\n\n const lm = result.landmarks;\n\n if (this.step() === 'BLINK' && this.isBlink(lm)) {\n this.step.set('MOUTH');\n } else if (this.step() === 'MOUTH' && this.isMouthOpen(lm)) {\n this.step.set('HEAD');\n } else if (this.step() === 'HEAD' && this.isHeadMoved(lm)) {\n this.step.set('HOLD');\n this.holdStart = Date.now();\n } else if (this.step() === 'HOLD') {\n if (!this.holdStart) this.holdStart = Date.now();\n if (Date.now() - this.holdStart > 3000) {\n this.step.set('DONE');\n return true; // 📸 CAPTURE SIGNAL\n }\n }\n\n return false;\n }\n}\n","import {\n Component,\n ElementRef,\n ViewChild,\n OnInit,\n computed,\n output,\n inject,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { LivenessService } from './liveness.service';\nimport { isPlatformBrowser } from '@angular/common';\n\n@Component({\n selector: 'ngx-face-liveness',\n template: `\n <div class=\"camera-wrapper\">\n @if (capturesImageSrc()) {\n <img [src]=\"capturesImageSrc()\" alt=\"Captured Image\" class=\"captured-image\" />\n }\n <video #video autoplay muted playsinline class=\"camera-video\"></video>\n\n <!-- Overlay -->\n <div class=\"overlay\">\n <div class=\"oval-mask\" [style.borderColor]=\"isNotValidOval() ? '#fb2c36' : '#65a0f8'\"></div>\n <p class=\"hint\">{{ stepText() }}</p>\n </div>\n </div>\n `,\n styleUrls: ['./face-liveness.scss'],\n})\nexport class NgxFaceLiveness implements OnInit {\n private platformId = inject(PLATFORM_ID);\n private isBrowser = isPlatformBrowser(this.platformId);\n\n @ViewChild('video') videoRef!: ElementRef<HTMLVideoElement>;\n intervalId: any;\n\n capturedImage = output<Blob>();\n capturesImageSrc = signal<string | null>(null);\n\n constructor(private liveness: LivenessService) {}\n\n async ngOnInit() {\n if (!this.isBrowser) return;\n\n await this.liveness.loadModels();\n await this.start();\n }\n\n currentStep = computed(() => this.liveness.currentStep());\n currentFaceInsideStatus = computed(() => this.liveness.currentFaceInsideStatus());\n isNotValidOval = computed(\n () =>\n this.currentFaceInsideStatus() === 'OUTSIDE_OVAL' ||\n this.currentFaceInsideStatus() === 'NO_FACE',\n );\n stepText = computed(() => {\n const currentFaceStatus = this.currentFaceInsideStatus();\n\n if (currentFaceStatus === 'NO_FACE') {\n return '❌ Yuz aniqlanmadi, iltimos, kameraga qarang';\n }\n if (currentFaceStatus === 'OUTSIDE_OVAL') {\n return '📐 Iltimos, yuzingizni oval ichiga joylashtiring';\n }\n if (currentFaceStatus === 'COME_CLOSE') {\n return '🔍 Iltimos, kameraga yaqinroq turing';\n }\n if (currentFaceStatus === 'VERY_CLOSE') {\n return '📷 Juda yaqin, kamerani orqaroq oling';\n }\n\n switch (this.currentStep()) {\n case 'BLINK':\n return '👁 Ko‘zingizni yumib oching(2 marta)';\n case 'MOUTH':\n return '🙂 Og‘zingizni ochib yuming';\n case 'HEAD':\n return '↔️ Boshni chapga yoki o‘ngga burang';\n case 'HOLD':\n return '✋ Barqaror turing (2 soniya)';\n default:\n return '✅ Tayyor!';\n }\n });\n\n async start() {\n this.liveness.reset();\n await this.openCamera();\n\n this.intervalId = setInterval(async () => {\n const done = await this.liveness.process(this.videoRef.nativeElement);\n\n if (done) {\n clearInterval(this.intervalId);\n const blob = await this.capture();\n this.sendToBackend(blob);\n }\n }, 300);\n }\n\n async openCamera() {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const videoDevices = devices.filter((d) => d.kind === 'videoinput');\n\n if (!videoDevices.length) {\n throw new Error('No video devices found');\n }\n\n // USB yoki default camera tanlash\n const stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: videoDevices[0].deviceId,\n }, // birinchi topilgan kamera\n });\n\n this.videoRef.nativeElement.srcObject = stream;\n } catch (err) {\n console.error('Cannot access camera:', err);\n }\n\n // const stream = await navigator.mediaDevices.getUserMedia({\n // video: { facingMode: 'user', aspectRatio: 3 / 4, width: { min: 480 }, height: { min: 640 } },\n // });\n // this.videoRef.nativeElement.srcObject = stream;\n }\n\n async capture(): Promise<Blob> {\n const video = this.videoRef.nativeElement;\n const canvas = document.createElement('canvas');\n\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n canvas.getContext('2d')!.drawImage(video, 0, 0);\n\n return new Promise((res) => canvas.toBlob((b) => res(b!), 'image/jpeg', 0.9));\n }\n\n sendToBackend(blob: Blob) {\n this.capturedImage.emit(blob);\n this.capturesImageSrc.set(URL.createObjectURL(blob));\n console.log(blob);\n\n // const fd = new FormData();\n // fd.append('file', blob, 'face.jpg');\n // this.http.post('/api/face/verify', fd).subscribe();\n }\n\n ngOnDestroy(): void {\n if (!this.isBrowser) return;\n\n this.cleanup();\n }\n\n private cleanup() {\n // 1️⃣ Interval\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n\n // 2️⃣ Kamera\n const video = this.videoRef?.nativeElement;\n if (video) {\n video.pause();\n\n const stream = video.srcObject as MediaStream | null;\n if (stream) {\n stream.getTracks().forEach((track) => track.stop());\n }\n\n video.srcObject = null;\n }\n\n // 3️⃣ Service state\n this.liveness.reset();\n }\n}\n","/*\r\n * Public API Surface of ngx-face-id\r\n */\r\n\r\nexport * from './lib/face-liveness/face-liveness';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.LivenessService"],"mappings":"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"}
1
+ {"version":3,"file":"utamuratov-ngx-face-id.mjs","sources":["../../../projects/ngx-face-id/src/lib/face-liveness/liveness.service.ts","../../../projects/ngx-face-id/src/lib/face-liveness/face-liveness.ts","../../../projects/ngx-face-id/src/public-api.ts","../../../projects/ngx-face-id/src/utamuratov-ngx-face-id.ts"],"sourcesContent":["import { computed, Injectable, signal } from '@angular/core';\nimport * as faceapi from 'face-api.js';\n\ntype Step = 'START' | 'BLINK' | 'MOUTH' | 'HEAD' | 'HOLD' | 'DONE';\n\ntype FaceInside = 'NO_FACE' | 'OUTSIDE_OVAL' | 'COME_CLOSE' | 'VERY_CLOSE' | 'VALID';\n\n@Injectable({ providedIn: 'root' })\nexport class LivenessService {\n private step = signal<Step>('START');\n private faceInsideStatus = signal<FaceInside>('OUTSIDE_OVAL');\n currentStep = computed(() => this.step());\n currentFaceInsideStatus = computed(() => this.faceInsideStatus());\n\n private prevBlinkAvg = 0;\n private blinkCount = 0;\n private prevNoseX?: number;\n private holdStart?: number;\n\n async loadModels() {\n const MODEL_URL = '/models';\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),\n faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),\n faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL),\n ]);\n }\n\n reset() {\n this.faceInsideStatus.set('OUTSIDE_OVAL');\n this.step.set('START');\n this.blinkCount = 0;\n this.prevBlinkAvg = 0;\n this.prevNoseX = undefined;\n this.holdStart = undefined;\n }\n\n // ---------- HELPERS ----------\n\n private isFaceInsideOval(box: faceapi.Box, video: HTMLVideoElement): boolean {\n const vw = video.videoWidth;\n const vh = video.videoHeight;\n\n // Oval markazi\n const cx = vw / 2;\n const cy = vh / 2;\n\n // Oval radius\n const ry = vh * 0.45; // height = 90%\n const rx = ry * (5 / 6); // aspect-ratio = 5/6\n\n // Mirror x for CSS scaleX(-1)\n const mirrorX = (x: number) => vw - x;\n\n // Face edge points\n const points = [\n { x: mirrorX(box.x), y: box.y + box.height / 2 }, // left\n { x: mirrorX(box.x + box.width), y: box.y + box.height / 2 }, // right\n { x: mirrorX(box.x + box.width / 2), y: box.y }, // top\n { x: mirrorX(box.x + box.width / 2), y: box.y + box.height }, // bottom\n ];\n\n const inside = points.every(\n (p) => Math.pow((p.x - cx) / rx, 2) + Math.pow((p.y - cy) / ry, 2) <= 1,\n );\n\n if (!inside) {\n this.faceInsideStatus.set('OUTSIDE_OVAL');\n return false;\n }\n\n // Fill ratio\n const faceArea = box.width * box.height;\n const ovalArea = Math.PI * rx * ry;\n const fillRatio = faceArea / ovalArea;\n\n if (fillRatio < 0.42) {\n this.faceInsideStatus.set('COME_CLOSE');\n return false;\n }\n\n if (fillRatio > 0.6) {\n this.faceInsideStatus.set('VERY_CLOSE');\n return false;\n }\n\n this.faceInsideStatus.set('VALID');\n return true;\n }\n\n private dist(a: any, b: any) {\n return Math.hypot(a.x - b.x, a.y - b.y);\n }\n\n private eyeEAR(eye: faceapi.Point[]) {\n return (\n (this.dist(eye[1], eye[5]) + this.dist(eye[2], eye[4])) / (2 * this.dist(eye[0], eye[3]))\n );\n }\n\n private isBlink(lm: faceapi.FaceLandmarks68) {\n const left = this.eyeEAR(lm.getLeftEye());\n const right = this.eyeEAR(lm.getRightEye());\n const avg = (left + right) / 2;\n\n if (this.prevBlinkAvg === 0 || Math.abs(avg - this.prevBlinkAvg) < 0.01) {\n this.prevBlinkAvg = avg;\n return false;\n }\n\n this.prevBlinkAvg = avg;\n this.blinkCount++;\n return this.blinkCount > 2;\n }\n\n private isMouthOpen(lm: faceapi.FaceLandmarks68) {\n const mouth = lm.getMouth();\n return this.dist(mouth[13], mouth[19]) / this.dist(mouth[0], mouth[6]) > 0.4;\n }\n\n private isHeadMoved(lm: faceapi.FaceLandmarks68) {\n const nose = lm.getNose()[3];\n if (!this.prevNoseX) {\n this.prevNoseX = nose.x;\n return false;\n }\n const moved = Math.abs(nose.x - this.prevNoseX) > 30;\n\n this.prevNoseX = nose.x;\n return moved;\n }\n\n // ---------- MAIN CHECK ----------\n\n async process(video: HTMLVideoElement): Promise<boolean> {\n const step = this.step();\n\n const result = await faceapi\n .detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())\n .withFaceLandmarks();\n\n if (!result) {\n if (step === 'START') {\n this.faceInsideStatus.set('NO_FACE');\n }\n return false;\n }\n\n if (step === 'START') {\n // 👇 OVAL CHECK\n const insideOval = this.isFaceInsideOval(result.detection.box, video);\n\n if (!insideOval) {\n if (this.step() === 'HOLD') {\n this.holdStart = Date.now();\n }\n return false;\n }\n }\n\n const lm = result.landmarks;\n\n if (step === 'START') {\n this.step.set('BLINK');\n } else if (step === 'BLINK' && this.isBlink(lm)) {\n this.step.set('MOUTH');\n } else if (step === 'MOUTH' && this.isMouthOpen(lm)) {\n this.step.set('HEAD');\n } else if (step === 'HEAD' && this.isHeadMoved(lm)) {\n this.step.set('HOLD');\n this.holdStart = Date.now();\n } else if (step === 'HOLD') {\n if (!this.holdStart) this.holdStart = Date.now();\n if (Date.now() - this.holdStart > 3000) {\n this.step.set('DONE');\n return true; // 📸 CAPTURE SIGNAL\n }\n }\n\n return false;\n }\n}\n","import {\n Component,\n ElementRef,\n ViewChild,\n OnInit,\n computed,\n output,\n inject,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { LivenessService } from './liveness.service';\nimport { isPlatformBrowser } from '@angular/common';\n\n@Component({\n selector: 'ngx-face-liveness',\n template: `\n <div class=\"camera-wrapper\" [hidden]=\"loadingResourses()\">\n @if (capturesImageSrc()) {\n <img [src]=\"capturesImageSrc()\" alt=\"Captured Image\" class=\"captured-image\" />\n }\n <video #video autoplay muted playsinline class=\"camera-video\"></video>\n\n <!-- Overlay -->\n <div class=\"overlay\">\n <div class=\"oval-mask\" [style.borderColor]=\"isNotValidOval() ? '#fb2c36' : '#65a0f8'\"></div>\n <p class=\"hint\">{{ stepText() }}</p>\n </div>\n </div>\n @if (loadingResourses()) {\n <div class=\"loading-text-container\">\n <p class=\"loading-text\">{{ loadingMessage() }}</p>\n </div>\n }\n `,\n styleUrls: ['./face-liveness.scss'],\n})\nexport class NgxFaceLiveness implements OnInit {\n private platformId = inject(PLATFORM_ID);\n private isBrowser = isPlatformBrowser(this.platformId);\n loadingResourses = signal(true);\n loadingMessage = signal('Kamera yuklanmoqda...');\n\n @ViewChild('video') videoRef!: ElementRef<HTMLVideoElement>;\n intervalId: any;\n\n capturedImage = output<Blob>();\n capturesImageSrc = signal<string | null>(null);\n\n constructor(private liveness: LivenessService) {}\n\n async ngOnInit() {\n if (!this.isBrowser) return;\n\n await this.liveness.loadModels();\n await this.start();\n }\n\n currentStep = computed(() => this.liveness.currentStep());\n currentFaceInsideStatus = computed(() => this.liveness.currentFaceInsideStatus());\n isNotValidOval = computed(\n () =>\n this.currentFaceInsideStatus() === 'OUTSIDE_OVAL' ||\n this.currentFaceInsideStatus() === 'NO_FACE',\n );\n stepText = computed(() => {\n const currentFaceStatus = this.currentFaceInsideStatus();\n\n if (currentFaceStatus === 'NO_FACE') {\n return '❌ Yuz aniqlanmadi, kameraga qarang';\n }\n if (currentFaceStatus === 'OUTSIDE_OVAL') {\n return '📐 Yuzingizni oval ichiga joylashtiring';\n }\n if (currentFaceStatus === 'COME_CLOSE') {\n return '🔍 Kameraga yaqinroq turing';\n }\n if (currentFaceStatus === 'VERY_CLOSE') {\n return '📷 Juda yaqin, kamerani orqaroq oling';\n }\n\n switch (this.currentStep()) {\n case 'BLINK':\n return '👁 Ko‘zingizni yumib oching(2 marta)';\n case 'MOUTH':\n return '🙂 Og‘zingizni ochib yuming';\n case 'HEAD':\n return '↔️ Boshni chapga yoki o‘ngga burang';\n case 'HOLD':\n return '✋ Barqaror turing (2 soniya)';\n default:\n return '✅ Tayyor!';\n }\n });\n\n async start() {\n this.liveness.reset();\n await this.openCamera();\n\n this.intervalId = setInterval(async () => {\n const done = await this.liveness.process(this.videoRef.nativeElement);\n\n if (done) {\n clearInterval(this.intervalId);\n const blob = await this.capture();\n this.sendToBackend(blob);\n }\n }, 300);\n }\n\n async openCamera() {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const videoDevices = devices.filter((d) => d.kind === 'videoinput');\n\n if (!videoDevices.length) {\n this.loadingMessage.set('Kamera qurilmasi topilmadi!');\n throw new Error('No video devices found');\n }\n\n // USB yoki default camera tanlash\n const stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: videoDevices[0].deviceId,\n }, // birinchi topilgan kamera\n });\n\n this.videoRef.nativeElement.srcObject = stream;\n this.loadingResourses.set(false);\n } catch (err) {\n console.error('Cannot access camera:', err);\n }\n\n // const stream = await navigator.mediaDevices.getUserMedia({\n // video: { facingMode: 'user', aspectRatio: 3 / 4, width: { min: 480 }, height: { min: 640 } },\n // });\n // this.videoRef.nativeElement.srcObject = stream;\n }\n\n async capture(): Promise<Blob> {\n const video = this.videoRef.nativeElement;\n const canvas = document.createElement('canvas');\n\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n canvas.getContext('2d')!.drawImage(video, 0, 0);\n\n return new Promise((res) => canvas.toBlob((b) => res(b!), 'image/jpeg', 0.9));\n }\n\n sendToBackend(blob: Blob) {\n this.capturedImage.emit(blob);\n this.capturesImageSrc.set(URL.createObjectURL(blob));\n console.log(blob);\n\n // const fd = new FormData();\n // fd.append('file', blob, 'face.jpg');\n // this.http.post('/api/face/verify', fd).subscribe();\n }\n\n ngOnDestroy(): void {\n if (!this.isBrowser) return;\n\n this.cleanup();\n }\n\n private cleanup() {\n // 1️⃣ Interval\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n\n // 2️⃣ Kamera\n const video = this.videoRef?.nativeElement;\n if (video) {\n video.pause();\n\n const stream = video.srcObject as MediaStream | null;\n if (stream) {\n stream.getTracks().forEach((track) => track.stop());\n }\n\n video.srcObject = null;\n }\n\n // 3️⃣ Service state\n this.liveness.reset();\n }\n}\n","/*\r\n * Public API Surface of ngx-face-id\r\n */\r\n\r\nexport * from './lib/face-liveness/face-liveness';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.LivenessService"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utamuratov/ngx-face-id",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,7 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { OnInit, ElementRef } from '@angular/core';
3
3
 
4
- type Step = 'BLINK' | 'MOUTH' | 'HEAD' | 'HOLD' | 'DONE';
4
+ type Step = 'START' | 'BLINK' | 'MOUTH' | 'HEAD' | 'HOLD' | 'DONE';
5
5
  type FaceInside = 'NO_FACE' | 'OUTSIDE_OVAL' | 'COME_CLOSE' | 'VERY_CLOSE' | 'VALID';
6
6
  declare class LivenessService {
7
7
  private step;
@@ -29,16 +29,18 @@ declare class NgxFaceLiveness implements OnInit {
29
29
  private liveness;
30
30
  private platformId;
31
31
  private isBrowser;
32
+ loadingResourses: _angular_core.WritableSignal<boolean>;
33
+ loadingMessage: _angular_core.WritableSignal<string>;
32
34
  videoRef: ElementRef<HTMLVideoElement>;
33
35
  intervalId: any;
34
36
  capturedImage: _angular_core.OutputEmitterRef<Blob>;
35
37
  capturesImageSrc: _angular_core.WritableSignal<string | null>;
36
38
  constructor(liveness: LivenessService);
37
39
  ngOnInit(): Promise<void>;
38
- currentStep: _angular_core.Signal<"BLINK" | "MOUTH" | "HEAD" | "HOLD" | "DONE">;
40
+ currentStep: _angular_core.Signal<"START" | "BLINK" | "MOUTH" | "HEAD" | "HOLD" | "DONE">;
39
41
  currentFaceInsideStatus: _angular_core.Signal<"NO_FACE" | "OUTSIDE_OVAL" | "COME_CLOSE" | "VERY_CLOSE" | "VALID">;
40
42
  isNotValidOval: _angular_core.Signal<boolean>;
41
- stepText: _angular_core.Signal<"❌ Yuz aniqlanmadi, iltimos, kameraga qarang" | "📐 Iltimos, yuzingizni oval ichiga joylashtiring" | "🔍 Iltimos, kameraga yaqinroq turing" | "📷 Juda yaqin, kamerani orqaroq oling" | "👁 Ko‘zingizni yumib oching(2 marta)" | "🙂 Og‘zingizni ochib yuming" | "↔️ Boshni chapga yoki o‘ngga burang" | "✋ Barqaror turing (2 soniya)" | "✅ Tayyor!">;
43
+ stepText: _angular_core.Signal<"❌ Yuz aniqlanmadi, kameraga qarang" | "📐 Yuzingizni oval ichiga joylashtiring" | "🔍 Kameraga yaqinroq turing" | "📷 Juda yaqin, kamerani orqaroq oling" | "👁 Ko‘zingizni yumib oching(2 marta)" | "🙂 Og‘zingizni ochib yuming" | "↔️ Boshni chapga yoki o‘ngga burang" | "✋ Barqaror turing (2 soniya)" | "✅ Tayyor!">;
42
44
  start(): Promise<void>;
43
45
  openCamera(): Promise<void>;
44
46
  capture(): Promise<Blob>;