|
@@ -3,89 +3,50 @@
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
+import * as faceapi from "face-api.js";
|
|
|
|
+
|
|
|
|
+// models path
|
|
|
|
+const modelsPath = "/models/";
|
|
|
|
+
|
|
|
|
+function getFaceDetectorOptions() {
|
|
|
|
+ window.____hideMe =
|
|
|
|
+ window.____hideMe ||
|
|
|
|
+ new faceapi.TinyFaceDetectorOptions({
|
|
|
|
+ inputSize: 320,
|
|
|
|
+ scoreThreshold: 0.5,
|
|
|
|
+ });
|
|
|
|
+ return window.____hideMe;
|
|
|
|
+ // return new faceapi.SsdMobilenetv1Options({ minConfidence: 0.8 });
|
|
|
|
+ // return new faceapi.MtcnnOptions({ minFaceSize: 200, scaleFactor: 0.8 });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const detectTimeArray = [];
|
|
export default {
|
|
export default {
|
|
name: "FaceTracking",
|
|
name: "FaceTracking",
|
|
|
|
+ async created() {
|
|
|
|
+ await faceapi.nets.tinyFaceDetector.load(modelsPath);
|
|
|
|
+ // faceapi.nets.faceRecognitionNet.load(modelsPath);
|
|
|
|
+ await faceapi.loadFaceLandmarkModel(modelsPath);
|
|
|
|
+ faceapi.tf.ENV.set("WEBGL_PACK", false);
|
|
|
|
+ },
|
|
async mounted() {
|
|
async mounted() {
|
|
- const that = this;
|
|
|
|
- var trackWarnings = 0;
|
|
|
|
- var trackMultiplePerson = false;
|
|
|
|
- // var trackerTask;
|
|
|
|
let trackStarted = false;
|
|
let trackStarted = false;
|
|
|
|
+ const that = this;
|
|
|
|
|
|
- function trackHead() {
|
|
|
|
- var video = document.getElementById("video");
|
|
|
|
- if (video && video.readyState === 4) {
|
|
|
|
|
|
+ async function trackHead() {
|
|
|
|
+ const video = document.getElementById("video");
|
|
|
|
+ if (
|
|
|
|
+ video &&
|
|
|
|
+ video.readyState === 4 &&
|
|
|
|
+ faceapi.nets.tinyFaceDetector.params
|
|
|
|
+ ) {
|
|
trackStarted = true;
|
|
trackStarted = true;
|
|
} else {
|
|
} else {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
console.log("start tracking ... ");
|
|
console.log("start tracking ... ");
|
|
- // var canvas = document.getElementById('canvas');
|
|
|
|
- // var context = canvas.getContext('2d');
|
|
|
|
-
|
|
|
|
- var tracker = new window.tracking.ObjectTracker("face");
|
|
|
|
- tracker.setInitialScale(4);
|
|
|
|
- tracker.setStepSize(2);
|
|
|
|
- tracker.setEdgesDensity(0.1);
|
|
|
|
-
|
|
|
|
- that.trackerTask = window.tracking.track("#video", tracker, {
|
|
|
|
- camera: false,
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- tracker.on("track", function(event) {
|
|
|
|
- // console.log("on track", event);
|
|
|
|
- // console.log(trackWarnings, trackMultiplePerson);
|
|
|
|
-
|
|
|
|
- if (event.data.length !== 1) {
|
|
|
|
- trackWarnings++;
|
|
|
|
- if (event.data.length > 1) {
|
|
|
|
- trackMultiplePerson = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (trackWarnings < 20) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!video.classList.contains("video-warning")) {
|
|
|
|
- video.classList.add("video-warning");
|
|
|
|
- setTimeout(function() {
|
|
|
|
- video.classList.remove("video-warning");
|
|
|
|
- }, 3000);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (trackMultiplePerson) {
|
|
|
|
- that.$Message.warning({
|
|
|
|
- content: "请独立完成考试",
|
|
|
|
- duration: 5,
|
|
|
|
- closable: true,
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- that.$Message.warning({
|
|
|
|
- content: "请调整坐姿,诚信考试",
|
|
|
|
- duration: 5,
|
|
|
|
- closable: true,
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- trackWarnings = 0;
|
|
|
|
- trackMultiplePerson = false;
|
|
|
|
- } else {
|
|
|
|
- video.classList.remove("video-warning");
|
|
|
|
- trackWarnings = 0;
|
|
|
|
- trackMultiplePerson = false;
|
|
|
|
- }
|
|
|
|
- // context.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
-
|
|
|
|
- // event.data.forEach(function(rect) {
|
|
|
|
- // context.strokeStyle = '#a64ceb';
|
|
|
|
- // context.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
- // context.font = '11px Helvetica';
|
|
|
|
- // context.fillStyle = "#fff";
|
|
|
|
- // context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
|
|
|
|
- // context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
|
|
|
|
- // });
|
|
|
|
- });
|
|
|
|
|
|
+ await that.detectFaces();
|
|
}
|
|
}
|
|
- // setTimeout(trackHead, 3000)
|
|
|
|
this.trackHeadInterval = setInterval(() => {
|
|
this.trackHeadInterval = setInterval(() => {
|
|
if (trackStarted) {
|
|
if (trackStarted) {
|
|
clearInterval(this.trackHeadInterval);
|
|
clearInterval(this.trackHeadInterval);
|
|
@@ -93,11 +54,86 @@ export default {
|
|
trackHead();
|
|
trackHead();
|
|
}
|
|
}
|
|
}, 1000);
|
|
}, 1000);
|
|
- // tracking.js
|
|
|
|
},
|
|
},
|
|
beforeDestroy() {
|
|
beforeDestroy() {
|
|
clearInterval(this.trackHeadInterval);
|
|
clearInterval(this.trackHeadInterval);
|
|
- this.trackerTask.stop();
|
|
|
|
|
|
+ clearTimeout(this.warningTimeout);
|
|
|
|
+ clearTimeout(this.detectFacesTimeout);
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ async detectFaces() {
|
|
|
|
+ const detectStartTime = performance.now();
|
|
|
|
+ const videoEl = document.getElementById("video");
|
|
|
|
+ // this.___vWidth =
|
|
|
|
+ // this.___vWidth ||
|
|
|
|
+ // document.getElementById("video-container").clientWidth;
|
|
|
|
+
|
|
|
|
+ const options = getFaceDetectorOptions();
|
|
|
|
+ let result;
|
|
|
|
+
|
|
|
|
+ result = await faceapi
|
|
|
|
+ // .detectSingleFace(videoEl, options)
|
|
|
|
+ .detectAllFaces(videoEl, options);
|
|
|
|
+ // console.log(result);
|
|
|
|
+
|
|
|
|
+ const detectEndTime = performance.now();
|
|
|
|
+ // console.log("WebGL: ", faceapi.tf.ENV.get("WEBGL_PACK"));
|
|
|
|
+ console.log(
|
|
|
|
+ "WebGL: ",
|
|
|
|
+ faceapi.tf.ENV.get("WEBGL_PACK"),
|
|
|
|
+ " single detect time: ",
|
|
|
|
+ detectEndTime - detectStartTime
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (detectTimeArray.length < 31) {
|
|
|
|
+ // 仅捕获一部分检测次数
|
|
|
|
+ detectTimeArray.push(detectEndTime - detectStartTime);
|
|
|
|
+ }
|
|
|
|
+ if (detectTimeArray.length === 31) {
|
|
|
|
+ detectTimeArray.shift();
|
|
|
|
+ const avg =
|
|
|
|
+ detectTimeArray.reduce((a, b) => a + b, 0) / detectTimeArray.length;
|
|
|
|
+ const roundAvg = Math.round(avg / 10) * 10;
|
|
|
|
+ window._hmt.push([
|
|
|
|
+ "_trackEvent",
|
|
|
|
+ "正在考试页面",
|
|
|
|
+ "实时人脸检测平均时长",
|
|
|
|
+ roundAvg + "ms",
|
|
|
|
+ ]);
|
|
|
|
+ console.log(detectTimeArray);
|
|
|
|
+ detectTimeArray.push(0, 0); // 避免再次达到push条件和上传条件
|
|
|
|
+ }
|
|
|
|
+ if (result.length >= 2) {
|
|
|
|
+ this.$Message.warning({
|
|
|
|
+ content: "请独立完成考试",
|
|
|
|
+ duration: 5,
|
|
|
|
+ closable: true,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (result.length === 0) {
|
|
|
|
+ this.findFirst = !this.findFirst; // 两次才告警
|
|
|
|
+ if (!this.findFirst) {
|
|
|
|
+ this.$Message.warning({
|
|
|
|
+ content: "请调整坐姿,诚信考试",
|
|
|
|
+ duration: 5,
|
|
|
|
+ closable: true,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (
|
|
|
|
+ (!result || result.length !== 1) &&
|
|
|
|
+ !videoEl.classList.contains("video-warning")
|
|
|
|
+ ) {
|
|
|
|
+ videoEl.classList.add("video-warning");
|
|
|
|
+ this.warningTimeout = setTimeout(function() {
|
|
|
|
+ videoEl.classList.remove("video-warning");
|
|
|
|
+ }, 3000);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.detectFacesTimeout = setTimeout(() => this.detectFaces(), 5000);
|
|
|
|
+ },
|
|
},
|
|
},
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|