Browse Source

feat : face

202226701008 3 months ago
parent
commit
fbc222810a

+ 404 - 11
AIart-app/package-lock.json

@@ -22,6 +22,12 @@
         "@capacitor/keyboard": "6.0.3",
         "@capacitor/status-bar": "6.0.2",
         "@ionic/angular": "^8.0.0",
+        "@tensorflow/tfjs": "^4.22.0",
+        "@tensorflow/tfjs-backend-wasm": "^4.22.0",
+        "@tensorflow/tfjs-backend-webgl": "^4.22.0",
+        "@tensorflow/tfjs-backend-webgpu": "^4.22.0",
+        "@tensorflow/tfjs-core": "^4.22.0",
+        "@vladmandic/face-api": "^1.7.14",
         "fmode-ng": "^0.0.63",
         "ionicons": "^7.2.1",
         "markmap-lib": "^0.17.2",
@@ -5822,6 +5828,260 @@
         "npm": ">=7.10.0"
       }
     },
+    "node_modules/@tensorflow/tfjs": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs/-/tfjs-4.22.0.tgz",
+      "integrity": "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@tensorflow/tfjs-backend-cpu": "4.22.0",
+        "@tensorflow/tfjs-backend-webgl": "4.22.0",
+        "@tensorflow/tfjs-converter": "4.22.0",
+        "@tensorflow/tfjs-core": "4.22.0",
+        "@tensorflow/tfjs-data": "4.22.0",
+        "@tensorflow/tfjs-layers": "4.22.0",
+        "argparse": "^1.0.10",
+        "chalk": "^4.1.0",
+        "core-js": "3.29.1",
+        "regenerator-runtime": "^0.13.5",
+        "yargs": "^16.0.3"
+      },
+      "bin": {
+        "tfjs-custom-module": "dist/tools/custom_module/cli.js"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-backend-cpu": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.22.0.tgz",
+      "integrity": "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/seedrandom": "^2.4.28",
+        "seedrandom": "^3.0.5"
+      },
+      "engines": {
+        "yarn": ">= 1.3.2"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-backend-wasm": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-backend-wasm/-/tfjs-backend-wasm-4.22.0.tgz",
+      "integrity": "sha512-/IYhReRIp4jg/wYW0OwbbJZG8ON87mbz0PgkiP3CdcACRSvUN0h8rvC0O3YcDtkTQtFWF/tcXq/KlVDyV49wmA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@tensorflow/tfjs-backend-cpu": "4.22.0",
+        "@types/emscripten": "~0.0.34"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-backend-webgl": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.22.0.tgz",
+      "integrity": "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@tensorflow/tfjs-backend-cpu": "4.22.0",
+        "@types/offscreencanvas": "~2019.3.0",
+        "@types/seedrandom": "^2.4.28",
+        "seedrandom": "^3.0.5"
+      },
+      "engines": {
+        "yarn": ">= 1.3.2"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-backend-webgpu": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-backend-webgpu/-/tfjs-backend-webgpu-4.22.0.tgz",
+      "integrity": "sha512-lvIc7Af4Tl2BCdYp43iQmSCRq3asaKT0q2xaErphXiUZ+jqeB0bQa0ZvQys1Xatvto0U4/c90DVsHPfvkn5ftg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@tensorflow/tfjs-backend-cpu": "4.22.0"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-converter": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-converter/-/tfjs-converter-4.22.0.tgz",
+      "integrity": "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ==",
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-core": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-core/-/tfjs-core-4.22.0.tgz",
+      "integrity": "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/long": "^4.0.1",
+        "@types/offscreencanvas": "~2019.7.0",
+        "@types/seedrandom": "^2.4.28",
+        "@webgpu/types": "0.1.38",
+        "long": "4.0.0",
+        "node-fetch": "~2.6.1",
+        "seedrandom": "^3.0.5"
+      },
+      "engines": {
+        "yarn": ">= 1.3.2"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": {
+      "version": "2019.7.3",
+      "resolved": "https://registry.npmmirror.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
+      "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
+      "license": "MIT"
+    },
+    "node_modules/@tensorflow/tfjs-data": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-data/-/tfjs-data-4.22.0.tgz",
+      "integrity": "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/node-fetch": "^2.1.2",
+        "node-fetch": "~2.6.1",
+        "string_decoder": "^1.3.0"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0",
+        "seedrandom": "^3.0.5"
+      }
+    },
+    "node_modules/@tensorflow/tfjs-layers": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmmirror.com/@tensorflow/tfjs-layers/-/tfjs-layers-4.22.0.tgz",
+      "integrity": "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA==",
+      "license": "Apache-2.0 AND MIT",
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "license": "MIT",
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/core-js": {
+      "version": "3.29.1",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.29.1.tgz",
+      "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT"
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "license": "MIT"
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/@tufjs/canonical-json": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
@@ -6191,6 +6451,12 @@
         "@types/d3-selection": "*"
       }
     },
