FaceTracking.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <template>
  2. <div></div>
  3. </template>
  4. <script>
  5. import * as faceapi from "face-api.js";
  6. import { FACE_API_MODEL_PATH } from "@/constants/constants";
  7. window.faceapi = faceapi;
  8. const os = (function() {
  9. const ua = navigator.userAgent.toLowerCase();
  10. return {
  11. isWin2K: /windows nt 5.0/.test(ua),
  12. isXP: /windows nt 5.1/.test(ua),
  13. isVista: /windows nt 6.0/.test(ua),
  14. isWin7: /windows nt 6.1/.test(ua),
  15. isWin8: /windows nt 6.2/.test(ua),
  16. isWin81: /windows nt 6.3/.test(ua),
  17. isWin10: /windows nt 10.0/.test(ua),
  18. };
  19. })();
  20. let __cache4WebglAvailable = null;
  21. function webgl_available() {
  22. if (__cache4WebglAvailable) return __cache4WebglAvailable;
  23. var canvas = document.createElement("canvas");
  24. var gl = canvas.getContext("webgl");
  25. __cache4WebglAvailable = gl && gl instanceof WebGLRenderingContext;
  26. return __cache4WebglAvailable;
  27. }
  28. let __cache4TensorFlowWebPackStatus = null;
  29. function tensorFlowWebPackStatus() {
  30. if (__cache4TensorFlowWebPackStatus) return __cache4TensorFlowWebPackStatus;
  31. __cache4TensorFlowWebPackStatus = faceapi.tf.ENV.get("WEBGL_PACK");
  32. return __cache4TensorFlowWebPackStatus;
  33. }
  34. function getCPUModel() {
  35. if (typeof nodeRequire != "undefined") {
  36. var os = window.nodeRequire("os");
  37. const cpus = os.cpus();
  38. if (cpus.length > 0) {
  39. return cpus[0].model;
  40. }
  41. }
  42. return "null";
  43. }
  44. // if (os.isWin7) alert("是win7");
  45. // if (os.isWin10) alert("是win10");
  46. function getFaceDetectorOptions() {
  47. let inputSize = 320;
  48. if (os.isWin7) {
  49. inputSize = 256; // 在win7上无bug,速度快,效果较好
  50. } else if (os.isWin10) {
  51. inputSize = 320; // 在win10上,效果较好
  52. }
  53. window.____hideMe =
  54. window.____hideMe ||
  55. new faceapi.TinyFaceDetectorOptions({
  56. inputSize: inputSize,
  57. scoreThreshold: 0.5,
  58. });
  59. return window.____hideMe;
  60. // return new faceapi.SsdMobilenetv1Options({ minConfidence: 0.8 });
  61. // return new faceapi.MtcnnOptions({ minFaceSize: 200, scaleFactor: 0.8 });
  62. }
  63. const detectTimeArray = [];
  64. export default {
  65. name: "FaceTracking",
  66. async created() {
  67. await faceapi.nets.tinyFaceDetector.load(FACE_API_MODEL_PATH);
  68. // faceapi.nets.faceRecognitionNet.load(modelsPath);
  69. await faceapi.loadFaceLandmarkModel(FACE_API_MODEL_PATH);
  70. faceapi.tf.ENV.set("WEBGL_PACK", false);
  71. },
  72. async mounted() {
  73. let trackStarted = false;
  74. const that = this;
  75. async function trackHead() {
  76. const video = document.getElementById("video");
  77. if (
  78. video &&
  79. video.readyState === 4 &&
  80. faceapi.nets.tinyFaceDetector.params
  81. ) {
  82. trackStarted = true;
  83. } else {
  84. return;
  85. }
  86. console.log("start tracking ... ");
  87. await that.detectFaces();
  88. }
  89. this.trackHeadInterval = setInterval(() => {
  90. if (trackStarted) {
  91. clearInterval(this.trackHeadInterval);
  92. } else {
  93. trackHead();
  94. }
  95. }, 1000);
  96. },
  97. beforeDestroy() {
  98. clearInterval(this.trackHeadInterval);
  99. clearTimeout(this.warningTimeout);
  100. clearTimeout(this.detectFacesTimeout);
  101. },
  102. methods: {
  103. async detectFaces() {
  104. this.singleTimeUsage = this.singleTimeUsage || 0;
  105. this.multipleTimeUsage = this.multipleTimeUsage || 0;
  106. if (
  107. this.singleTimeUsage > 10 * 1000 ||
  108. this.multipleTimeUsage > 3 * 1000
  109. ) {
  110. window._hmt.push([
  111. "_trackEvent",
  112. "正在考试页面",
  113. "关闭实时人脸检测,因为耗时过长",
  114. ]);
  115. return;
  116. }
  117. const detectStartTime = performance.now();
  118. const videoEl = document.getElementById("video");
  119. // this.___vWidth =
  120. // this.___vWidth ||
  121. // document.getElementById("video-container").clientWidth;
  122. const options = getFaceDetectorOptions();
  123. let result;
  124. try {
  125. result = await faceapi
  126. // .detectSingleFace(videoEl, options)
  127. .detectAllFaces(videoEl, options);
  128. } catch (e) {
  129. window._hmt.push(["_trackEvent", "正在考试页面", "实时人脸检测失败"]);
  130. throw e;
  131. }
  132. // console.log(result);
  133. const detectEndTime = performance.now();
  134. // console.log("WebGL: ", faceapi.tf.ENV.get("WEBGL_PACK"));
  135. console.log(
  136. "WebGL: ",
  137. webgl_available(),
  138. " WEBGL_PACK: ",
  139. tensorFlowWebPackStatus(),
  140. " single detect time: ",
  141. detectEndTime - detectStartTime,
  142. " result: ",
  143. result.length
  144. );
  145. this.singleTimeUsage = detectEndTime - detectStartTime;
  146. if (detectTimeArray.length < 11) {
  147. // 仅捕获一部分检测次数
  148. detectTimeArray.push(detectEndTime - detectStartTime);
  149. }
  150. if (detectTimeArray.length === 11) {
  151. detectTimeArray.shift();
  152. const avg =
  153. detectTimeArray.reduce((a, b) => a + b, 0) / detectTimeArray.length;
  154. const roundAvg = Math.round(avg / 100) * 100;
  155. window._hmt.push([
  156. "_trackEvent",
  157. "正在考试页面",
  158. "实时人脸检测平均时长",
  159. roundAvg + "ms",
  160. ]);
  161. console.log(detectTimeArray);
  162. detectTimeArray.push(0, 0); // 避免再次达到push条件和上传条件
  163. const roundAvg100 = Math.round(avg / 100) * 100;
  164. const osType = os.isWin7 ? "win7" : os.isWin10 ? "win10" : "other";
  165. const stats = `webgl: ${webgl_available()}; tf_backend: ${faceapi.tf.getBackend()}; os: ${osType}; cpu: ${getCPUModel()}`;
  166. window._hmt.push([
  167. "_trackEvent",
  168. "正在考试页面",
  169. "实时人脸检测统计" + roundAvg100 + "ms",
  170. stats,
  171. ]);
  172. this.multipleTimeUsage = roundAvg;
  173. }
  174. // init this.showWaringTime
  175. this.showWaringTime = this.showWaringTime || Date.now();
  176. if (result.length >= 2 && Date.now() - this.showWaringTime > 20 * 1000) {
  177. this.showWaringTime = Date.now();
  178. this.$Message.warning({
  179. content: "请独立完成考试",
  180. duration: 5,
  181. closable: true,
  182. });
  183. }
  184. if (result.length === 0 && Date.now() - this.showWaringTime > 20 * 1000) {
  185. this.showWaringTime = Date.now();
  186. this.$Message.warning({
  187. content: "请调整坐姿,诚信考试",
  188. duration: 5,
  189. closable: true,
  190. });
  191. }
  192. if (
  193. (!result || result.length !== 1) &&
  194. !videoEl.classList.contains("video-warning")
  195. ) {
  196. videoEl.classList.add("video-warning");
  197. this.warningTimeout = setTimeout(function() {
  198. videoEl.classList.remove("video-warning");
  199. }, 3000);
  200. }
  201. this.detectFacesTimeout = setTimeout(() => this.detectFaces(), 10 * 1000);
  202. },
  203. },
  204. };
  205. </script>
  206. <style>
  207. @keyframes warning-people {
  208. 0% {
  209. /* border: solid 5px white; */
  210. box-shadow: 0 0 20px white;
  211. }
  212. 100% {
  213. /* border: solid 5px red; */
  214. box-shadow: 0 0 20px gold;
  215. }
  216. }
  217. .video-warning {
  218. animation: warning-people 3s infinite;
  219. }
  220. </style>