|
@@ -1,9 +1,18 @@
|
|
|
<template>
|
|
|
<div class="videoContainer">
|
|
|
- <video ref="video" autoplay muted playsinline></video>
|
|
|
+ <video
|
|
|
+ ref="video"
|
|
|
+ id="video"
|
|
|
+ :width="1280"
|
|
|
+ :height="800"
|
|
|
+ autoplay
|
|
|
+ muted
|
|
|
+ playsinline
|
|
|
+ ></video>
|
|
|
<div id="timestamp" v-show="false">
|
|
|
{{ datetimeData.date + " " + datetimeData.time }}
|
|
|
</div>
|
|
|
+ <canvas id="blurred-canvas" :width="1280" :height="800"></canvas>
|
|
|
<!-- <el-button @click="startCamera">开启摄像头</el-button>
|
|
|
<el-button @click="stopCamera" :disabled="!isCameraActive"
|
|
|
>关闭摄像头</el-button
|
|
@@ -13,6 +22,9 @@
|
|
|
|
|
|
|
|
|
<script>
|
|
|
+/* eslint-disable */
|
|
|
+import * as tf from "@tensorflow/tfjs";
|
|
|
+import * as bodyPix from "@tensorflow-models/body-pix";
|
|
|
import { GetPersonByFace, GetPersonByFaceBase64 } from "@/API/custom";
|
|
|
import Dayjs from "dayjs";
|
|
|
|
|
@@ -59,6 +71,7 @@ export default {
|
|
|
data() {
|
|
|
return {
|
|
|
isCameraActive: false,
|
|
|
+ video: null,
|
|
|
stream: null,
|
|
|
captureInterval: null, // 新增定时器变量
|
|
|
datetimeData: {
|
|
@@ -66,6 +79,12 @@ export default {
|
|
|
time: Dayjs(new Date()).format("HH:mm:ss"),
|
|
|
week: Dayjs(new Date()).format("dddd"),
|
|
|
},
|
|
|
+ // BodyPix模型加载
|
|
|
+ net: null,
|
|
|
+ localModelPath: "/js/model/bodypix/model-stride16.json",
|
|
|
+ // 修改默认值为 true,自动开启背景虚化
|
|
|
+ showBlurred: true,
|
|
|
+ loading: true,
|
|
|
};
|
|
|
},
|
|
|
methods: {
|
|
@@ -84,18 +103,51 @@ export default {
|
|
|
timer = null;
|
|
|
});
|
|
|
},
|
|
|
+ async loadBodyPix() {
|
|
|
+ this.net = await bodyPix.load({ modelUrl: this.localModelPath });
|
|
|
+ console.log("BodyPix模型加载返回", this.net);
|
|
|
+ this.processVideoFrame();
|
|
|
+ },
|
|
|
+ async processVideoFrame() {
|
|
|
+ // 确保 video 和 blurredCanvas 存在
|
|
|
+ if (this.showBlurred && this.video && this.blurredCanvas) {
|
|
|
+ try {
|
|
|
+ const segmentation = await this.net.segmentPerson(this.video);
|
|
|
+ const backgroundBlurAmount = 10;
|
|
|
+ const edgeBlurAmount = 3;
|
|
|
+ const flipHorizontal = false;
|
|
|
+
|
|
|
+ bodyPix.drawBokehEffect(
|
|
|
+ this.blurredCanvas,
|
|
|
+ this.video,
|
|
|
+ segmentation,
|
|
|
+ backgroundBlurAmount,
|
|
|
+ edgeBlurAmount,
|
|
|
+ flipHorizontal
|
|
|
+ );
|
|
|
+ } catch (err) {
|
|
|
+ console.error("处理视频帧时出错:", err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ requestAnimationFrame(() => this.processVideoFrame());
|
|
|
+ },
|
|
|
async startCamera() {
|
|
|
try {
|
|
|
- const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
- video: {
|
|
|
- width: { ideal: 1280 },
|
|
|
- height: { ideal: 800 },
|
|
|
- facingMode: "user",
|
|
|
- },
|
|
|
+ this.stream = await navigator.mediaDevices.getUserMedia({
|
|
|
+ video: true,
|
|
|
});
|
|
|
- this.stream = stream;
|
|
|
+ this.video = document.getElementById("video");
|
|
|
this.isCameraActive = true;
|
|
|
- this.$refs.video.srcObject = stream;
|
|
|
+
|
|
|
+ if (this.video) {
|
|
|
+ this.video.srcObject = this.stream;
|
|
|
+ this.loading = false;
|
|
|
+ this.loadBodyPix();
|
|
|
+ } else {
|
|
|
+ this.error = "未能找到视频元素,请检查 HTML 结构。";
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
|
|
|
if (this.isUpload) {
|
|
|
setTimeout(() => {
|
|
@@ -111,13 +163,14 @@ export default {
|
|
|
} catch (err) {
|
|
|
console.error("摄像头访问错误:", err);
|
|
|
alert(`摄像头访问失败: ${err.message}`);
|
|
|
+ this.loading = false;
|
|
|
}
|
|
|
},
|
|
|
stopCamera() {
|
|
|
if (this.stream) {
|
|
|
this.stream.getTracks().forEach((track) => track.stop());
|
|
|
this.isCameraActive = false;
|
|
|
- this.$refs.video.srcObject = null;
|
|
|
+ this.video.srcObject = null;
|
|
|
this.stream = null;
|
|
|
|
|
|
// 停止定时器
|
|
@@ -127,65 +180,24 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- // 新增截图方法
|
|
|
- // async captureImage() {
|
|
|
- // const video = this.$refs.video;
|
|
|
- // video.currentTime = 1;
|
|
|
- // const canvas = document.createElement("canvas");
|
|
|
- // canvas.width = video.videoWidth;
|
|
|
- // canvas.height = video.videoHeight;
|
|
|
- // canvas
|
|
|
- // .getContext("2d")
|
|
|
- // .drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
|
- // // 下载到本地
|
|
|
- // // const link = document.createElement("a");
|
|
|
- // // link.download = `capture_${new Date()
|
|
|
- // // .toISOString()
|
|
|
- // // .replace(/[:.]/g, "-")}.png`;
|
|
|
- // // link.href = canvas.toDataURL("image/png");
|
|
|
- // // link.click();
|
|
|
-
|
|
|
- // // 将canvas转为Blob对象
|
|
|
- // canvas.toBlob(async (blob) => {
|
|
|
- // if (blob) {
|
|
|
- // console.log("blob数据", blob);
|
|
|
- // const formData = new FormData();
|
|
|
- // formData.append("timestamp", new Date().toISOString());
|
|
|
- // formData.append("file", blob, "screenshot.png");
|
|
|
- // console.log("上传数据", formData);
|
|
|
- // try {
|
|
|
- // // const res = await GetPersonByFace(formData);
|
|
|
- // // console.log("上传反馈", res);
|
|
|
- // // const response = await fetch("System/Person/GetPersonByFace", {
|
|
|
- // // method: "POST",
|
|
|
- // // body: formData,
|
|
|
- // // });
|
|
|
- // } catch (err) {
|
|
|
- // console.error("上传过程中出错:", err);
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }, "image/png");
|
|
|
- // },
|
|
|
-
|
|
|
async captureImage() {
|
|
|
- const video = this.$refs.video;
|
|
|
- if (video.paused) return;
|
|
|
+ if (this.video.paused) return;
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
|
|
- const cropWidth = video.videoWidth * 0.5;
|
|
|
- const cropHeight = video.videoHeight * 0.98;
|
|
|
+ const cropWidth = this.video.videoWidth * 0.5;
|
|
|
+ const cropHeight = this.video.videoHeight * 0.98;
|
|
|
|
|
|
// 设置canvas尺寸为选择区域大小
|
|
|
canvas.width = cropWidth;
|
|
|
canvas.height = cropHeight;
|
|
|
|
|
|
- const x = (video.videoWidth - cropWidth) / 2;
|
|
|
- const y = (video.videoHeight - cropHeight) / 2;
|
|
|
+ const x = (this.video.videoWidth - cropWidth) / 2;
|
|
|
+ const y = (this.video.videoHeight - cropHeight) / 2;
|
|
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
|
//中心60%区域
|
|
|
ctx.drawImage(
|
|
|
- video,
|
|
|
+ this.video,
|
|
|
x,
|
|
|
y,
|
|
|
cropWidth,
|
|
@@ -232,27 +244,48 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
async uploadImage() {
|
|
|
- const video = this.$refs.video;
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
|
|
- const cropWidth = video.videoWidth * 0.5;
|
|
|
- const cropHeight = video.videoHeight * 0.98;
|
|
|
+ const cropWidth = this.video.videoWidth * 0.5;
|
|
|
+ const cropHeight = this.video.videoHeight * 0.98;
|
|
|
|
|
|
// 设置canvas尺寸为选择区域大小
|
|
|
canvas.width = cropWidth;
|
|
|
canvas.height = cropHeight;
|
|
|
|
|
|
- const x = (video.videoWidth - cropWidth) / 2;
|
|
|
- const y = (video.videoHeight - cropHeight) / 2;
|
|
|
+ const x = (this.video.videoWidth - cropWidth) / 2;
|
|
|
+ const y = (this.video.videoHeight - cropHeight) / 2;
|
|
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
|
- //中心60%区域
|
|
|
+ // console.log(
|
|
|
+ // "???????????????????",
|
|
|
+ // x,
|
|
|
+ // y,
|
|
|
+ // cropWidth,
|
|
|
+ // cropHeight,
|
|
|
+ // this.video,
|
|
|
+ // this.blurredCanvas,
|
|
|
+ // canvas
|
|
|
+ // );
|
|
|
+ //中心60%区域-原图
|
|
|
+ // ctx.drawImage(
|
|
|
+ // this.video,
|
|
|
+ // x,
|
|
|
+ // y,
|
|
|
+ // cropWidth,
|
|
|
+ // cropHeight,
|
|
|
+ // 0,
|
|
|
+ // 0,
|
|
|
+ // cropWidth,
|
|
|
+ // cropHeight
|
|
|
+ // );
|
|
|
+ // 虚化背景处理绘制
|
|
|
ctx.drawImage(
|
|
|
- video,
|
|
|
- x,
|
|
|
- y,
|
|
|
- cropWidth,
|
|
|
- cropHeight,
|
|
|
+ this.showBlurred ? this.blurredCanvas : this.video,
|
|
|
+ this.blurredCanvas.width * 0.25,
|
|
|
+ this.blurredCanvas.height * 0.01,
|
|
|
+ this.blurredCanvas.width * 0.5,
|
|
|
+ this.blurredCanvas.height * 0.98,
|
|
|
0,
|
|
|
0,
|
|
|
cropWidth,
|
|
@@ -294,6 +327,14 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
},
|
|
|
+ mounted() {
|
|
|
+ if (this.loading) {
|
|
|
+ this.blurredCanvas = document.getElementById("blurred-canvas");
|
|
|
+ if (this.blurredCanvas) {
|
|
|
+ this.ctx = this.blurredCanvas.getContext("2d");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
created() {
|
|
|
this.startCamera();
|
|
|
this.refreshTime();
|
|
@@ -329,4 +370,7 @@ video {
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
padding: 5px;
|
|
|
}
|
|
|
+#blurred-video {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
</style>
|