+    "node_modules/@types/emscripten": {
+      "version": "0.0.34",
+      "resolved": "https://registry.npmmirror.com/@types/emscripten/-/emscripten-0.0.34.tgz",
+      "integrity": "sha512-QSb9ojDincskc+uKMI0KXp8e1NALFINCrMlp8VGKGcTSxeEyRTTKyjWw75NYrCZHUsVEEEpr1tYHpbtaC++/sQ==",
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.5",
       "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
@@ -6298,6 +6564,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/long": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/@types/long/-/long-4.0.2.tgz",
+      "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
+      "license": "MIT"
+    },
     "node_modules/@types/markdown-it": {
       "version": "14.1.2",
       "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.2.tgz",
@@ -6342,6 +6614,16 @@
         "undici-types": "~6.19.8"
       }
     },
+    "node_modules/@types/node-fetch": {
+      "version": "2.6.12",
+      "resolved": "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.12.tgz",
+      "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "form-data": "^4.0.0"
+      }
+    },
     "node_modules/@types/node-forge": {
       "version": "1.3.11",
       "resolved": "https://registry.npmmirror.com/@types/node-forge/-/node-forge-1.3.11.tgz",
@@ -6352,6 +6634,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/offscreencanvas": {
+      "version": "2019.3.0",
+      "resolved": "https://registry.npmmirror.com/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz",
+      "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==",
+      "license": "MIT"
+    },
     "node_modules/@types/parse": {
       "version": "3.0.9",
       "resolved": "https://registry.npmmirror.com/@types/parse/-/parse-3.0.9.tgz",
@@ -6383,6 +6671,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/seedrandom": {
+      "version": "2.4.34",
+      "resolved": "https://registry.npmmirror.com/@types/seedrandom/-/seedrandom-2.4.34.tgz",
+      "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==",
+      "license": "MIT"
+    },
     "node_modules/@types/semver": {
       "version": "7.5.8",
       "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.8.tgz",
@@ -6945,6 +7239,15 @@
         "vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
       }
     },
+    "node_modules/@vladmandic/face-api": {
+      "version": "1.7.14",
+      "resolved": "https://registry.npmmirror.com/@vladmandic/face-api/-/face-api-1.7.14.tgz",
+      "integrity": "sha512-WTechvIQ+t7JS7ASQ2n1XaTCNSXQiqdTQmtWAuGrpClAIHIP18FVV66dPWDA8/0XIdotbWnzGjuS3WzybxVlJw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.14.1",
       "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -7106,6 +7409,12 @@
         "@xtuc/long": "4.2.2"
       }
     },
+    "node_modules/@webgpu/types": {
+      "version": "0.1.38",
+      "resolved": "https://registry.npmmirror.com/@webgpu/types/-/types-0.1.38.tgz",
+      "integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==",
+      "license": "BSD-3-Clause"
+    },
     "node_modules/@xmldom/xmldom": {
       "version": "0.8.10",
       "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
@@ -7571,6 +7880,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
     "node_modules/at-least-node": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
@@ -8186,7 +8501,6 @@
       "version": "4.1.2",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
       "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.1.0",
@@ -8506,6 +8820,18 @@
         "node": ">=0.1.90"
       }
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/commander": {
       "version": "2.20.3",
       "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
@@ -9642,6 +9968,15 @@
         "robust-predicates": "^3.0.2"
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@@ -9873,7 +10208,6 @@
       "version": "0.1.13",
       "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz",
       "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "dependencies": {
