|
@@ -0,0 +1,1064 @@
|
|
|
+<template>
|
|
|
+ <div style="width: 100%; height: calc(100%); background: #fff; display: flex">
|
|
|
+ <div class="ablockly">
|
|
|
+ <div id="blocklyDiv"></div>
|
|
|
+ <xml id="toolbox" style="display: none">
|
|
|
+ <category name="逻辑" colour="%{BKY_LOGIC_HUE}">
|
|
|
+ <block type="controls_if"></block>
|
|
|
+ <block type="logic_compare"></block>
|
|
|
+ <block type="logic_operation"></block>
|
|
|
+ <block type="logic_negate"></block>
|
|
|
+ <block type="logic_boolean"></block>
|
|
|
+ <block type="logic_number"></block>
|
|
|
+ </category>
|
|
|
+ <sep></sep>
|
|
|
+ <category name="循环" colour="#5ba55b">
|
|
|
+ <block type="controls_repeat_forever"></block>
|
|
|
+ <block type="controls_whileUntil"></block>
|
|
|
+ <block type="controls_for"></block>
|
|
|
+ </category>
|
|
|
+ <sep></sep>
|
|
|
+ <category id="catIOTScreen" name="屏幕" colour="#5cb2d6">
|
|
|
+ <block type="iot_lcd_screeninit"></block>
|
|
|
+ </category>
|
|
|
+ <sep></sep>
|
|
|
+ <category id="cat" name="人脸识别" colour="#ee783a">
|
|
|
+ <block type="iot_lcd_faceinit"></block>
|
|
|
+ </category>
|
|
|
+ <sep></sep>
|
|
|
+ <category id="police" name="电子警察组件" colour="#1b5873">
|
|
|
+ <block type="iot_lcd_policeinit"></block>
|
|
|
+ </category>
|
|
|
+ <sep></sep>
|
|
|
+ <category id="police" name="AI组件" colour="#935ba5">
|
|
|
+ <block type="ai_gesture"></block>
|
|
|
+ <block type="ai_motor"></block>
|
|
|
+ </category>
|
|
|
+ </xml>
|
|
|
+ </div>
|
|
|
+ <div class="container">
|
|
|
+ <div class="img">
|
|
|
+ <div class="left" style="width: 120px"></div>
|
|
|
+ <div class="controlZ">
|
|
|
+ <div id="fan" ref="fan">
|
|
|
+ <img :src="img[0]" alt />
|
|
|
+ </div>
|
|
|
+ <div id="fanB">
|
|
|
+ <img :src="img[6]" alt />
|
|
|
+ </div>
|
|
|
+ <div id="motor">
|
|
|
+ <img :src="img[1]" alt />
|
|
|
+ </div>
|
|
|
+ <div id="base">
|
|
|
+ <img :src="img[2]" alt />
|
|
|
+ </div>
|
|
|
+ <div id="a4">
|
|
|
+ <img :src="img[3]" alt />
|
|
|
+ </div>
|
|
|
+ <div id="screan">
|
|
|
+ <img :src="img[4]" alt />
|
|
|
+ <!--图片展示-->
|
|
|
+ <!-- <video
|
|
|
+ ref="video"
|
|
|
+ id="video_cam"
|
|
|
+ width="178"
|
|
|
+ height="142.4"
|
|
|
+ class="face"
|
|
|
+ autoplay
|
|
|
+ v-show="isCamera"
|
|
|
+ ></video>
|
|
|
+ <canvas
|
|
|
+ ref="canvasDOM"
|
|
|
+ width="178"
|
|
|
+ height="142.4"
|
|
|
+ class="kuang"
|
|
|
+ v-show="isCamera"
|
|
|
+ ></canvas> -->
|
|
|
+ <video
|
|
|
+ ref="video"
|
|
|
+ id="video_cam"
|
|
|
+ width="118.666"
|
|
|
+ height="94.933"
|
|
|
+ class="face"
|
|
|
+ autoplay
|
|
|
+ v-show="isCamera"
|
|
|
+ ></video>
|
|
|
+ <canvas
|
|
|
+ ref="canvasDOM"
|
|
|
+ width="118.666"
|
|
|
+ height="94.933"
|
|
|
+ class="kuang"
|
|
|
+ v-show="isCamera"
|
|
|
+ ></canvas>
|
|
|
+ </div>
|
|
|
+ <div id="line">
|
|
|
+ <img :src="img[5]" alt />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="cameraZ">
|
|
|
+ <!--开启摄像头-->
|
|
|
+ <div class="cameraBtn">
|
|
|
+ <div class="open" @click="start()" v-if="!isZuan">
|
|
|
+ <img src="../assets/img/fan/icon.png" alt />
|
|
|
+ </div>
|
|
|
+ <div class="close" @click="closeCamera()" v-else>
|
|
|
+ <img src="../assets/img/fan/icon2.png" alt />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--确认-->
|
|
|
+ <div v-show="false" class="isPhoto">
|
|
|
+ <!--canvas截取流-->
|
|
|
+ <canvas
|
|
|
+ ref="canvas"
|
|
|
+ width="300"
|
|
|
+ height="240"
|
|
|
+ v-show="false"
|
|
|
+ ></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<style>
|
|
|
+.as {
|
|
|
+ animation: myfirst 1s linear infinite;
|
|
|
+}
|
|
|
+
|
|
|
+.asn {
|
|
|
+ transform: rotate(0deg);
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes myfirst {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0deg);
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@-webkit-keyframes myfirst /* Safari and Chrome */ {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0deg);
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.ass {
|
|
|
+ animation: myfirst 3s linear infinite;
|
|
|
+}
|
|
|
+</style>
|
|
|
+<script>
|
|
|
+import * as handPoseDetection from "@tensorflow-models/hand-pose-detection";
|
|
|
+
|
|
|
+import "@tensorflow/tfjs-backend-webgl";
|
|
|
+import * as mpHands from "@mediapipe/hands";
|
|
|
+
|
|
|
+// 引入Blockly
|
|
|
+import Blockly from "blockly";
|
|
|
+// 引入想要转换的语言,语言有php python dart lua javascript
|
|
|
+import * as JavaScript from "blockly/javascript";
|
|
|
+import * as Blocks from "blockly/blocks";
|
|
|
+// 引入语言包并使用
|
|
|
+import * as hans from "blockly/msg/zh-hans";
|
|
|
+Blockly.setLocale(hans);
|
|
|
+//引入媒体文件:我是在github上下载的blockly源码,将源码中的media文件放入我项目中的public文件夹下
|
|
|
+import $ from "jquery";
|
|
|
+//忽略被vue错认为组件的blockly中的标签,不止以下这些,请发现一个忽略一个
|
|
|
+import Vue from "vue";
|
|
|
+Vue.config.ignoredElements.push("xml");
|
|
|
+Vue.config.ignoredElements.push("block");
|
|
|
+Vue.config.ignoredElements.push("field");
|
|
|
+Vue.config.ignoredElements.push("category");
|
|
|
+Vue.config.ignoredElements.push("sep");
|
|
|
+Vue.config.ignoredElements.push("value");
|
|
|
+Vue.config.ignoredElements.push("statement");
|
|
|
+Vue.config.ignoredElements.push("mutation");
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ that: this,
|
|
|
+ img: [
|
|
|
+ require("../assets/img/fan/fan.png"),
|
|
|
+ require("../assets/img/fan/2.png"),
|
|
|
+ require("../assets/img/fan/3.png"),
|
|
|
+ require("../assets/img/fan/4.png"),
|
|
|
+ require("../assets/img/fan/5.png"),
|
|
|
+ require("../assets/img/fan/6.png"),
|
|
|
+ require("../assets/img/fan/fanB.png"),
|
|
|
+ ],
|
|
|
+ shibieImg: require("../assets/img/face.png"),
|
|
|
+ isCamera: false,
|
|
|
+ isZuan: false,
|
|
|
+ count: 0,
|
|
|
+ change: 0,
|
|
|
+ closeUpdateMessage: false,
|
|
|
+ updateMessage: false,
|
|
|
+ upName: "",
|
|
|
+ number: 0,
|
|
|
+ isdetected: "请您保持脸部在画面中央",
|
|
|
+ videoEl: {},
|
|
|
+ canvasEL: {},
|
|
|
+ formLabelWidth: "100px",
|
|
|
+ resultImg: {
|
|
|
+ img: [],
|
|
|
+ name: "",
|
|
|
+ },
|
|
|
+ // 预设样本图,支持本地,网络,beas64
|
|
|
+ sampleArr: [
|
|
|
+ // {
|
|
|
+ // name: "编号1",
|
|
|
+ // img: []
|
|
|
+ // }
|
|
|
+ ],
|
|
|
+ // 匹配图,支持本地,网络,beas64
|
|
|
+ detArr: [
|
|
|
+ //"" 图片1
|
|
|
+ ],
|
|
|
+ numberOne: 0,
|
|
|
+ // 匹配结果
|
|
|
+ resultArr: [],
|
|
|
+ // 人脸匹配矩阵数组对象转码结果
|
|
|
+ faceMatcher: null,
|
|
|
+ rotate: 0,
|
|
|
+ timer: null,
|
|
|
+ detector: null,
|
|
|
+ hand: 0,
|
|
|
+ isC: false,
|
|
|
+ f: null,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ cancel() {
|
|
|
+ this.updateMessage = false;
|
|
|
+ this.sampleArr[this.sampleArr.length - 1].name = "编号:" + this.number;
|
|
|
+ if (this.sampleArr.length > 0) {
|
|
|
+ this.fnsample();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async start() {
|
|
|
+ this.isZuan = true;
|
|
|
+ this.isCamera = true;
|
|
|
+ // let video = this.$refs["video"];
|
|
|
+ this.timer = setInterval(() => {
|
|
|
+ this.handsFind();
|
|
|
+ }, 5000);
|
|
|
+ },
|
|
|
+ zhuan() {
|
|
|
+ var _fan = this.$refs.fan;
|
|
|
+ _fan.className = "asn as";
|
|
|
+ clearInterval(this.timer);
|
|
|
+ this.timer = setInterval(function () {
|
|
|
+ _fan.className = "asn";
|
|
|
+ setTimeout(function () {
|
|
|
+ _fan.className = "as";
|
|
|
+ }, 0);
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+ zhuann() {
|
|
|
+ var _fan = this.$refs.fan;
|
|
|
+ this.$refs.fan.className = "asn ass";
|
|
|
+ clearInterval(this.timer);
|
|
|
+ this.timer = setInterval(function () {
|
|
|
+ _fan.className = "asn";
|
|
|
+ setTimeout(function () {
|
|
|
+ _fan.className = "ass";
|
|
|
+ }, 0);
|
|
|
+ }, 3000);
|
|
|
+ },
|
|
|
+ update() {
|
|
|
+ this.change = 1;
|
|
|
+ this.number = this.number + 1;
|
|
|
+ if (this.change == 1) {
|
|
|
+ if (this.sampleArr.length > 0) {
|
|
|
+ this.sampleArr[this.sampleArr.length - 1].name = this.upName;
|
|
|
+ this.isdetected = "已识别到" + this.resultImg.name + "的图片";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (this.sampleArr.length > 0) {
|
|
|
+ // var a = document.getElementsByClassName("spot");
|
|
|
+ // a[0].style.display = "block";
|
|
|
+ this.fnsample();
|
|
|
+ }
|
|
|
+ this.updateMessage = false;
|
|
|
+ },
|
|
|
+ // image conversion
|
|
|
+ b64toBlob(b64Data, contentType, sliceSize) {
|
|
|
+ contentType = contentType || "";
|
|
|
+ sliceSize = sliceSize || 512;
|
|
|
+
|
|
|
+ var byteCharacters = atob(b64Data);
|
|
|
+ var byteArrays = [];
|
|
|
+
|
|
|
+ for (
|
|
|
+ var offset = 0;
|
|
|
+ offset < byteCharacters.length;
|
|
|
+ offset += sliceSize
|
|
|
+ ) {
|
|
|
+ var slice = byteCharacters.slice(offset, offset + sliceSize);
|
|
|
+
|
|
|
+ var byteNumbers = new Array(slice.length);
|
|
|
+ for (var i = 0; i < slice.length; i++) {
|
|
|
+ byteNumbers[i] = slice.charCodeAt(i);
|
|
|
+ }
|
|
|
+
|
|
|
+ var byteArray = new Uint8Array(byteNumbers);
|
|
|
+
|
|
|
+ byteArrays.push(byteArray);
|
|
|
+ }
|
|
|
+
|
|
|
+ var blob = new Blob(byteArrays, { type: contentType });
|
|
|
+ return blob;
|
|
|
+ },
|
|
|
+ async handsFind() {
|
|
|
+ var gesture_imageURLbase64 = this.photograph2();
|
|
|
+ var gesture_ImageURL = gesture_imageURLbase64;
|
|
|
+ var gesture_block = gesture_ImageURL.split(";");
|
|
|
+ var gesture_contentType = gesture_block[0].split(":")[1];
|
|
|
+ var gesture_realData = gesture_block[1].split(",")[1];
|
|
|
+ var gesture_blob = this.b64toBlob(gesture_realData, gesture_contentType);
|
|
|
+ let str = new File([gesture_blob], "filename.png", { type: "image/png" });
|
|
|
+ var gesture_formData = new FormData();
|
|
|
+ gesture_formData.append("image", str);
|
|
|
+ let params = [
|
|
|
+ {
|
|
|
+ gesture_formData,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ let _this = this
|
|
|
+ $.ajax({
|
|
|
+ url: `//ai-api.cocorobo.cn/gesture`,
|
|
|
+ data: gesture_formData,
|
|
|
+ type: "POST",
|
|
|
+ contentType: false, // NEEDED, DON'T OMIT THIS (requires jQuery 1.6+)
|
|
|
+ processData: false, // NEEDED, DON'T OMIT THIS
|
|
|
+ timeout: 10000,
|
|
|
+ })
|
|
|
+ .done(function (res, data) {
|
|
|
+ // console.log(res.data.result[0].classname);
|
|
|
+ let hand = res.data.result;
|
|
|
+ console.log(hand);
|
|
|
+ console.log(hand.length);
|
|
|
+ console.log(hand.length > 0 && _this.isCamera);
|
|
|
+ if (hand.length > 0 && _this.isCamera) {
|
|
|
+ let hands = res.data.result[0]
|
|
|
+ console.log(hands)
|
|
|
+ console.log(hands.classname)
|
|
|
+ /*石头剪刀布的转速 */
|
|
|
+ let buNum = 100;
|
|
|
+ let sNum = 0;
|
|
|
+ let jNum = 50;
|
|
|
+ /**1布2石头3剪刀 */
|
|
|
+ if (hands.classname == "Five") {
|
|
|
+ console.log("布");
|
|
|
+ if (_this.hand == 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ _this.hand = 1;
|
|
|
+ // this.zhuan();
|
|
|
+ _this.$refs.fan.className = "as";
|
|
|
+ } else if (hands.classname == "Fist") {
|
|
|
+ console.log("石头");
|
|
|
+ if (_this.hand == 2) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ _this.hand = 2;
|
|
|
+ //clearInterval(this.timer);
|
|
|
+ _this.$refs.fan.className = "asn";
|
|
|
+ } else if (hands.classname == "Two") {
|
|
|
+ console.log("剪刀");
|
|
|
+ if (_this.hand == 3) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ _this.hand = 3;
|
|
|
+ _this.$refs.fan.className = "ass";
|
|
|
+ // this.zhuann();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log(err);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 调用摄像头
|
|
|
+ callCamera() {
|
|
|
+ const loading = this.$loading.service({
|
|
|
+ background: "rgba(255, 255, 255, 0.7)",
|
|
|
+ target: document.body,
|
|
|
+ });
|
|
|
+ let _this = this;
|
|
|
+ // H5调用电脑摄像头API
|
|
|
+ window.navigator.mediaDevices
|
|
|
+ .getUserMedia({
|
|
|
+ video: true,
|
|
|
+ })
|
|
|
+ .then((success) => {
|
|
|
+ // 摄像头开启成功
|
|
|
+ _this.$refs["video"].srcObject = success;
|
|
|
+ // 实时拍照效果
|
|
|
+ _this.$refs["video"].play();
|
|
|
+ loading.close();
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ // console.error("摄像头开启失败,请检查摄像头是否可用!");
|
|
|
+ _this.isC = false;
|
|
|
+ _this.$message.error("摄像头开启失败,请检查摄像头是否可用!");
|
|
|
+ loading.close();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 拍照
|
|
|
+ photograph2() {
|
|
|
+ let ctx = this.$refs["canvas"].getContext("2d");
|
|
|
+ // 把当前视频帧内容渲染到canvas上
|
|
|
+ ctx.drawImage(this.$refs["video"], 0, 0, 300, 240);
|
|
|
+ // 转base64格式、图片格式转换、图片质量压缩
|
|
|
+ let imgBase64 = this.$refs["canvas"].toDataURL("image/jpeg", 0.7); // 由字节转换为KB 判断大小
|
|
|
+
|
|
|
+ return imgBase64;
|
|
|
+ },
|
|
|
+ // 拍照
|
|
|
+ photograph() {
|
|
|
+ let ctx = this.$refs["canvas"].getContext("2d");
|
|
|
+ // 把当前视频帧内容渲染到canvas上
|
|
|
+ ctx.drawImage(this.$refs["video"], 0, 0, 300, 240);
|
|
|
+ // 转base64格式、图片格式转换、图片质量压缩
|
|
|
+ let imgBase64 = this.$refs["canvas"].toDataURL("image/jpeg", 0.7); // 由字节转换为KB 判断大小
|
|
|
+
|
|
|
+ let str = imgBase64.replace("data:image/jpeg;base64,", "");
|
|
|
+ let strLength = str.length;
|
|
|
+ let fileLength = parseInt(strLength - (strLength / 8) * 2); // 图片尺寸 用于判断
|
|
|
+ let size = (fileLength / 1024).toFixed(2);
|
|
|
+ console.log(size); // 上传拍照信息 调用接口上传图片 .........
|
|
|
+
|
|
|
+ // this.detArr.push(imgBase64);
|
|
|
+ var json = { name: "", img: [] };
|
|
|
+ this.number = this.number + 1;
|
|
|
+ this.upName = "";
|
|
|
+ json.img.push(imgBase64);
|
|
|
+ this.sampleArr.push(json);
|
|
|
+ this.updateMessage = true;
|
|
|
+ },
|
|
|
+ // 关闭摄像头
|
|
|
+ closeCamera() {
|
|
|
+ this.isCamera = false;
|
|
|
+ this.isZuan = false;
|
|
|
+ clearInterval(this.timer);
|
|
|
+ this.$refs.fan.className = "asn";
|
|
|
+ this.timer = null;
|
|
|
+ this.hand = 0;
|
|
|
+ },
|
|
|
+
|
|
|
+ async fnInit() {
|
|
|
+ const model = handPoseDetection.SupportedModels.MediaPipeHands;
|
|
|
+ const detectorConfig = {
|
|
|
+ runtime: "mediapipe", // or 'tfjs'
|
|
|
+ modelType: "full",
|
|
|
+ solutionPath: `/static/hands`,
|
|
|
+ };
|
|
|
+ this.detector = await handPoseDetection.createDetector(
|
|
|
+ model,
|
|
|
+ detectorConfig
|
|
|
+ );
|
|
|
+ },
|
|
|
+ async fnsample() {
|
|
|
+ const labeledFaceDescriptors = await Promise.all(
|
|
|
+ this.sampleArr.map(async (item) => {
|
|
|
+ // 临时图片转码数据,将图片对象转数据矩阵对象
|
|
|
+ let descriptors = [];
|
|
|
+ for (let image of item.img) {
|
|
|
+ const imageEl = await faceapi.fetchImage(image);
|
|
|
+ descriptors.push(await faceapi.computeFaceDescriptor(imageEl));
|
|
|
+ }
|
|
|
+ // 返回图片用户和图片转码数组
|
|
|
+ return new faceapi.LabeledFaceDescriptors(item.name, descriptors);
|
|
|
+ })
|
|
|
+ );
|
|
|
+ // 人脸匹配矩阵数组对象转码结果
|
|
|
+ this.faceMatcher = new faceapi.FaceMatcher(labeledFaceDescriptors);
|
|
|
+ },
|
|
|
+ // 执行遍历识别匹配图片,数值误差越小越精确
|
|
|
+ fnRun() {
|
|
|
+ let ctx = this.$refs["canvas"].getContext("2d");
|
|
|
+ // 把当前视频帧内容渲染到canvas上
|
|
|
+ ctx.drawImage(this.$refs["video"], 0, 0, 300, 240);
|
|
|
+ // 转base64格式、图片格式转换、图片质量压缩
|
|
|
+ let imgBase64 = this.$refs["canvas"].toDataURL("image/jpeg", 0.7); // 由字节转换为KB 判断大小
|
|
|
+ this.detArr = [];
|
|
|
+ this.detArr.push(imgBase64);
|
|
|
+ this.detArr.forEach(async (img) => {
|
|
|
+ let ts = Date.now();
|
|
|
+ // 将图片对象转数据矩阵对象,进行匹配
|
|
|
+ const inputEl = await faceapi.fetchImage(img);
|
|
|
+ const inputDescriptor = await faceapi.computeFaceDescriptor(inputEl);
|
|
|
+ const bestMatch = await this.faceMatcher.findBestMatch(inputDescriptor);
|
|
|
+ // 结果
|
|
|
+ this.resultArr = [];
|
|
|
+ this.resultArr.push({
|
|
|
+ target: img,
|
|
|
+ result: bestMatch.toString(),
|
|
|
+ time: Date.now() - ts + "ms",
|
|
|
+ fps: Math.round(1000 / (Date.now() - ts)),
|
|
|
+ });
|
|
|
+ console.log(this.resultArr);
|
|
|
+ var a = document.getElementsByClassName("pFace");
|
|
|
+ for (var i = 0; i < this.sampleArr.length; i++) {
|
|
|
+ if (this.sampleArr[i].name == bestMatch.label) {
|
|
|
+ // this.closeUpdateMessage = true;
|
|
|
+ // if (this.change == 1) {
|
|
|
+ // this.resultImg.name = this.upName;
|
|
|
+ // } else {
|
|
|
+ // this.resultImg.name = this.sampleArr[i].name;
|
|
|
+ // }
|
|
|
+ this.isdetected = "已识别到" + this.sampleArr[i].name + "的图片";
|
|
|
+ this.shibieImg = this.sampleArr[i].img[0];
|
|
|
+ if (this.shibieImg.length > 0) {
|
|
|
+ a[0].style.width = "300px";
|
|
|
+ }
|
|
|
+ this.resultImg.img[0] = this.sampleArr[i].img[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleClose(done) {
|
|
|
+ done();
|
|
|
+ },
|
|
|
+ // 更换匹配图
|
|
|
+ async fnChange(e) {
|
|
|
+ if (!e.target.files.length) return;
|
|
|
+ this.detArr = [];
|
|
|
+ this.resultArr = [];
|
|
|
+ // 将文件显示为图像并识别
|
|
|
+ e.target.files.forEach(async (file) => {
|
|
|
+ let ts = Date.now();
|
|
|
+ let img = await faceapi.bufferToImage(file);
|
|
|
+ const inputDescriptor = await faceapi.computeFaceDescriptor(img);
|
|
|
+ const bestMatch = await this.faceMatcher.findBestMatch(inputDescriptor);
|
|
|
+ // 结果
|
|
|
+ this.detArr.push(img.src);
|
|
|
+ this.resultArr.push({
|
|
|
+ target: file.name,
|
|
|
+ result: bestMatch.toString(),
|
|
|
+ time: Date.now() - ts + "ms",
|
|
|
+ fps: Math.round(1000 / (Date.now() - ts)),
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ blocklyInit() {
|
|
|
+ this.workspace = Blockly.inject("blocklyDiv", {
|
|
|
+ //工具栏
|
|
|
+ toolbox: document.getElementById("toolbox"),
|
|
|
+ //网格效果
|
|
|
+ grid: { spacing: 20, length: 3, colour: "#ccc", snap: true },
|
|
|
+ //媒体资源
|
|
|
+ media: "../assets/img/",
|
|
|
+ //垃圾桶
|
|
|
+ trashcan: false,
|
|
|
+ });
|
|
|
+ //工作区监听代码生成器
|
|
|
+ this.workspace.addChangeListener(this.myUpdateFunction);
|
|
|
+
|
|
|
+ Blockly.Blocks["iot_lcd_screeninit"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendDummyInput().appendField(
|
|
|
+ new Blockly.FieldImage(
|
|
|
+ require("../assets/img/screen_init_header.png"),
|
|
|
+ 45,
|
|
|
+ 45
|
|
|
+ )
|
|
|
+ );
|
|
|
+ this.appendDummyInput().appendField("LCD屏幕打开");
|
|
|
+ this.setInputsInline(false);
|
|
|
+ this.setPreviousStatement(true);
|
|
|
+ this.setNextStatement(true);
|
|
|
+ this.setColour("#5cb2d6");
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.JavaScript.iot_lcd_screeninit = function (block) {
|
|
|
+ var _code = "screen=1;";
|
|
|
+ return _code;
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.Blocks["iot_lcd_faceinit"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendDummyInput().appendField(
|
|
|
+ new Blockly.FieldImage(
|
|
|
+ require("../assets/img/face_recognition_header.png"),
|
|
|
+ 45,
|
|
|
+ 45
|
|
|
+ )
|
|
|
+ );
|
|
|
+ this.appendDummyInput().appendField("人脸辨识");
|
|
|
+ this.setInputsInline(false);
|
|
|
+ this.setPreviousStatement(true);
|
|
|
+ this.setNextStatement(true);
|
|
|
+ this.setColour("#ee783a");
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.JavaScript.iot_lcd_faceinit = function (block) {
|
|
|
+ var _code = "face=1;";
|
|
|
+ return _code;
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.Blocks["iot_lcd_policeinit"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendDummyInput().appendField(
|
|
|
+ new Blockly.FieldImage(
|
|
|
+ require("../assets/img/screen_init_header.png"),
|
|
|
+ 45,
|
|
|
+ 45
|
|
|
+ )
|
|
|
+ );
|
|
|
+ this.appendDummyInput().appendField("电子警察组件");
|
|
|
+ this.setInputsInline(false);
|
|
|
+ this.setPreviousStatement(true);
|
|
|
+ this.setNextStatement(true);
|
|
|
+ this.setColour("#1b5873");
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.JavaScript.iot_lcd_policeinit = function (block) {
|
|
|
+ var _code = "police=1;";
|
|
|
+ return _code;
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.Blocks["logic_loop"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendStatementInput("NAME")
|
|
|
+ .setCheck(null)
|
|
|
+ .appendField("重复执行");
|
|
|
+ this.setPreviousStatement(true, null);
|
|
|
+ this.setNextStatement(true, null);
|
|
|
+ this.setColour("#5b80a5");
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+ Blockly.JavaScript["logic_loop"] = function (block) {
|
|
|
+ var statements_name = Blockly.JavaScript.statementToCode(block, "NAME");
|
|
|
+ // TODO: Assemble JavaScript into code variable.
|
|
|
+ var code = "for;\n";
|
|
|
+ return code;
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.Blocks.controls_repeat_forever = {
|
|
|
+ init: function () {
|
|
|
+ this.jsonInit({
|
|
|
+ message0: Blockly.Msg.CONTROLS_REPEAT_FOREVER,
|
|
|
+ previousStatement: null,
|
|
|
+ nextStatement: null,
|
|
|
+ colour: "#5ba55b",
|
|
|
+ tooltip: Blockly.Msg.CONTROLS_REPEAT_FOREVER_TOOLTIP,
|
|
|
+ helpUrl: Blockly.Msg.CONTROLS_REPEAT_HELPURL,
|
|
|
+ });
|
|
|
+ this.appendStatementInput("DO").appendField(
|
|
|
+ Blockly.Msg.CONTROLS_REPEAT_INPUT_DO
|
|
|
+ );
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.JavaScript.controls_repeat_forever = function (a) {
|
|
|
+ var d = Blockly.JavaScript.statementToCode(a, "DO");
|
|
|
+ d = Blockly.JavaScript.addLoopTrap(d, a);
|
|
|
+ return "while (true) {\n" + d + "}\n";
|
|
|
+ };
|
|
|
+
|
|
|
+ Blockly.Blocks["logic_number"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendDummyInput().appendField(
|
|
|
+ new Blockly.FieldTextInput("0"),
|
|
|
+ "number"
|
|
|
+ );
|
|
|
+ this.setInputsInline(true);
|
|
|
+ this.setOutput(true, "Number");
|
|
|
+ this.setColour("#5b80a5");
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+ Blockly.JavaScript["logic_number"] = function (block) {
|
|
|
+ var text_number = block.getFieldValue("number");
|
|
|
+ // TODO: Assemble JavaScript into code variable.
|
|
|
+ var code = text_number;
|
|
|
+ // TODO: Change ORDER_NONE to the correct strength.
|
|
|
+ return [code, Blockly.JavaScript.ORDER_NONE];
|
|
|
+ };
|
|
|
+ Blockly.Blocks["ai_gesture"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendDummyInput()
|
|
|
+ .appendField("AI手势识别")
|
|
|
+ .appendField(new Blockly.FieldDropdown([["ID", "ID"]]), "ID");
|
|
|
+ this.setInputsInline(true);
|
|
|
+ this.setOutput(true, null);
|
|
|
+ this.setColour(285);
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+ Blockly.JavaScript["ai_gesture"] = function (block) {
|
|
|
+ var dropdown_id = block.getFieldValue("ID");
|
|
|
+ // TODO: Assemble JavaScript into code variable.
|
|
|
+ var code = "hands";
|
|
|
+ // TODO: Change ORDER_NONE to the correct strength.
|
|
|
+ return [code, Blockly.JavaScript.ORDER_NONE];
|
|
|
+ };
|
|
|
+ Blockly.Blocks["ai_motor"] = {
|
|
|
+ init: function () {
|
|
|
+ this.appendDummyInput()
|
|
|
+ .appendField("马达")
|
|
|
+ .appendField(new Blockly.FieldDropdown([["M1", "M1"]]), "motor")
|
|
|
+ .appendField("以速度")
|
|
|
+ .appendField(new Blockly.FieldTextInput("0"), "speed")
|
|
|
+ .appendField("转动");
|
|
|
+ this.setPreviousStatement(true, null);
|
|
|
+ this.setNextStatement(true, null);
|
|
|
+ this.setColour(285);
|
|
|
+ this.setTooltip("");
|
|
|
+ this.setHelpUrl("");
|
|
|
+ },
|
|
|
+ };
|
|
|
+ Blockly.JavaScript["ai_motor"] = function (block) {
|
|
|
+ var dropdown_motor = block.getFieldValue("motor");
|
|
|
+ var text_speed = block.getFieldValue("speed");
|
|
|
+ // TODO: Assemble JavaScript into code variable.
|
|
|
+ var code = "motor=" + text_speed;
|
|
|
+ // TODO: Change ORDER_NONE to the correct strength.
|
|
|
+ return code;
|
|
|
+ };
|
|
|
+ },
|
|
|
+ // 代码生成器
|
|
|
+ myUpdateFunction(event) {
|
|
|
+ var code = Blockly.JavaScript.workspaceToCode(this.workspace);
|
|
|
+ // debugger;
|
|
|
+ return code;
|
|
|
+ },
|
|
|
+ // 清空工作区
|
|
|
+ clearBlockData() {
|
|
|
+ this.workspace.clear();
|
|
|
+ },
|
|
|
+ // 回显工作区中的xml结构
|
|
|
+ setBlockData(xmlText) {
|
|
|
+ this.clearBlockData();
|
|
|
+ const xml = Blockly.Xml.textToDom(xmlText);
|
|
|
+ Blockly.Xml.domToWorkspace(xml, this.workspace);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.blocklyInit();
|
|
|
+ // this.fnInit();
|
|
|
+ this.callCamera();
|
|
|
+ this.videoEl = this.$refs.video;
|
|
|
+ this.canvasEL = this.$refs.canvasDOM;
|
|
|
+ var xmlText =
|
|
|
+ '<xml xmlns="https://developers.google.com/blockly/xml"><block type="iot_lcd_screeninit" id="{3nwI]%M1OB@v~%c^GJ*" x="270" y="150"><next><block type="controls_repeat_forever" id="%RwQN-vn~S,Qz:U%Dp2C"><statement name="DO"><block type="controls_if" id="tTNBMMPITBf)yE@hA}Lt"><value name="IF0"><block type="logic_compare" id="kW(af5I*_dMR|37aEHA8"><field name="OP">EQ</field><value name="A"><block type="ai_gesture" id="Msy)DT1[/[pA7vU$u2BD"><field name="ID">ID</field></block></value><value name="B"><block type="logic_number" id="F7nQ+Rn{h=Z-:`nxfOV{"><field name="number">1</field></block></value></block></value><statement name="DO0"><block type="ai_motor" id="9=+m`N)^mOs;,8?7ZZ:K"><field name="motor">M1</field><field name="speed">100</field></block></statement><next><block type="controls_if" id="~ykQvxBR]wnz6FHIGc^s"><value name="IF0"><block type="logic_compare" id="+v0*1:oG$.+wMFe_g_F@"><field name="OP">EQ</field><value name="A"><block type="ai_gesture" id=",|{9@1In%8:74vXPPn{O"><field name="ID">ID</field></block></value><value name="B"><block type="logic_number" id="[J4:1z-D*iz3){K$v[gM"><field name="number">2</field></block></value></block></value><statement name="DO0"><block type="ai_motor" id="XIVuWHcKb[O5|0*HC_Em"><field name="motor">M1</field><field name="speed">0</field></block></statement><next><block type="controls_if" id="bNZCd.Zj2arimcA^dZvc"><value name="IF0"><block type="logic_compare" id="ul/^s*@P{HkWe6(^9fgn"><field name="OP">EQ</field><value name="A"><block type="ai_gesture" id="Wd)15h0yyFb`)Sy:H}0w"><field name="ID">ID</field></block></value><value name="B"><block type="logic_number" id="1f0Wep9dlC0i-})rTiqN"><field name="number">3</field></block></value></block></value><statement name="DO0"><block type="ai_motor" id="bY;.J)_PvmP5TAD/KDs,"><field name="motor">M1</field><field name="speed">50</field></block></statement></block></next></block></next></block></statement></block></next></block></xml>';
|
|
|
+ this.setBlockData(xmlText);
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+html,
|
|
|
+body {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy >>> .el-form-item__label {
|
|
|
+ width: 50px !important;
|
|
|
+ margin-left: 35px !important;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog_diy >>> .el-form-item__content {
|
|
|
+ margin-left: 0 !important;
|
|
|
+}
|
|
|
+
|
|
|
+.update {
|
|
|
+ margin-left: 20%;
|
|
|
+ font-size: 16px !important;
|
|
|
+}
|
|
|
+
|
|
|
+.tip {
|
|
|
+ margin: 25px 0 30px 20px;
|
|
|
+}
|
|
|
+#fan > img,
|
|
|
+#fanB > img,
|
|
|
+#motor > img,
|
|
|
+#base > img,
|
|
|
+#line > img,
|
|
|
+.right > img,
|
|
|
+#a4 > img,
|
|
|
+#screan > img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
+.img {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: flex-start;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 500px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+#fan {
|
|
|
+ /* width: 425px; */
|
|
|
+ width: 283.33px;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 5;
|
|
|
+ /* left: 150px; */
|
|
|
+ left: 100px;
|
|
|
+ top: 0;
|
|
|
+ transition: all 1s;
|
|
|
+ transform: rotate(0deg);
|
|
|
+}
|
|
|
+
|
|
|
+#fanB {
|
|
|
+ /* width: 116.6px; */
|
|
|
+ width: 77.733px;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 4;
|
|
|
+ /* left: 300px;
|
|
|
+ top: 155px; */
|
|
|
+ left: 200px;
|
|
|
+ top: 103.333px;
|
|
|
+}
|
|
|
+
|
|
|
+#base {
|
|
|
+ /* width: 423.33px; */
|
|
|
+ width: 282.21px;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 4;
|
|
|
+ /* top: 295px;
|
|
|
+ left: 0; */
|
|
|
+ top: 196.666px;
|
|
|
+ left: 0;
|
|
|
+}
|
|
|
+
|
|
|
+#motor {
|
|
|
+ /* width: 134.16px; */
|
|
|
+ width: 89.44px;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 3;
|
|
|
+ /* left: 292px;
|
|
|
+ top: 175px; */
|
|
|
+ left: 194.666px;
|
|
|
+ top: 116.666px;
|
|
|
+}
|
|
|
+
|
|
|
+#a4 {
|
|
|
+ /* width: 210px; */
|
|
|
+ width: 140px;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 5;
|
|
|
+ /* top: 517px;
|
|
|
+ left: 111px; */
|
|
|
+ top: 344.666px;
|
|
|
+ left: 74px;
|
|
|
+}
|
|
|
+#screan {
|
|
|
+ /* width: 178.33px; */
|
|
|
+ width: 118.886px;
|
|
|
+ position: absolute;
|
|
|
+ /* left: -19px;
|
|
|
+ top: 126px; */
|
|
|
+ left: -12.666px;
|
|
|
+ top: 84px;
|
|
|
+}
|
|
|
+#line {
|
|
|
+ /* width: 470.83px; */
|
|
|
+ width: 313.886px;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 2;
|
|
|
+ /* top: 245px;
|
|
|
+ left: -113px; */
|
|
|
+ top: 163.333px;
|
|
|
+ left: -75.333px;
|
|
|
+}
|
|
|
+
|
|
|
+.button {
|
|
|
+ color: #fff;
|
|
|
+ background: #8ca1de;
|
|
|
+ width: 550px;
|
|
|
+ height: 55px;
|
|
|
+ font-size: 20px;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 55px;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 10%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
+.right {
|
|
|
+ width: 40px;
|
|
|
+ position: absolute;
|
|
|
+ left: 55%;
|
|
|
+ top: 70%;
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+
|
|
|
+.dark {
|
|
|
+ background: #5b79d0;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.controlZ {
|
|
|
+ width: 560px;
|
|
|
+ height: 620px;
|
|
|
+ /* margin-left: calc(50% - (490px / 2)); */
|
|
|
+ position: relative;
|
|
|
+ top: 10%;
|
|
|
+ left: 5%;
|
|
|
+}
|
|
|
+
|
|
|
+.cameraZ {
|
|
|
+ display: flex;
|
|
|
+ height: 340px;
|
|
|
+ flex-direction: column;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ width: 300px;
|
|
|
+}
|
|
|
+
|
|
|
+.cameraBtn {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.cameraBtn button {
|
|
|
+ margin: 0 0 10px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.face {
|
|
|
+ position: absolute;
|
|
|
+ z-index: 9999;
|
|
|
+ padding: 5px 0px;
|
|
|
+ left: 0;
|
|
|
+ top: 37px;
|
|
|
+}
|
|
|
+
|
|
|
+.kuang {
|
|
|
+ position: absolute;
|
|
|
+ z-index: 10000;
|
|
|
+ padding: 26.5px 0;
|
|
|
+ left: 0;
|
|
|
+ top: 37px;
|
|
|
+}
|
|
|
+
|
|
|
+.pFace {
|
|
|
+ /* width: 300px; */
|
|
|
+ /* height: 240px; */
|
|
|
+ width: 150px;
|
|
|
+ margin: 0 auto;
|
|
|
+ margin-top: 220px;
|
|
|
+}
|
|
|
+
|
|
|
+.close > img,
|
|
|
+.save > img,
|
|
|
+.pFace > img,
|
|
|
+.open > img,
|
|
|
+.spotPhoto > img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.cameraBtn {
|
|
|
+ margin-top: 80px;
|
|
|
+}
|
|
|
+
|
|
|
+.close {
|
|
|
+ margin-bottom: 25px;
|
|
|
+}
|
|
|
+
|
|
|
+.isPhoto {
|
|
|
+ height: 410px;
|
|
|
+ padding: 80px 0 0 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.isPhoto > span {
|
|
|
+ font-size: 25px;
|
|
|
+ color: #ccc;
|
|
|
+ margin-left: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.nav {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 32px;
|
|
|
+ color: #ccc;
|
|
|
+}
|
|
|
+
|
|
|
+.spot {
|
|
|
+ background: #64ff64;
|
|
|
+ color: #fff;
|
|
|
+ width: 140px;
|
|
|
+ height: 40px;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 40px;
|
|
|
+ border-radius: 20px;
|
|
|
+ margin-top: 25px;
|
|
|
+ cursor: pointer;
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+
|
|
|
+.spotNumber {
|
|
|
+ text-align: center;
|
|
|
+ margin: 0 auto;
|
|
|
+ font-size: 20px;
|
|
|
+ color: #8c8c8c;
|
|
|
+}
|
|
|
+
|
|
|
+.spotPhoto {
|
|
|
+ width: 300px;
|
|
|
+ height: 245px;
|
|
|
+}
|
|
|
+
|
|
|
+.spotPhoto {
|
|
|
+ margin-top: 20px;
|
|
|
+ font-size: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.gdt {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.ablockly,
|
|
|
+#blocklyDiv {
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.blocklySvg {
|
|
|
+ height: 905px !important;
|
|
|
+}
|
|
|
+
|
|
|
+.ablockly {
|
|
|
+ position: relative;
|
|
|
+ width: 50%;
|
|
|
+}
|
|
|
+
|
|
|
+.container {
|
|
|
+ width: 50%;
|
|
|
+ height: 100%;
|
|
|
+ min-width: 700px;
|
|
|
+}
|
|
|
+</style>
|