facecog-liveness-showcase 0.0.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.
Files changed (229) hide show
  1. package/.browserslistrc +15 -0
  2. package/.dockerignore +48 -0
  3. package/.editorconfig +16 -0
  4. package/.eslintrc.json +47 -0
  5. package/.vercelignore +7 -0
  6. package/.vscode/extensions.json +5 -0
  7. package/.vscode/settings.json +3 -0
  8. package/DOCKER.md +221 -0
  9. package/Dockerfile +33 -0
  10. package/README.md +268 -0
  11. package/angular.json +156 -0
  12. package/capacitor.config.ts +9 -0
  13. package/docker-compose.dev.yml +20 -0
  14. package/docker-compose.yml +18 -0
  15. package/ionic.config.json +7 -0
  16. package/jest.config.js +38 -0
  17. package/nginx.conf +50 -0
  18. package/package.json +131 -0
  19. package/patches/ng-packagr+20.3.2.patch +60 -0
  20. package/projects/facecog-liveness-verification/README.md +295 -0
  21. package/projects/facecog-liveness-verification/ng-package.json +7 -0
  22. package/projects/facecog-liveness-verification/package.json +48 -0
  23. package/projects/facecog-liveness-verification/scripts/build-with-wrapper-copy.js +38 -0
  24. package/projects/facecog-liveness-verification/scripts/copy-wrapper-after-ngc.js +35 -0
  25. package/projects/facecog-liveness-verification/sources/FaceLivenessReactWrapper.tsx +320 -0
  26. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.generated.d.ts +28 -0
  27. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.generated.js +247 -0
  28. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.generated.js.map +1 -0
  29. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.js.map +1 -0
  30. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.ts +5 -0
  31. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/aws-face-liveness.component.ts +500 -0
  32. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.html +41 -0
  33. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.scss +234 -0
  34. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.spec.ts +158 -0
  35. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.ts +58 -0
  36. package/projects/facecog-liveness-verification/src/lib/components/camera-verification/camera-verification.component.html +34 -0
  37. package/projects/facecog-liveness-verification/src/lib/components/camera-verification/camera-verification.component.ts +210 -0
  38. package/projects/facecog-liveness-verification/src/lib/components/dialogs/save-custom-pose-dialog.component.ts +174 -0
  39. package/projects/facecog-liveness-verification/src/lib/components/facetec-scan/facetec-scan.component.html +45 -0
  40. package/projects/facecog-liveness-verification/src/lib/components/facetec-scan/facetec-scan.component.scss +87 -0
  41. package/projects/facecog-liveness-verification/src/lib/components/facetec-scan/facetec-scan.component.ts +182 -0
  42. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.html +394 -0
  43. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.scss +1567 -0
  44. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.spec.ts +699 -0
  45. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.ts +721 -0
  46. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.html +120 -0
  47. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.scss +611 -0
  48. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.spec.ts +605 -0
  49. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.ts +524 -0
  50. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.html +73 -0
  51. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.scss +19 -0
  52. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.spec.ts +673 -0
  53. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.ts +963 -0
  54. package/projects/facecog-liveness-verification/src/lib/components/liveness-verification/liveness-verification.component.html +38 -0
  55. package/projects/facecog-liveness-verification/src/lib/components/liveness-verification/liveness-verification.component.scss +10 -0
  56. package/projects/facecog-liveness-verification/src/lib/components/liveness-verification/liveness-verification.component.ts +233 -0
  57. package/projects/facecog-liveness-verification/src/lib/components/pose-selection/pose-selection.component.html +17 -0
  58. package/projects/facecog-liveness-verification/src/lib/components/pose-selection/pose-selection.component.spec.ts +35 -0
  59. package/projects/facecog-liveness-verification/src/lib/components/pose-selection/pose-selection.component.ts +33 -0
  60. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.html +17 -0
  61. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.scss +156 -0
  62. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.spec.ts +46 -0
  63. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.ts +18 -0
  64. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.html +190 -0
  65. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.scss +534 -0
  66. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.spec.ts +286 -0
  67. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.ts +155 -0
  68. package/projects/facecog-liveness-verification/src/lib/interfaces/analyze-response.interface.ts +16 -0
  69. package/projects/facecog-liveness-verification/src/lib/interfaces/aws-face-liveness.interface.ts +46 -0
  70. package/projects/facecog-liveness-verification/src/lib/interfaces/backend-adapter.interface.ts +21 -0
  71. package/projects/facecog-liveness-verification/src/lib/interfaces/backend-http-client.interface.ts +93 -0
  72. package/projects/facecog-liveness-verification/src/lib/interfaces/backend-response.interface.ts +9 -0
  73. package/projects/facecog-liveness-verification/src/lib/interfaces/camera-provider.interface.ts +107 -0
  74. package/projects/facecog-liveness-verification/src/lib/interfaces/category-info.interface.ts +9 -0
  75. package/projects/facecog-liveness-verification/src/lib/interfaces/custom-pose-data.interface.ts +14 -0
  76. package/projects/facecog-liveness-verification/src/lib/interfaces/custom-pose-repository.interface.ts +48 -0
  77. package/projects/facecog-liveness-verification/src/lib/interfaces/custom-pose-response.interface.ts +14 -0
  78. package/projects/facecog-liveness-verification/src/lib/interfaces/index.ts +52 -0
  79. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-action-result.interface.ts +13 -0
  80. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-config.interface.ts +17 -0
  81. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-metadata.interface.ts +17 -0
  82. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-result.interface.ts +24 -0
  83. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-verification-config.interface.ts +41 -0
  84. package/projects/facecog-liveness-verification/src/lib/interfaces/multi-backend-analyze-response.interface.ts +21 -0
  85. package/projects/facecog-liveness-verification/src/lib/interfaces/multi-backend-liveness-result.interface.ts +14 -0
  86. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-definition.interface.ts +35 -0
  87. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-keypoint.interface.ts +12 -0
  88. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-match-result.interface.ts +9 -0
  89. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-verify-response.interface.ts +8 -0
  90. package/projects/facecog-liveness-verification/src/lib/interfaces/scan-results.interface.ts +29 -0
  91. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-plan.interface.ts +42 -0
  92. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-progress-event.interface.ts +12 -0
  93. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-session.interface.ts +72 -0
  94. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-step-change-event.interface.ts +11 -0
  95. package/projects/facecog-liveness-verification/src/lib/interfaces/video-recording.interface.ts +9 -0
  96. package/projects/facecog-liveness-verification/src/lib/liveness-verification.module.ts +123 -0
  97. package/projects/facecog-liveness-verification/src/lib/models/constants/aws-face-liveness-component.token.ts +23 -0
  98. package/projects/facecog-liveness-verification/src/lib/models/constants/category-info.constant.ts +14 -0
  99. package/projects/facecog-liveness-verification/src/lib/models/constants/default-liveness-config.constant.ts +18 -0
  100. package/projects/facecog-liveness-verification/src/lib/models/constants/index.ts +5 -0
  101. package/projects/facecog-liveness-verification/src/lib/models/constants/liveness-verification-config.token.ts +16 -0
  102. package/projects/facecog-liveness-verification/src/lib/models/constants/pose-definitions.constant.ts +377 -0
  103. package/projects/facecog-liveness-verification/src/lib/models/index.ts +5 -0
  104. package/projects/facecog-liveness-verification/src/lib/models/utils/index.ts +2 -0
  105. package/projects/facecog-liveness-verification/src/lib/models/utils/pose.utils.spec.ts +76 -0
  106. package/projects/facecog-liveness-verification/src/lib/models/utils/pose.utils.ts +59 -0
  107. package/projects/facecog-liveness-verification/src/lib/services/aws-face-liveness.service.ts +49 -0
  108. package/projects/facecog-liveness-verification/src/lib/services/backend-http.service.spec.ts +111 -0
  109. package/projects/facecog-liveness-verification/src/lib/services/backend-http.service.ts +130 -0
  110. package/projects/facecog-liveness-verification/src/lib/services/backends/azure-backend.service.spec.ts +69 -0
  111. package/projects/facecog-liveness-verification/src/lib/services/backends/azure-backend.service.ts +72 -0
  112. package/projects/facecog-liveness-verification/src/lib/services/backends/facetec-backend.service.spec.ts +24 -0
  113. package/projects/facecog-liveness-verification/src/lib/services/backends/facetec-backend.service.ts +35 -0
  114. package/projects/facecog-liveness-verification/src/lib/services/backends/mock-backend.service.spec.ts +36 -0
  115. package/projects/facecog-liveness-verification/src/lib/services/backends/mock-backend.service.ts +39 -0
  116. package/projects/facecog-liveness-verification/src/lib/services/backends/openpose-backend.service.spec.ts +81 -0
  117. package/projects/facecog-liveness-verification/src/lib/services/backends/openpose-backend.service.ts +72 -0
  118. package/projects/facecog-liveness-verification/src/lib/services/backends/rekognition-analysis-backend.service.spec.ts +69 -0
  119. package/projects/facecog-liveness-verification/src/lib/services/backends/rekognition-analysis-backend.service.ts +83 -0
  120. package/projects/facecog-liveness-verification/src/lib/services/camera.service.spec.ts +200 -0
  121. package/projects/facecog-liveness-verification/src/lib/services/camera.service.ts +155 -0
  122. package/projects/facecog-liveness-verification/src/lib/services/custom-poses-api.service.ts +117 -0
  123. package/projects/facecog-liveness-verification/src/lib/services/index.ts +18 -0
  124. package/projects/facecog-liveness-verification/src/lib/services/liveness-backend.service.spec.ts +103 -0
  125. package/projects/facecog-liveness-verification/src/lib/services/liveness-backend.service.ts +61 -0
  126. package/projects/facecog-liveness-verification/src/lib/services/liveness-config.service.spec.ts +109 -0
  127. package/projects/facecog-liveness-verification/src/lib/services/liveness-config.service.ts +70 -0
  128. package/projects/facecog-liveness-verification/src/lib/services/liveness-orchestrator.service.spec.ts +144 -0
  129. package/projects/facecog-liveness-verification/src/lib/services/liveness-orchestrator.service.ts +162 -0
  130. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/hand-gesture-detection.service.ts +315 -0
  131. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/index.ts +5 -0
  132. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/openpose.service.ts +287 -0
  133. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/pose-comparison.service.ts +353 -0
  134. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/pose-matching.service.ts +2370 -0
  135. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/reference-pose.service.ts +271 -0
  136. package/projects/facecog-liveness-verification/src/lib/services/pose-selection.service.spec.ts +183 -0
  137. package/projects/facecog-liveness-verification/src/lib/services/pose-selection.service.ts +179 -0
  138. package/projects/facecog-liveness-verification/src/lib/services/verification-api.service.spec.ts +159 -0
  139. package/projects/facecog-liveness-verification/src/lib/services/verification-api.service.ts +151 -0
  140. package/projects/facecog-liveness-verification/src/lib/services/verification-plan.service.spec.ts +184 -0
  141. package/projects/facecog-liveness-verification/src/lib/services/verification-plan.service.ts +94 -0
  142. package/projects/facecog-liveness-verification/src/lib/services/video-recorder.service.spec.ts +52 -0
  143. package/projects/facecog-liveness-verification/src/lib/services/video-recorder.service.ts +117 -0
  144. package/projects/facecog-liveness-verification/src/lib/types/detection-strategy.type.ts +5 -0
  145. package/projects/facecog-liveness-verification/src/lib/types/index.ts +7 -0
  146. package/projects/facecog-liveness-verification/src/lib/types/liveness-action.type.ts +31 -0
  147. package/projects/facecog-liveness-verification/src/lib/types/liveness-backend.type.ts +5 -0
  148. package/projects/facecog-liveness-verification/src/lib/types/pose-category.type.ts +5 -0
  149. package/projects/facecog-liveness-verification/src/lib/types/pose-difficulty.type.ts +5 -0
  150. package/projects/facecog-liveness-verification/src/lib/types/verification-flow-step.type.ts +5 -0
  151. package/projects/facecog-liveness-verification/src/lib/types/verification-step-kind.type.ts +4 -0
  152. package/projects/facecog-liveness-verification/src/public-api.ts +150 -0
  153. package/projects/facecog-liveness-verification/tsconfig.lib.json +20 -0
  154. package/projects/facecog-liveness-verification/tsconfig.lib.prod.json +11 -0
  155. package/projects/facecog-liveness-verification/tsconfig.spec.json +13 -0
  156. package/projects/facecog-liveness-verification/tsconfig.wrapper.json +15 -0
  157. package/projects/facecog-liveness-verification-test/src/app/app-routing.module.ts +22 -0
  158. package/projects/facecog-liveness-verification-test/src/app/app.component.html +3 -0
  159. package/projects/facecog-liveness-verification-test/src/app/app.component.scss +0 -0
  160. package/projects/facecog-liveness-verification-test/src/app/app.component.ts +11 -0
  161. package/projects/facecog-liveness-verification-test/src/app/app.module.ts +27 -0
  162. package/projects/facecog-liveness-verification-test/src/app/home/home-routing.module.ts +16 -0
  163. package/projects/facecog-liveness-verification-test/src/app/home/home.module.ts +19 -0
  164. package/projects/facecog-liveness-verification-test/src/app/home/home.page.html +39 -0
  165. package/projects/facecog-liveness-verification-test/src/app/home/home.page.scss +97 -0
  166. package/projects/facecog-liveness-verification-test/src/app/home/home.page.spec.ts +24 -0
  167. package/projects/facecog-liveness-verification-test/src/app/home/home.page.ts +92 -0
  168. package/projects/facecog-liveness-verification-test/src/app/home/verification-modal.component.ts +106 -0
  169. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Bold_0.ttf +0 -0
  170. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Medium_0.ttf +0 -0
  171. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Regular_0.ttf +0 -0
  172. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-SemiBold_0.ttf +0 -0
  173. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Thin_0.ttf +0 -0
  174. package/projects/facecog-liveness-verification-test/src/assets/icon/favicon.png +0 -0
  175. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Five_Fingers_Left.jpg +0 -0
  176. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Left_Palm.jpg +0 -0
  177. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Ok_Sign_Right.jpg +0 -0
  178. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Peace_Sign_Left.jpg +0 -0
  179. package/projects/facecog-liveness-verification-test/src/assets/images/poses/README.md +77 -0
  180. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Right_Palm.jpg +0 -0
  181. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Speak_Phrase.jpg +0 -0
  182. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Three_Fingers_Right.jpg +0 -0
  183. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Thumbs_Up_Left.jpg +0 -0
  184. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Thumbs_Up_Right.jpg +0 -0
  185. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Wave_Right.jpg +0 -0
  186. package/projects/facecog-liveness-verification-test/src/assets/images/poses/blink.jpeg +0 -0
  187. package/projects/facecog-liveness-verification-test/src/assets/images/poses/blink_twice.jpeg +0 -0
  188. package/projects/facecog-liveness-verification-test/src/assets/images/poses/center_face.png +0 -0
  189. package/projects/facecog-liveness-verification-test/src/assets/images/poses/clap.jpeg +0 -0
  190. package/projects/facecog-liveness-verification-test/src/assets/images/poses/cover_mouth.png +0 -0
  191. package/projects/facecog-liveness-verification-test/src/assets/images/poses/cover_right_eye.png +0 -0
  192. package/projects/facecog-liveness-verification-test/src/assets/images/poses/cross_arms.png +0 -0
  193. package/projects/facecog-liveness-verification-test/src/assets/images/poses/face_straight.png +0 -0
  194. package/projects/facecog-liveness-verification-test/src/assets/images/poses/follow_dot.png +0 -0
  195. package/projects/facecog-liveness-verification-test/src/assets/images/poses/look_down.png +0 -0
  196. package/projects/facecog-liveness-verification-test/src/assets/images/poses/look_up.png +0 -0
  197. package/projects/facecog-liveness-verification-test/src/assets/images/poses/move_closer.png +0 -0
  198. package/projects/facecog-liveness-verification-test/src/assets/images/poses/nod.png +0 -0
  199. package/projects/facecog-liveness-verification-test/src/assets/images/poses/open_mouth.png +0 -0
  200. package/projects/facecog-liveness-verification-test/src/assets/images/poses/raise_eyebrow.png +0 -0
  201. package/projects/facecog-liveness-verification-test/src/assets/images/poses/rotate_face.jpeg +0 -0
  202. package/projects/facecog-liveness-verification-test/src/assets/images/poses/shake_head.jpeg +0 -0
  203. package/projects/facecog-liveness-verification-test/src/assets/images/poses/smile.png +0 -0
  204. package/projects/facecog-liveness-verification-test/src/assets/images/poses/tilt_left.png +0 -0
  205. package/projects/facecog-liveness-verification-test/src/assets/images/poses/tilt_right.png +0 -0
  206. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_chin_left.jpg +0 -0
  207. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_left_cheek.jpeg +0 -0
  208. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_nose_right.png +0 -0
  209. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_right_cheek.jpeg +0 -0
  210. package/projects/facecog-liveness-verification-test/src/assets/images/poses/turn_left.png +0 -0
  211. package/projects/facecog-liveness-verification-test/src/assets/images/poses/turn_right.png +0 -0
  212. package/projects/facecog-liveness-verification-test/src/assets/images/poses/wink.jpeg +0 -0
  213. package/projects/facecog-liveness-verification-test/src/assets/images/reference-pose.jpg +0 -0
  214. package/projects/facecog-liveness-verification-test/src/assets/shapes.svg +1 -0
  215. package/projects/facecog-liveness-verification-test/src/environments/environment.prod.ts +4 -0
  216. package/projects/facecog-liveness-verification-test/src/environments/environment.ts +17 -0
  217. package/projects/facecog-liveness-verification-test/src/global.scss +288 -0
  218. package/projects/facecog-liveness-verification-test/src/index.html +31 -0
  219. package/projects/facecog-liveness-verification-test/src/main.ts +6 -0
  220. package/projects/facecog-liveness-verification-test/src/polyfills.ts +55 -0
  221. package/projects/facecog-liveness-verification-test/src/theme/nextsapien-theme.scss +174 -0
  222. package/projects/facecog-liveness-verification-test/src/theme/variables.scss +2 -0
  223. package/projects/facecog-liveness-verification-test/src/zone-flags.ts +6 -0
  224. package/projects/facecog-liveness-verification-test/tsconfig.app.json +15 -0
  225. package/projects/facecog-liveness-verification-test/tsconfig.spec.json +14 -0
  226. package/setup-jest.ts +118 -0
  227. package/tsconfig.json +41 -0
  228. package/tsconfig.spec.json +15 -0
  229. package/vercel.json +24 -0