@@ -9884,7 +10218,6 @@
       "version": "0.6.3",
       "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
       "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "dependencies": {
@@ -10223,7 +10556,6 @@
       "version": "3.2.0",
       "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
       "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -11280,6 +11612,20 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
@@ -11658,7 +12004,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -14039,6 +14384,12 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
     "node_modules/lru-cache": {
       "version": "5.1.1",
       "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -14500,7 +14851,6 @@
       "version": "1.52.0",
       "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.6"
@@ -14510,7 +14860,6 @@
       "version": "2.1.35",
       "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "mime-db": "1.52.0"
@@ -15041,6 +15390,26 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/node-fetch": {
+      "version": "2.6.13",
+      "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.13.tgz",
+      "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/node-forge": {
       "version": "1.3.1",
       "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz",
@@ -17281,7 +17650,6 @@
       "version": "5.2.1",
       "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -17426,6 +17794,12 @@
         }
       }
     },
+    "node_modules/seedrandom": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/seedrandom/-/seedrandom-3.0.5.tgz",
+      "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
+      "license": "MIT"
+    },
     "node_modules/select-hose": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
@@ -18176,7 +18550,6 @@
       "version": "1.3.0",
       "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
       "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "safe-buffer": "~5.2.0"
@@ -18377,7 +18750,6 @@
       "version": "7.2.0",
       "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "has-flag": "^4.0.0"
@@ -18709,6 +19081,12 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "license": "MIT"
+    },
     "node_modules/tree-dump": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/tree-dump/-/tree-dump-1.0.2.tgz",
@@ -19806,6 +20184,12 @@
       "integrity": "sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==",
       "license": "Apache-2.0"
     },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "license": "BSD-2-Clause"
+    },
     "node_modules/webpack": {
       "version": "5.94.0",
       "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.94.0.tgz",
@@ -20177,6 +20561,16 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
     "node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
@@ -20488,7 +20882,6 @@
       "version": "5.0.8",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
       "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=10"

+ 6 - 0
AIart-app/package.json

@@ -27,6 +27,12 @@
     "@capacitor/keyboard": "6.0.3",
     "@capacitor/status-bar": "6.0.2",
     "@ionic/angular": "^8.0.0",
+    "@tensorflow/tfjs": "^4.22.0",
+    "@tensorflow/tfjs-backend-wasm": "^4.22.0",
+    "@tensorflow/tfjs-backend-webgl": "^4.22.0",
+    "@tensorflow/tfjs-backend-webgpu": "^4.22.0",
+    "@tensorflow/tfjs-core": "^4.22.0",
+    "@vladmandic/face-api": "^1.7.14",
     "fmode-ng": "^0.0.63",
     "ionicons": "^7.2.1",
     "markmap-lib": "^0.17.2",

+ 3 - 1
AIart-app/src/app/app.routes.ts

@@ -20,5 +20,7 @@ export const routes: Routes = [
     path: 'chat/pro/chat/:chatId',
     redirectTo: '/chat/session/chat/:chatId',
     pathMatch: 'full'
-  }
+  },
+  
+
 ];

+ 12 - 0
AIart-app/src/app/modules/face/page-feat68/ReadMe.md

@@ -0,0 +1,12 @@
+
+# 依赖安装
+``` bash
+npm i -S @vladmandic/face-api
+npm i -S @tensorflow/tfjs
+npm i -S @tensorflow/tfjs-core @tensorflow/tfjs-core @tensorflow/tfjs-backend-webgpu @tensorflow/tfjs-backend-webgl @tensorflow/tfjs-backend-wasm
+```
+- tsconfig.json
+``` ts
+    "skipLibCheck": true,
+```
+

+ 194 - 0
AIart-app/src/app/modules/face/page-feat68/face-api.service.ts

