FaceTracking.vue 5.9 KB

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