|
@@ -6,6 +6,7 @@ import { onMounted, onUnmounted } from "vue";
|
|
|
import { useTimers } from "@/setups/useTimers";
|
|
|
import { store } from "@/store/store";
|
|
|
import { throttle } from "lodash-es";
|
|
|
+import { MessageReactive } from "naive-ui";
|
|
|
|
|
|
const { addTimeout, addInterval } = useTimers();
|
|
|
// window.faceapi = faceapi;
|
|
@@ -141,9 +142,12 @@ async function detectTest() {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ let singleCost = 0;
|
|
|
for (let idx = 0; idx < inputSizeList.length; idx++) {
|
|
|
for (let n = 0; n < detectTimes; n++) {
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 3 * 1000));
|
|
|
+ await new Promise((resolve) =>
|
|
|
+ setTimeout(resolve, Math.min(singleCost * 2, 3 * 1000))
|
|
|
+ );
|
|
|
if (store.exam.isDoingFaceLiveness) {
|
|
|
logger({
|
|
|
cnl: ["server", "console"],
|
|
@@ -168,16 +172,15 @@ async function detectTest() {
|
|
|
new Promise((resolve) => setTimeout(resolve, 10 * 1000)),
|
|
|
]);
|
|
|
const detectEndTime = performance.now();
|
|
|
- if (detectStartTime - detectEndTime > 0.2 * 1000) {
|
|
|
+ singleCost = detectStartTime - detectEndTime;
|
|
|
+ if (singleCost > 0.2 * 1000) {
|
|
|
disableFaceTracking = true;
|
|
|
_hmt.push(["_trackEvent", "答题页面", "单次检测耗时过长:停止实时"]);
|
|
|
logger({
|
|
|
cnl: ["server"],
|
|
|
key: "FaceTracking",
|
|
|
act: "单次检测耗时过长:停止实时",
|
|
|
- ext: {
|
|
|
- cost: detectStartTime - detectEndTime,
|
|
|
- },
|
|
|
+ ext: { cost: singleCost },
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
@@ -245,12 +248,17 @@ function getFaceDetectorOptions() {
|
|
|
// return new faceapi.MtcnnOptions({ minFaceSize: 200, scaleFactor: 0.8 });
|
|
|
}
|
|
|
|
|
|
+let displayedMsg: MessageReactive | null = null;
|
|
|
const indepentExamingMsg = throttle(
|
|
|
- () => $message.warning("请独立完成考试"),
|
|
|
+ () => (displayedMsg = $message.warning("请独立完成考试")),
|
|
|
20 * 1000
|
|
|
);
|
|
|
const posureExamingMsg = throttle(
|
|
|
- () => $message.warning("请调整坐姿,诚信考试"),
|
|
|
+ () => (displayedMsg = $message.warning("请调整坐姿,诚信考试")),
|
|
|
+ 20 * 1000
|
|
|
+);
|
|
|
+const bgCheckExamingMsg = throttle(
|
|
|
+ () => $message.warning("请保持正确坐姿,确保脸部在摄像头内,背景无强光。"),
|
|
|
20 * 1000
|
|
|
);
|
|
|
|
|
@@ -296,7 +304,12 @@ async function detectFaces() {
|
|
|
let result;
|
|
|
|
|
|
try {
|
|
|
- logger({ cnl: ["server"], key: "FaceTracking", act: "开始一次人脸检测" });
|
|
|
+ logger({
|
|
|
+ cnl: ["server"],
|
|
|
+ lvl: "debug",
|
|
|
+ key: "FaceTracking",
|
|
|
+ act: "开始一次人脸检测",
|
|
|
+ });
|
|
|
result = await faceapi
|
|
|
// .detectSingleFace(videoEl, options)
|
|
|
.detectAllFaces(videoEl, options);
|
|
@@ -331,6 +344,7 @@ async function detectFaces() {
|
|
|
const detectEndTime = performance.now();
|
|
|
logger({
|
|
|
cnl: ["server", "console"],
|
|
|
+ lvl: "debug",
|
|
|
key: "FaceTracking",
|
|
|
pgn: "实时人脸检测",
|
|
|
act: "做完一次人脸检测,准备统计...",
|
|
@@ -387,6 +401,17 @@ async function detectFaces() {
|
|
|
failTimes++;
|
|
|
}
|
|
|
|
|
|
+ if (result.length === 1) {
|
|
|
+ try {
|
|
|
+ // 在失败一次后马上调整坐姿的话,会主动去除警告
|
|
|
+ displayedMsg?.destroy();
|
|
|
+ } catch (error) {
|
|
|
+ displayedMsg = null;
|
|
|
+ console.debug("ignore", error);
|
|
|
+ }
|
|
|
+ failTimes = 0;
|
|
|
+ }
|
|
|
+
|
|
|
if (result.length !== 1 && !videoEl.classList.contains("video-warning")) {
|
|
|
videoEl.classList.add("video-warning");
|
|
|
addTimeout(() => videoEl.classList.remove("video-warning"), 3000);
|
|
@@ -399,14 +424,18 @@ async function detectFaces() {
|
|
|
lvl: "debug",
|
|
|
act: "准备下次人脸检测",
|
|
|
});
|
|
|
+
|
|
|
+ const machineCap = 10 * multipleTimeUsage;
|
|
|
+ // 最小1秒,最大10秒
|
|
|
+ let desiredInterval = Math.min(10 * 1000, Math.max(1000, machineCap));
|
|
|
detectFacesTimeout = addTimeout(async () => {
|
|
|
- if (failTimes >= 5) {
|
|
|
- $message.warning("请保持正确坐姿,确保脸部在摄像头内,背景无强光。");
|
|
|
+ if (failTimes >= 10) {
|
|
|
+ bgCheckExamingMsg();
|
|
|
failTimes = 0;
|
|
|
await detectTest();
|
|
|
}
|
|
|
await detectFaces();
|
|
|
- }, 20 * 1000);
|
|
|
+ }, desiredInterval);
|
|
|
}
|
|
|
|
|
|
onUnmounted(() => {
|