@@ -0,0 +1,194 @@
+import { Injectable } from '@angular/core';
+import {
+  Platform,
+  ToastController,
+} from "@ionic/angular"
+// import * as faceapi from "face-api.js"
+// import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
+
+import * as tf from '@tensorflow/tfjs-core';
+import '@tensorflow/tfjs-backend-webgpu';
+import '@tensorflow/tfjs-backend-webgl';
+import '@tensorflow/tfjs-backend-wasm';
+import * as faceapi from '@vladmandic/face-api';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class FaceApiService {
+
+  isLoaded:boolean = false;
+  isLoading:boolean = false;
+  constructor(
+    public toastController: ToastController,
+    private platform:Platform,
+  ) { }
+
+  async loadImg(url:string):Promise<HTMLImageElement|null>{
+    return new Promise(async (resolve,reject)=>{
+      let img = document.createElement("img");
+      img.crossOrigin = '*';
+      img.src = url
+      img.onload = async (data)=>{
+        try{
+          let startDeect = new Date();
+          resolve(img)
+        }catch(err){
+          console.log(err)
+          reject(null)
+        }
+      }
+      img.onerror = (err)=>{
+        reject(null)
+      }
+    })
+  }
+
+  mathFace(detect1:any,detect2:any){
+    
+    let faceMatcher = new faceapi.FaceMatcher([detect1])
+
+    let bestMatch:any;
+    let label="",distance=1,score=0;
+      
+    bestMatch = faceMatcher.findBestMatch(detect2.descriptor)
+    console.log("bestMatch",bestMatch)
+    if(bestMatch?._label.startsWith("person")){
+        label = bestMatch._label;
+        distance = bestMatch._distance;
+    };
+
+    return {distance,score:1-distance,label};
+  }
+
+  async getFaceDetections(photo:string,faceimg:HTMLImageElement){
+    this.isLoading = false;
+    await this.loadFaceModule(true);
+
+    let canvas = faceapi.createCanvasFromMedia(faceimg)
+    let referenceImage = await this.loadImg(photo);
+    if(!(referenceImage)){
+        console.log("图片不够清晰请重新上传")
+        return null;
+    }
+
+    console.log("referenceImage",referenceImage)
+    let detections
+    try{
+      detections = await faceapi.detectAllFaces(referenceImage)
+      .withFaceLandmarks()
+      .withFaceDescriptors()
+      .withFaceExpressions()
+    }catch(err){
+      console.error(err)
+    }
+    console.log("detections",detections)
+
+    if(!detections){
+        console.log("图片不够清晰请重新上传")
+        return null;
+    }
+    if(detections.length==0){
+        // openapi.goWrong(response,"照片未上传或不清晰,请重新上传");
+        let imgstr = ""
+        if(detections.length==0){
+            imgstr += "<1>"
+        }
+        console.log(`图${imgstr}无法识别人脸`)
+        return null;
+    }
+
+    faceapi.draw.drawDetections(canvas,detections)
+    faceapi.draw.drawFaceLandmarks(canvas,detections)
+    faceapi.draw.drawFaceExpressions(canvas,detections)
+    faceimg.src = canvas.toDataURL()
+
+    // 获取特征向量 68关键点 以及128特征向量
+    return detections
+  }
+  async detectAllFaces(input: faceapi.TNetInput, options?: faceapi.FaceDetectionOptions){
+    console.log(input,this.isLoaded)
+    if(!this.isLoaded){
+      return [
+          {_score:0}
+      ]
+    }
+    return await faceapi?.detectAllFaces(input,options)//.withFaceLandmarks(true);
+  }
+  /**
+   * 加载FaceApis模型库
+   * @param needAll 是否加载完整模型
+   * @returns 
+   * @desc
+   * 移动端,需要通过FileSystem获取文件本地Uri传入load加载
+   * adb shell 进入文件夹进行检索 data/user/0/com.nova.app/files => assets 被集成在apk的二进制包中,无法从文件目录找到
+   * file:///android_asset/ 读取目录,逐层寻找assets目录 => 
+   * 快速打包测试方法
+   * ng build nova-mirror
+   * cp -rf dist/nova-mirror/* www/
+   * ionic cap run android --project=nova-mirror
+   * 暂时:通过HTTPS线上加载模型资源
+   * @see
+   * 其他替代库:https://docs.regulaforensics.com/develop/face-sdk/mobile/face-detection/basic-detection/
+   * 其他替代库:https://docs.regulaforensics.com/develop/face-sdk/mobile/installation/ionic/
+   */
+  async loadFaceModule(needAll=false){
+    await this.setBackend()
+    let prefix = ""
+    if(this.platform.is("capacitor")) prefix = "https://pwa.fmode.cn/"
+    if(this.isLoading) return;
+    this.isLoading = true
+    // 仅加载面部识别Model库
+    await faceapi.nets.ssdMobilenetv1.loadFromUri(prefix+"assets/face/models")
+    if(needAll){
+      await faceapi.nets.faceRecognitionNet.loadFromUri(prefix+"assets/face/models")
+      // await faceapi.nets.tinyFaceDetector.loadFromUri(prefix+"assets/face/models")
+      await faceapi.nets.faceLandmark68Net.loadFromUri(prefix+"assets/face/models")
+      await faceapi.nets.faceExpressionNet.loadFromUri(prefix+"assets/face/models")
+      // await faceapi.nets.faceLandmark68TinyNet.loadFromUri(prefix+"assets/face/models")
+    }
+    this.isLoaded = true
+    // this.toast("DETECT"+ new Date(),)
+    return
+  }
+  async setBackend(){
+
+        let backend
+        let WebGPU = (navigator as any).gpu
+        if (WebGPU) {
+          // WebGPU is supported
+          // console.log(WebGPU)
+          backend = "webgpu"
+        } else {
+          // WebGPU is not supported
+        }
+        let glcanvas = document.createElement('canvas');
+        let WebGL = glcanvas.getContext('webgl') || glcanvas.getContext('experimental-webgl');
+        if (WebGL) {
+          // console.log(WebGL)
+          // WebGL is supported
+          if(!backend) backend = "webgl"
+        } else {
+          // WebGL is not supported
+        }
+
+        if (typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function') {
+          // WebAssembly is supported
+          // console.log(WebAssembly)
+          if(!backend) backend = "wasm"
+        } else {
+          // WebAssembly is not supported
+        }
+
+        backend&&await tf.setBackend(backend);
+        await tf.ready();
+        return
+  }
+  async toast(msg:any){
+    let toast = await this.toastController.create({
+      message: String(msg),
+      duration: 1000,
+    })
+    toast.present()
+  }
+}