@@ -0,0 +1,721 @@
1
+ import { Component, EventEmitter, Input, Output, OnInit, Optional, Inject } from '@angular/core';
2
+ import { IonicModule, ModalController, AlertController, ToastController, LoadingController } from '@ionic/angular';
3
+ import { CommonModule } from '@angular/common';
4
+ import { FormsModule } from '@angular/forms';
5
+ import { ReferencePoseService, ReferencePose } from '../../services/pose-detection/reference-pose.service';
6
+ import { OpenposeService } from '../../services/pose-detection/openpose.service';
7
+ import { PoseSelectionService, SelectedPoseInfo } from '../../services/pose-selection.service';
8
+ import { LivenessConfigService } from '../../services/liveness-config.service';
9
+ import { ICustomPoseRepository, CUSTOM_POSE_REPOSITORY } from '../../interfaces/custom-pose-repository.interface';
10
+ import { PoseDefinition } from '../../interfaces/pose-definition.interface';
11
+ import { LivenessBackend } from '../../types/liveness-backend.type';
12
+ import { POSE_DEFINITIONS } from '../../models/constants/pose-definitions.constant';
13
+ import { CATEGORY_INFO } from '../../models/constants/category-info.constant';
14
+ import { mergeCustomPoses, customPoseToPoseDefinition } from '../../models/utils/pose.utils';
15
+ import { SaveCustomPoseDialogComponent } from '../dialogs/save-custom-pose-dialog.component';
16
+
17
+ @Component({
18
+ selector: 'lib-intro',
19
+ standalone: true,
20
+ imports: [CommonModule, IonicModule, FormsModule],
21
+ templateUrl: './intro.component.html',
22
+ styleUrls: ['./intro.component.scss']
23
+ })
24
+ export class IntroComponent implements OnInit {
25
+ @Input() exampleImageUrl?: string;
26
+ @Input() exampleImageBase64?: string;
27
+ /** When true (pose-specific flow from host), hide backend selection, cancel, and pose-choice text. */
28
+ @Input() poseSpecificMode = false;
29
+ @Output() startClicked = new EventEmitter<void>();
30
+ @Output() cancelClicked = new EventEmitter<void>();
31
+ @Output() referencePoseUploaded = new EventEmitter<ReferencePose>();
32
+ @Output() poseSelected = new EventEmitter<SelectedPoseInfo>();
33
+ @Output() awsFaceLivenessToggled = new EventEmitter<boolean>();
34
+
35
+ // Pose selection state
36
+ allPoses: PoseDefinition[] = POSE_DEFINITIONS;
37
+ selectedPose: SelectedPoseInfo | null = null;
38
+ categoryInfo = CATEGORY_INFO;
39
+
40
+ // Filter state
41
+ selectedCategory: PoseDefinition['category'] | 'all' = 'all';
42
+ selectedDifficulty: PoseDefinition['difficulty'] | 'all' = 'all';
43
+ searchQuery: string = '';
44
+
45
+ // UI state
46
+ viewMode: 'grid' | 'list' = 'grid';
47
+ showFilters: boolean = false;
48
+
49
+ // Pose selection mode: 'browse' (grid) or 'upload' (custom image)
50
+ poseSelectionMode: 'browse' | 'upload' = 'browse';
51
+
52
+ // Reference pose upload state
53
+ referencePoseImage: string | null = null;
54
+ referencePoseDescription: string | null = null;
55
+ isProcessingImage = false;
56
+ poseDetectionStatus: { success: boolean; message: string } | null = null;
57
+ isDragging = false;
58
+ isSavingCustomPose = false; // Prevent double-submission
59
+
60
+ // Backend selection state
61
+ selectedBackends: { [key: string]: boolean } = {
62
+ amazon: true,
63
+ openpose: true,
64
+ facetec: false
65
+ };
66
+
67
+ // Available backend options with descriptions
68
+ backendOptions = [
69
+ { id: 'amazon', name: 'Amazon Rekognition', description: 'Face detection & image quality analysis' },
70
+ { id: 'openpose', name: 'OpenPose/MoveNet', description: 'Pose detection with TensorFlow' },
71
+ { id: 'facetec', name: 'FaceTec 3D', description: '3D face liveness detection' }
72
+ ];
73
+
74
+ showBackendSettings: boolean = false;
75
+
76
+ // AWS Face Liveness (Anti-Spoofing) toggle
77
+ useAwsFaceLiveness: boolean = false;
78
+
79
+ // Browser detection for AWS Face Liveness compatibility
80
+ isSafariBrowser: boolean = false;
81
+
82
+ // Custom pose feature availability
83
+ get customPosesEnabled(): boolean {
84
+ return !!this.customPoseRepo;
85
+ }
86
+
87
+ get displayImage(): string | undefined {
88
+ return this.exampleImageBase64 || this.exampleImageUrl;
89
+ }
90
+
91
+ get filteredPoses(): PoseDefinition[] {
92
+ let filtered = this.allPoses;
93
+
94
+ // Filter by category
95
+ if (this.selectedCategory !== 'all') {
96
+ filtered = filtered.filter(pose => pose.category === this.selectedCategory);
97
+ }
98
+
99
+ // Filter by difficulty
100
+ if (this.selectedDifficulty !== 'all') {
101
+ filtered = filtered.filter(pose => pose.difficulty === this.selectedDifficulty);
102
+ }
103
+
104
+ // Filter by search query
105
+ if (this.searchQuery.trim()) {
106
+ const query = this.searchQuery.toLowerCase();
107
+ filtered = filtered.filter(pose =>
108
+ pose.name.toLowerCase().includes(query) ||
109
+ pose.description.toLowerCase().includes(query)
110
+ );
111
+ }
112
+
113
+ return filtered;
114
+ }
115
+
116
+ get categories(): Array<{ key: PoseDefinition['category'] | 'all'; label: string }> {
117
+ return [
118
+ { key: 'all', label: 'All Poses' },
119
+ { key: 'face', label: 'Face Expressions' },
120
+ { key: 'head', label: 'Head Movements' },
121
+ { key: 'hand', label: 'Hand Gestures' },
122
+ { key: 'gesture', label: 'Gestures' },
123
+ { key: 'combined', label: 'Combined' }
124
+ ];
125
+ }
126
+
127
+ get difficulties(): Array<{ key: PoseDefinition['difficulty'] | 'all'; label: string }> {
128
+ return [
129
+ { key: 'all', label: 'All Levels' },
130
+ { key: 'easy', label: 'Easy' },
131
+ { key: 'medium', label: 'Medium' },
132
+ { key: 'hard', label: 'Hard' }
133
+ ];
134
+ }
135
+
136
+ // Store reference pose for saving
137
+ private referencePose: ReferencePose | null = null;
138
+
139
+ constructor(
140
+ private referencePoseService: ReferencePoseService,
141
+ private openposeService: OpenposeService,
142
+ private poseSelectionService: PoseSelectionService,
143
+ private configService: LivenessConfigService,
144
+ private modalCtrl: ModalController,
145
+ private alertCtrl: AlertController,
146
+ private toastCtrl: ToastController,
147
+ private loadingCtrl: LoadingController,
148
+ @Optional() @Inject(CUSTOM_POSE_REPOSITORY) private customPoseRepo?: ICustomPoseRepository
149
+ ) {
150
+ // Apply initial backend configuration
151
+ this.applyBackendConfig();
152
+
153
+ // Detect Safari browser for AWS Face Liveness compatibility warning
154
+ this.detectSafariBrowser();
155
+ }
156
+
157
+ /**
158
+ * Detect if the browser is Safari (not compatible with AWS Face Liveness)
159
+ */
160
+ private detectSafariBrowser(): void {
161
+ const userAgent = navigator.userAgent.toLowerCase();
162
+ this.isSafariBrowser = /safari/.test(userAgent) && !/chrome/.test(userAgent) && !/chromium/.test(userAgent);
163
+ if (this.isSafariBrowser) {
164
+ console.log('[IntroComponent] Safari browser detected - AWS Face Liveness may not work');
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Apply the selected backends to the config service
170
+ */
171
+ applyBackendConfig(): void {
172
+ const backends = Object.entries(this.selectedBackends)
173
+ .filter(([_, enabled]) => enabled)
174
+ .map(([id]) => id as LivenessBackend);
175
+
176
+ if (backends.length === 0) {
177
+ // Default to amazon if nothing selected
178
+ backends.push('amazon');
179
+ this.selectedBackends['amazon'] = true;
180
+ }
181
+
182
+ console.log('[IntroComponent] Configuring backends:', backends);
183
+ this.configService.enableBackends(backends);
184
+ }
185
+
186
+ /**
187
+ * Handle backend checkbox change
188
+ */
189
+ onBackendChange(): void {
190
+ this.applyBackendConfig();
191
+ }
192
+
193
+ /**
194
+ * Get count of selected backends
195
+ */
196
+ getSelectedBackendCount(): number {
197
+ return Object.values(this.selectedBackends).filter(v => v).length;
198
+ }
199
+
200
+ /**
201
+ * Get name of the single selected backend
202
+ */
203
+ getSelectedBackendName(): string {
204
+ const selectedId = Object.entries(this.selectedBackends)
205
+ .find(([_, enabled]) => enabled)?.[0];
206
+ return this.backendOptions.find(b => b.id === selectedId)?.name || 'Unknown';
207
+ }
208
+
209
+ /**
210
+ * Toggle backend settings visibility
211
+ */
212
+ toggleBackendSettings(): void {
213
+ this.showBackendSettings = !this.showBackendSettings;
214
+ }
215
+
216
+ /**
217
+ * Handle AWS Face Liveness toggle change
218
+ */
219
+ onAwsFaceLivenessChange(): void {
220
+ console.log('[IntroComponent] AWS Face Liveness toggled:', this.useAwsFaceLiveness);
221
+ this.awsFaceLivenessToggled.emit(this.useAwsFaceLiveness);
222
+ }
223
+
224
+ async ngOnInit() {
225
+ try {
226
+ // Load OpenPose model on component init
227
+ console.log('[Intro] Loading OpenPose model...');
228
+ await this.openposeService.loadModel();
229
+ console.log('[Intro] OpenPose model loaded successfully');
230
+
231
+ // Load custom poses from backend if available
232
+ if (this.customPoseRepo) {
233
+ this.customPoseRepo.loadCustomPoses().subscribe({
234
+ next: (response) => {
235
+ if (response.success && response.poses) {
236
+ // Merge custom poses with static poses
237
+ this.allPoses = mergeCustomPoses(response.poses);
238
+ console.log('[Intro] Loaded custom poses:', response.poses.length);
239
+ }
240
+ },
241
+ error: (error) => {
242
+ console.error('[Intro] Failed to load custom poses:', error);
243
+ // Continue with static poses only
244
+ this.allPoses = POSE_DEFINITIONS;
245
+ }
246
+ });
247
+ }
248
+
249
+ // Subscribe to pose selection changes
250
+ this.poseSelectionService.selectedPose$.subscribe(selectedPose => {
251
+ this.selectedPose = selectedPose;
252
+ });
253
+ } catch (error) {
254
+ console.error('[Intro] Failed to load OpenPose model:', error);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Handle mode change between browse and upload
260
+ */
261
+ onModeChange(event: any): void {
262
+ this.poseSelectionMode = event.detail.value;
263
+ console.log('[Intro] Pose selection mode changed to:', this.poseSelectionMode);
264
+ }
265
+
266
+ /**
267
+ * Handle drag over event
268
+ */
269
+ onDragOver(event: DragEvent): void {
270
+ event.preventDefault();
271
+ event.stopPropagation();
272
+ this.isDragging = true;
273
+ }
274
+
275
+ /**
276
+ * Handle drag leave event
277
+ */
278
+ onDragLeave(event: DragEvent): void {
279
+ event.preventDefault();
280
+ event.stopPropagation();
281
+ this.isDragging = false;
282
+ }
283
+
284
+ /**
285
+ * Handle file drop event
286
+ */
287
+ async onDrop(event: DragEvent): Promise<void> {
288
+ event.preventDefault();
289
+ event.stopPropagation();
290
+ this.isDragging = false;
291
+
292
+ const files = event.dataTransfer?.files;
293
+ if (!files || files.length === 0) {
294
+ return;
295
+ }
296
+
297
+ const file = files[0];
298
+
299
+ // Validate file type
300
+ if (!file.type.startsWith('image/')) {
301
+ this.poseDetectionStatus = {
302
+ success: false,
303
+ message: 'Please drop a valid image file'
304
+ };
305
+ return;
306
+ }
307
+
308
+ // Validate file size (max 10MB)
309
+ const maxSize = 10 * 1024 * 1024; // 10MB
310
+ if (file.size > maxSize) {
311
+ this.poseDetectionStatus = {
312
+ success: false,
313
+ message: 'Image file is too large. Maximum size is 10MB.'
314
+ };
315
+ return;
316
+ }
317
+
318
+ await this.processReferenceImage(file);
319
+ }
320
+
321
+ /**
322
+ * Handle file selection from file input element
323
+ */
324
+ async onFileSelected(event: Event): Promise<void> {
325
+ const input = event.target as HTMLInputElement;
326
+
327
+ if (!input.files || input.files.length === 0) {
328
+ return;
329
+ }
330
+
331
+ const file = input.files[0];
332
+
333
+ // Validate file type
334
+ if (!file.type.startsWith('image/')) {
335
+ this.poseDetectionStatus = {
336
+ success: false,
337
+ message: 'Please select a valid image file'
338
+ };
339
+ return;
340
+ }
341
+
342
+ // Validate file size (max 10MB)
343
+ const maxSize = 10 * 1024 * 1024; // 10MB
344
+ if (file.size > maxSize) {
345
+ this.poseDetectionStatus = {
346
+ success: false,
347
+ message: 'Image file is too large. Maximum size is 10MB.'
348
+ };
349
+ return;
350
+ }
351
+
352
+ await this.processReferenceImage(file);
353
+ }
354
+
355
+ /**
356
+ * Process uploaded reference image
357
+ */
358
+ private async processReferenceImage(file: File): Promise<void> {
359
+ try {
360
+ // Clear any selected pose from grid (mutual exclusivity)
361
+ if (this.selectedPose) {
362
+ this.clearPoseSelection();
363
+ }
364
+
365
+ this.isProcessingImage = true;
366
+ this.poseDetectionStatus = null;
367
+
368
+ console.log('[Intro] Processing reference image:', file.name);
369
+
370
+ // Extract pose from image
371
+ const referencePose = await this.referencePoseService.extractPoseFromImage(file);
372
+
373
+ // Store reference pose for later saving
374
+ this.referencePose = referencePose;
375
+
376
+ // Update UI
377
+ this.referencePoseImage = referencePose.imageData;
378
+ this.referencePoseDescription = referencePose.poseDescription;
379
+ this.poseDetectionStatus = {
380
+ success: true,
381
+ message: 'Pose detected successfully!'
382
+ };
383
+
384
+ // Emit event to parent component
385
+ this.referencePoseUploaded.emit(referencePose);
386
+
387
+ console.log('[Intro] Reference pose processed successfully');
388
+ } catch (error: any) {
389
+ console.error('[Intro] Failed to process reference image:', error);
390
+
391
+ this.poseDetectionStatus = {
392
+ success: false,
393
+ message: error.message || 'Failed to detect pose in the image'
394
+ };
395
+
396
+ this.referencePoseImage = null;
397
+ this.referencePoseDescription = null;
398
+ } finally {
399
+ this.isProcessingImage = false;
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Clear reference pose and revert to default
405
+ */
406
+ clearReferencePose(): void {
407
+ this.referencePoseImage = null;
408
+ this.referencePoseDescription = null;
409
+ this.poseDetectionStatus = null;
410
+ this.referencePose = null;
411
+ this.isSavingCustomPose = false; // Reset save flag
412
+ this.referencePoseService.clearReferencePose();
413
+ this.poseSelectionMode = 'browse'; // Switch back to browse mode
414
+
415
+ console.log('[Intro] Reference pose cleared');
416
+ }
417
+
418
+ /**
419
+ * Select a pose from the grid
420
+ */
421
+ onSelectPose(pose: PoseDefinition): void {
422
+ // Clear any uploaded custom pose (mutual exclusivity)
423
+ if (this.referencePoseImage) {
424
+ this.clearReferencePose();
425
+ }
426
+
427
+ this.poseSelectionService.selectPoseByObject(pose);
428
+ const selectedPoseInfo = this.poseSelectionService.getSelectedPose();
429
+
430
+ if (selectedPoseInfo) {
431
+ console.log('[Intro] Pose selected:', pose.name);
432
+ console.log('[Intro] Pose metadata:', {
433
+ id: pose.id,
434
+ category: pose.category,
435
+ detectionStrategy: pose.detectionStrategy,
436
+ difficulty: pose.difficulty
437
+ });
438
+
439
+ // Simply emit the pose selection
440
+ // The live-preview component will handle detection based on pose metadata
441
+ // The pose image is only used for display, not for OpenPose comparison
442
+ this.poseSelected.emit(selectedPoseInfo);
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Clear pose selection
448
+ */
449
+ clearPoseSelection(): void {
450
+ this.poseSelectionService.clearSelection();
451
+ this.selectedPose = null;
452
+ console.log('[Intro] Pose selection cleared');
453
+ }
454
+
455
+ /**
456
+ * Check if a pose is currently selected
457
+ */
458
+ isPoseSelected(pose: PoseDefinition): boolean {
459
+ return this.selectedPose?.pose.id === pose.id;
460
+ }
461
+
462
+ /**
463
+ * Toggle filters visibility
464
+ */
465
+ toggleFilters(): void {
466
+ this.showFilters = !this.showFilters;
467
+ }
468
+
469
+ /**
470
+ * Change view mode
471
+ */
472
+ setViewMode(mode: 'grid' | 'list'): void {
473
+ this.viewMode = mode;
474
+ }
475
+
476
+ /**
477
+ * Filter by category
478
+ */
479
+ filterByCategory(category: PoseDefinition['category'] | 'all'): void {
480
+ this.selectedCategory = category;
481
+ }
482
+
483
+ /**
484
+ * Filter by difficulty
485
+ */
486
+ filterByDifficulty(difficulty: PoseDefinition['difficulty'] | 'all'): void {
487
+ this.selectedDifficulty = difficulty;
488
+ }
489
+
490
+ /**
491
+ * Search poses
492
+ */
493
+ onSearchChange(event: any): void {
494
+ this.searchQuery = event.target.value || '';
495
+ }
496
+
497
+ /**
498
+ * Clear all filters
499
+ */
500
+ clearFilters(): void {
501
+ this.selectedCategory = 'all';
502
+ this.selectedDifficulty = 'all';
503
+ this.searchQuery = '';
504
+ }
505
+
506
+ /**
507
+ * Get icon for pose category
508
+ */
509
+ getCategoryIcon(category: PoseDefinition['category']): string {
510
+ return this.categoryInfo[category]?.icon || 'star-outline';
511
+ }
512
+
513
+ /**
514
+ * Get color for pose category
515
+ */
516
+ getCategoryColor(category: PoseDefinition['category']): string {
517
+ return this.categoryInfo[category]?.color || 'medium';
518
+ }
519
+
520
+ /**
521
+ * Get difficulty badge color
522
+ */
523
+ getDifficultyColor(difficulty: PoseDefinition['difficulty']): string {
524
+ const colors = {
525
+ easy: 'success',
526
+ medium: 'warning',
527
+ hard: 'danger'
528
+ };
529
+ return colors[difficulty] || 'medium';
530
+ }
531
+
532
+ /**
533
+ * Handle image load error (fallback to icon)
534
+ */
535
+ onImageError(event: any): void {
536
+ console.warn('[Intro] Failed to load pose image:', event.target.src);
537
+ event.target.style.display = 'none';
538
+ }
539
+
540
+ /**
541
+ * Save uploaded pose as custom pose
542
+ */
543
+ async onSaveCustomPose(): Promise<void> {
544
+ // Prevent double-submission
545
+ if (this.isSavingCustomPose) {
546
+ console.log('[Intro] Save already in progress, ignoring duplicate click');
547
+ return;
548
+ }
549
+
550
+ if (!this.referencePoseImage || !this.referencePose) {
551
+ await this.showToast('No pose to save', 'warning');
552
+ return;
553
+ }
554
+
555
+ if (!this.customPosesEnabled) {
556
+ await this.showToast('Custom pose saving not available', 'warning');
557
+ return;
558
+ }
559
+
560
+ try {
561
+ // Show dialog to get pose details
562
+ const poseDetails = await this.showSaveCustomPoseDialog();
563
+
564
+ if (!poseDetails) {
565
+ return; // User cancelled
566
+ }
567
+
568
+ // Set flag to prevent double-submission
569
+ this.isSavingCustomPose = true;
570
+
571
+ // Show loading indicator
572
+ const loading = await this.loadingCtrl.create({
573
+ message: 'Saving custom pose...',
574
+ spinner: 'circles'
575
+ });
576
+ await loading.present();
577
+
578
+ // Create custom pose via API
579
+ const { pose: customPose, isNew } = await this.customPoseRepo!.createCustomPoseFromBase64(
580
+ poseDetails.name,
581
+ poseDetails.description,
582
+ poseDetails.category,
583
+ poseDetails.difficulty,
584
+ this.referencePoseImage,
585
+ this.referencePose.keypoints
586
+ );
587
+
588
+ // Close loading indicator
589
+ await loading.dismiss();
590
+
591
+ if (isNew) {
592
+ // Convert to PoseDefinition and add to list
593
+ const poseDefinition = customPoseToPoseDefinition(customPose);
594
+ this.allPoses = [...this.allPoses, poseDefinition];
595
+ await this.showToast('Custom pose saved successfully!', 'success');
596
+ } else {
597
+ // Duplicate found - show warning with existing pose name
598
+ await this.showToast(`This pose already exists as "${customPose.name}"`, 'warning');
599
+ }
600
+
601
+ // Clear upload state
602
+ this.clearReferencePose();
603
+ } catch (error: any) {
604
+ console.error('[Intro] Failed to save custom pose:', error);
605
+ await this.showToast(error.message || 'Failed to save custom pose', 'danger');
606
+ } finally {
607
+ // Reset flag
608
+ this.isSavingCustomPose = false;
609
+ }
610
+ }
611
+
612
+ /**
613
+ * Delete custom pose
614
+ */
615
+ async onDeleteCustomPose(pose: PoseDefinition, event: Event): Promise<void> {
616
+ event.stopPropagation(); // Prevent pose selection
617
+
618
+ if (!pose.isCustom || !this.customPosesEnabled) {
619
+ return;
620
+ }
621
+
622
+ try {
623
+ // Show confirmation dialog
624
+ const confirmed = await this.showDeleteConfirmation(pose);
625
+
626
+ if (!confirmed) {
627
+ return;
628
+ }
629
+
630
+ // Show loading indicator
631
+ const loading = await this.loadingCtrl.create({
632
+ message: 'Deleting pose...',
633
+ spinner: 'circles'
634
+ });
635
+ await loading.present();
636
+
637
+ // Delete via API (remove the offset to get original ID)
638
+ const originalId = pose.id - 10000;
639
+ await this.customPoseRepo!.deleteCustomPose(originalId).toPromise();
640
+
641
+ // Remove from local list
642
+ this.allPoses = this.allPoses.filter(p => p.id !== pose.id);
643
+
644
+ // Close loading indicator
645
+ await loading.dismiss();
646
+
647
+ await this.showToast('Custom pose deleted', 'success');
648
+ } catch (error: any) {
649
+ console.error('[Intro] Failed to delete custom pose:', error);
650
+ await this.showToast(error.message || 'Failed to delete custom pose', 'danger');
651
+ }
652
+ }
653
+
654
+ /**
655
+ * Show save custom pose dialog
656
+ */
657
+ private async showSaveCustomPoseDialog(): Promise<any> {
658
+ const modal = await this.modalCtrl.create({
659
+ component: SaveCustomPoseDialogComponent,
660
+ componentProps: {
661
+ imageData: this.referencePoseImage,
662
+ suggestedDescription: this.referencePoseDescription
663
+ }
664
+ });
665
+
666
+ await modal.present();
667
+ const { data } = await modal.onWillDismiss();
668
+ return data;
669
+ }
670
+
671
+ /**
672
+ * Show delete confirmation dialog
673
+ */
674
+ private async showDeleteConfirmation(pose: PoseDefinition): Promise<boolean> {
675
+ const alert = await this.alertCtrl.create({
676
+ header: 'Delete Custom Pose',
677
+ message: `Are you sure you want to delete "${pose.name}"? This action cannot be undone.`,
678
+ buttons: [
679
+ {
680
+ text: 'Cancel',
681
+ role: 'cancel'
682
+ },
683
+ {
684
+ text: 'Delete',
685
+ role: 'destructive',
686
+ handler: () => true
687
+ }
688
+ ]
689
+ });
690
+
691
+ await alert.present();
692
+ const { role } = await alert.onDidDismiss();
693
+ return role !== 'cancel';
694
+ }
695
+
696
+ /**
697
+ * Show toast notification
698
+ */
699
+ private async showToast(message: string, color: string = 'primary'): Promise<void> {
700
+ const toast = await this.toastCtrl.create({
701
+ message,
702
+ duration: 3000,
703
+ color,
704
+ position: 'bottom'
705
+ });
706
+ await toast.present();
707
+ }
708
+
709
+ onStart(): void {
710
+ // Check if a pose is selected
711
+ if (!this.selectedPose) {
712
+ console.warn('[Intro] No pose selected, starting with default actions');
713
+ }
714
+
715
+ this.startClicked.emit();
716
+ }
717
+
718
+ onCancel(): void {
719
+ this.cancelClicked.emit();
720
+ }
721
+ }