+ 90 - 0
AIart-app/src/app/modules/face/page-feat68/page-feat68.component.html

@@ -0,0 +1,90 @@
+<ion-content>
+  <ion-segment [value]="tab"  (ionChange)="tabChange($event)">
+    <ion-segment-button value="models">
+      <ion-label>模型加载</ion-label>
+    </ion-segment-button>
+    <ion-segment-button value="feat68">
+      <ion-label>特征提取</ion-label>
+    </ion-segment-button>
+    <ion-segment-button value="match">
+      <ion-label>相似对比</ion-label>
+    </ion-segment-button>
+  </ion-segment>
+  @if(tab=="match"){
+    <ion-button (click)="matchFaces()">开始跑分</ion-button>
+    @for(face of faceListSelect;track face.photo){
+      <div style="display: flex;flex-direction: column;">
+        <div style="display: flex;">
+          <img style="width: 60%;" [src]="face?.photo" [alt]="face?.title">
+          <div style="flex:1;display: flex; flex-direction: column;">
+            @if(face?.distance||face?.score){
+              <div>差异性 {{face?.distance}}</div>
+              <div>相似度 {{(face?.score)*100}}%</div>
+            }
+          </div>
+        </div>
+
+      </div>
+    }
+  }
+
+  @if(tab=="feat68"){
+    @for(face of faceList;track face.photo){
+      <div style="display: flex;flex-direction: column;">
+        <div style="display: flex;">
+          <img #faceimg style="width: 70%;" [src]="face?.photo" [alt]="face?.title">
+          <div style="flex:1;display: flex; flex-direction: column;">
+            <ion-button (click)="getFeat68(face,faceimg)">特征向量</ion-button>
+            @if(face?.feat68){
+              <div>长度 {{face?.feat68?.length}}</div>
+              <ion-button (click)="selectFace(face)">选中</ion-button>
+            }
+          </div>
+        </div>
+
+      </div>
+    }
+  }
+
+  @if(tab=="models"){
+    <div class="test-card">
+      <h1>精简库加载测试</h1>
+      <h2 *ngIf="!timeMap['faceload']">...加载中...</h2>
+      <ng-container *ngIf="timeMap['faceload']">
+        <h2>加载时间:{{timeMap['faceload']}}秒</h2>
+        <button (click)="loadFaceApi()">重新测试</button>
+      </ng-container>
+    </div>
+
+    <div class="test-card">
+      <h1>完整库加载测试</h1>
+      <h2 *ngIf="!timeMap['faceloadall']">...加载中...</h2>
+      <ng-container *ngIf="timeMap['faceloadall']">
+        <h2>加载时间:{{timeMap['faceloadall']}}秒</h2>
+        <button (click)="loadFaceApi('faceloadall')">重新测试</button>
+      </ng-container>
+    </div>
+
+    <div class="test-card">
+      <h1>单图识别速度测试</h1>
+      <h2 *ngIf="!timeMap['imgload']">...图片加载中...</h2>
+      <ng-container *ngIf="timeMap['imgload']">
+        <h2>图片加载:{{timeMap['imgload']}}秒</h2>
+      </ng-container>
+
+      <h2 *ngIf="!timeMap['detect']">...人脸识别中...</h2>
+      <ng-container *ngIf="timeMap['detect']">
+        <h2>人脸识别:{{timeMap['detect']}}秒</h2>
+        <button (click)="detectFace()">重新测试</button>
+      </ng-container>
+      <button *ngIf="detectTimeout" (click)="detectFace()">重新测试</button>
+
+    </div>
+
+    <div style="text-align: center;">
+      <button style="width: 80vw;" (click)="doTest()">开始测试</button>
+      <button style="width: 80vw;" (click)="back()">返回</button>
+    </div>
+  }
+
+</ion-content>

+ 15 - 0
AIart-app/src/app/modules/face/page-feat68/page-feat68.component.scss

@@ -0,0 +1,15 @@
+.test-card{
+    margin:2vw;
+    padding:2vw;
+    text-align: center;
+    border: solid 1px;
+    display: flex;
+    flex-wrap: wrap;
+    h1,h2,button{
+        flex:100%;
+    }
+}
+button{
+    height: 7vw;
+    font-size: 5vw;
+}

+ 22 - 0
AIart-app/src/app/modules/face/page-feat68/page-feat68.component.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { PageFeat68Component } from './page-feat68.component';
+
+describe('PageFeat68Component', () => {
+  let component: PageFeat68Component;
+  let fixture: ComponentFixture<PageFeat68Component>;
+
+  beforeEach(waitForAsync(() => {
+    TestBed.configureTestingModule({
+      imports: [PageFeat68Component],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(PageFeat68Component);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 167 - 0
AIart-app/src/app/modules/face/page-feat68/page-feat68.component.ts

@@ -0,0 +1,167 @@
+import { Component, OnInit } from '@angular/core';
+import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
+import { Router } from '@angular/router';
+import { FaceApiService } from '../page-feat68/face-api.service';
+import { CommonModule } from '@angular/common';
+    
+import { IonContent,IonButton,IonSegment,IonSegmentButton,IonLabel } from "@ionic/angular/standalone";
+
+@Component({
+  selector: 'app-page-feat68',
+  templateUrl: './page-feat68.component.html',
+  styleUrls: ['./page-feat68.component.scss'],
+  standalone: true,
+  imports:[
+    CommonModule,
+    IonContent,IonButton,
+    IonSegment,IonSegmentButton,IonLabel
+    
+  ]
+})
+export class PageFeat68Component  implements OnInit {
+
+  tab:string = "models"
+  tabChange(ev:any){
+    this.tab = ev.detail.value
+  }
+  constructor(private faceServ:FaceApiService,private dom:DomSanitizer,
+    private router: Router,
+    ) { }
+
+  ngOnInit() {
+    this.doTest();
+  }
+  
+  back(){
+    this.router.navigate(["/mirror/login"])
+  }
+
+
+  /**
+   * 特征提取:测试人脸面部68特征向量
+   */
+  faceList:Array<any> = [
+    {title:"lyf1",photo:"assets/face/lyf1.jpg"},
+    {title:"lyf2",photo:"assets/face/lyf2.jpg"},
+    {title:"ym11",photo:"assets/face/ym1.jpg"},
+    {title:"ym2",photo:"assets/face/ym2.jpg"},
+    {title:"gtl1",photo:"assets/face/gtl1.jpg"},
+    {title:"gtl2",photo:"assets/face/gtl2.jpg"},
+    {title:"gtl3",photo:"assets/face/gtl3.jpg"},
+  ]
+  async getFeat68(face:any,faceimg:HTMLImageElement){
+    let detections = await this.faceServ.getFaceDetections(face?.photo,faceimg)
+    if(detections){
+      face.detect = detections?.[0];
+      face.feat68 = detections?.[0].descriptor;
+      console.log(face.feat68)
+    }
+  }
+  faceListSelect:Array<any> = []
+  selectFace(face:any){
+    let idx = this.faceListSelect.findIndex(item=>item.photo == face.photo)
+    if(idx>-1){
+      this.faceListSelect.splice(idx,1);
+    }else{
+      this.faceListSelect.push(face);
+    }
+  }
+  matchFaces(){
+    let first = this.faceListSelect[0];
+    if(first){
+      this.faceListSelect.forEach(face=>{
+        let matchRes = this.faceServ.mathFace(first.detect,face.detect)
+        face.distance = matchRes.distance
+        face.score = matchRes.score
+      })
+    }
+  }
+
+  /**
+   * 模型加载:测试加载模型效率
+   */
+  async doTest(){
+    await this.loadFaceApi()
+    // await this.loadFaceApi('faceloadall')
+    await this.detectFace()
+  }
+  timeMap:any = {
+    faceload:null
+  }
+  async loadFaceApi(type='faceload') {
+    let start = new Date();
+    this.timeMap[type] = null;
+    this.faceServ.isLoaded = false;
+    this.faceServ.isLoading = false;
+    if(type=="faceloadall"){
+      await this.faceServ.loadFaceModule(true);
+    }else{
+      await this.faceServ.loadFaceModule();
+    }
+    this.timeMap[type] = ((new Date()).getTime() - start.getTime())/1000;
+  }
+  detectTimeout = false;
+  async detectFace(onlyimg:boolean=false){
+    this.detectTimeout = false;
+    this.wait(5000).then(()=>{
+      this.detectTimeout = true;
+    })
+    return new Promise(async (resolve,reject)=>{
+      let startImg = new Date();
+      let img = document.createElement("img");
+      // (img as any).src = this.dom.bypassSecurityTrustResourceUrl("https://file-cloud.fmode.cn/khgbeQmvYZ/20230522/crv61q070604117.png")
+      img.crossOrigin = '*';
+      img.src = "https://file-cloud.fmode.cn/khgbeQmvYZ/20230522/crv61q070604117.png"
+      // img.src = await this.getBase64("https://file-cloud.fmode.cn/khgbeQmvYZ/20230522/crv61q070604117.png")
+      img.onload = async (data)=>{
+        try{
+          let startDetect = new Date();
+          this.timeMap['imgload'] = ((new Date()).getTime() - startImg.getTime())/1000;
+          if(onlyimg) resolve(true)
+          let detections: any = await this.faceServ.detectAllFaces(img)
+          console.log(detections[0]?._score)
+          this.timeMap['detect'] = ((new Date()).getTime() - startDetect.getTime())/1000;
+          resolve(true)
+        }catch(err){
+          console.log(err)
+          reject(err)
+        }
+      }
+      img.onerror = (err)=>{
+        console.log(err)
+        reject(err)
+      }
+    })
+  }
+
+  async getBase64(imageUrl:string):Promise<string>{
+    console.log("fetchres")
+    let response = await fetch(imageUrl)
+    let blob = await response.blob()
+    return new Promise((resolve)=>{
+      let oFileReader = new FileReader();
+      console.log("loadfile")
+      oFileReader.onabort = e=>{
+        console.log(e)
+      }
+      oFileReader.onloadend = (e:any)=>{
+        console.log("loaded")
+        let base64 = e.target.result;
+        //项目用的是react,这里的this.canvasImage是定义在组件内的
+        resolve(base64)
+      };
+      oFileReader.onerror = (err)=>{
+        console.log("error")
+        console.log(err)
+      }
+      oFileReader.readAsDataURL(blob);
+    })
+  };
+  wait(timeout:number){
+    return new Promise((resolve,reject)=>{
+      setTimeout(() => {
+        resolve(true);
+      }, timeout);
+    })
+  }
+}