上传视频,逐帧识别,绘制多边形

功能描述:

1、web端界面展示,支持上传本地视频,进行逐帧识别并且返回模型推理结果进行展示(前端上传原视频,后端返回逐帧识别的视频)

2、支持设定电子围栏区域,设定围栏后界面需要相应可视化,只有区域内的事件才能报警(后端返回一个图片,前端在图片上绘制多边形,把多边形的顶点传给后端,后端返回带有图标的视频)

详细代码

<template>
  <div class="home">
    <div style="display:flex;justify-content: center;">
      <el-upload
        class="upload-demo"
        action="http://10.127.173.6:30023/api/upload"
        multiple
        :limit="1"
        accept=".mp4"
        :before-upload="beforeUpload"
        :on-exceed="handleExceed"
        :on-success="handleSuccess"
        :on-error="handleError"
        v-loading="loading"
      >
        <el-button size="small" type="primary">点击上传</el-button>
      </el-upload>
      <el-button v-if="videoSrc" type="primary" @click="initKonvaStage('poly')" style="height: 40px;margin: 0 10px;">获取视频当前帧</el-button>
      <el-button v-if="videoSrc" type="primary" @click="checkVideo" style="height: 40px">视频检测</el-button>
    </div>
 

    <!-- 视频 -->
    <div style="display: flex;">
      <!-- 原始视频 -->
      <div v-loading="loading" v-if="videoSrc">
        <video ref="video"  :src="videoSrc" controls style="width: 640px; height: 360px;margin-top: 20px;"></video>
        <div>原始视频</div>
        <!-- 多边形绘制 -->
        <div v-if="videoSrc" id="map" ref="map"></div>
      </div>
      <!-- 检测视频 -->
       <div v-loading="checkVideoLoading" element-loading-text="识别中,请等待..." style="width: 640px;height: 360px;">
        <div v-if="checkVideoSrc">
          <video :src="checkVideoSrc" controls style="width: 640px; height: 360px; margin-top: 20px;margin-left: 10px;"></video>
          <div>检测视频</div>
        </div>
       </div>
    </div>
  </div>
</template>
<script>
import axios from 'axios'
import Konva from "konva";
export default {
  name: 'HomeView',
  data() {
    return {
      w: 1920,
      h: 1080,
      isShow: false,
      videoSrc: '',
      loading: false,
      checkVideoLoading: false,
      // 图片
      capturedImage:  null,
      // 多边形绘制
      stage: null,
      layer: null,
      shape: null,
      // image: { src: `${this.capturedImage}`},
      currentTool: "",
      drawing: false, //一开始不能绘画
      currentDrawingShape: null, //现在绘画的图形
      pointStart: [], //记录鼠标按下的起始坐标
      polygonPoints: [], //存储绘画多边形各个顶点的数组
      uploadPointArr: [],
      filePath: '',
      checkVideoSrc: ''
    };
  },

  methods: {
    beforeUpload(file) {
      this.videoSrc = ''
      const fileName = file.name
      const extention = fileName.split('.')[1]
      this.loading = true
      if( extention != 'mp4') {
        this.$message.error('上传的文件格式不正确,请上传MP4格式的文件!');
        this.loading = false
        return false
      }
    },
    handleExceed(files, fileList) {
      this.loading = false
      this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
    },
    handleSuccess(data){
      this.filePath = data.file_path
      this.videoSrc = 'http://10.127.173.6:30023/static/' + data.file_path
      this.loading = false
      this.capturedImage = 'http://10.127.173.6:30023/static/' + data.frame_file
    },
    handleError() {
      this.loading = false
    },
    
    // 视频检测
    checkVideo() {
      this.uploadPointArr.forEach((item,index) => {
        if(INDEX % 2 == 0) {
          this.uploadPointArr[index] = (item*this.w) / 640
        } else {
          this.uploadPointArr[index] = (item * this.h) / 360
        }
      })
      this.checkVideoLoading = true
      axios.post('http://10.127.173.6:30023/api/detect',
        {
          fileName: this.filePath,
          polygon_points: this.uploadPointArr
        }
      ).then((res) => {
        this.checkVideoLoading = false
        this.checkVideoSrc = 'http://10.127.173.6:30023/static/' + res.data.file_path
      })
      .catch(() => {
        this.checkVideoLoading = false
      })
    },

    /**
     *初始化konva舞台
    */
    initKonvaStage(key) {
      this.currentTool = key
      //1实例化stage层
      this.stage = new Konva.Stage({
        container: "map",
        width: this.$refs.map.clientWidth,
        height: this.$refs.map.clientHeight,
      });
      this.stage.container().style.cursor = "crosshair";

      //2实例化layer层
      this.layer = new Konva.Layer();
      var imageObj = new Image();
      //imageObj的this是imagedom对象,不是vc
      var vc_this = this;
      imageObj.onload = function () {
        //3实例化shape层
        vc_this.shape = new Konva.Image({
          x: 0,
          y: 0,
          width: vc_this.stage.width(),
          height: vc_this.stage.height(),
          image: imageObj,
        });

        //4将layer层添加到stage层
        vc_this.stage.add(vc_this.layer);

        // 5将shape层添加到layer层
        vc_this.layer.add(vc_this.shape);
      };
      imageObj.src = this.capturedImage;
      // imageObj.src = require(this.capturedImage)

      //给***舞台***绑定事件
      //鼠标按下
      this.stage.on("mousedown", (e) => {
        //图形起始点只能在图片层上,移除变形框
        if (e.target === this.shape) {
          // 如果有,就移除舞台上唯一一个的变形框
          if (this.stage.find("Transformer").length != 0) {
            this.stage.find("Transformer")[0].destroy();
          }
          //如果不在绘画且舞台上的多边形被选中
          if (!this.drawing && this.stage.find("Circle").length != 0) {
            var circlePoints = this.stage.find("Circle");
            for (var i = 0; i < circlePoints.length; i++) {
              if (circlePoints[i].visible()) {
                //隐藏顶点
                this.stage.find("Circle").forEach((element) => {
                  element.hide();
                });
                return;
              }
            }
          }
          this.layer.draw();
          //开始初始绘画
          this.stageMousedown(this.currentTool, e);
          return;
        }
        //允许后续点绘画在其他图形上
        if (this.drawing) {
          this.stageMousedown(this.currentTool, e);
          return;
        }
      });
      //鼠标移动
      this.stage.on("mousemove", (e) => {
        if (this.currentTool && this.drawing) {
          //绘画中
          this.stageMousemove(this.currentTool, e);
        }
      });
      //鼠标放开
      this.stage.on("mouseup", (e) => {
        this.stageMouseup(this.currentTool, e);
      });
    },

    /**
     * 圆形
     * @param //x x坐标
     * @param //y y坐标
     */

    drawCircle(x, y) {
      const circle = new Konva.Circle({
        name: "circle",
        x: x,
        y: y,
        radius: 5,
        visible: true, //是否显示
        fill: "#ffffff",
        stroke: "#333333",
        draggable: false,
        strokeWidth: 0.5,
      });
      var vc_this = this;
      var xChange, yChange;
      this.layer.add(circle);
      this.layer.draw();
      circle.on("dragstart", () => {
        var polyPoints = vc_this.currentDrawingShape
          .getChildren((element) => {
            return element.getClassName() === "Line";
          })[0]
          .points();
        //查找拖拽了多边形的哪个点
        for (var i = 0; i < polyPoints.length; i += 2) {
          if (
            circle.getAttr("x") == polyPoints[i] &&
            circle.getAttr("y") == polyPoints[i + 1]
          ) {
            xChange = i;
            yChange = i + 1;
            break;
          }
        }
      });
      circle.on("dragmove", (e) => {
        //更改拖拽多边形点的位置
        var polyPoints = vc_this.currentDrawingShape
          .getChildren((element) => {
            return element.getClassName() === "Line";
          })[0]
          .points();
        /*   e.evt.offsetX - vc_this.currentDrawingShape.getAttr('x') ---> 抵消拖动组的xy影响  */
        polyPoints[xChange] =
          e.evt.offsetX - vc_this.currentDrawingShape.getAttr("x");
        polyPoints[yChange] =
          e.evt.offsetY - vc_this.currentDrawingShape.getAttr("y");
        vc_this.currentDrawingShape
          .getChildren((element) => {
            return element.getClassName() === "Line";
          })[0]
          .points(polyPoints);
      });
      return circle;
    },

    /**
     *多边形
     * @param points 多边形绘画的各个顶点,类型数组
     */

    drawPloygon(points) {
      const poly = new Konva.Line({
        name: "poly",
        points: points,
        fill: "red",
        stroke: "red",
        strokeWidth: 1,
        draggable: false,
        opacity: 0.3,
        lineCap: "round",
        lineJoin: "round",
        closed: true,
        strokeScaleEnabled: false,
      });
      this.currentDrawingShape = poly;
      this.layer.add(poly);
      this.layer.draw();
      var vc_this = this;
      poly.on("mouseenter", () => {
        vc_this.stage.container().style.cursor = "move";
      });
      poly.on("mouseleave", () => {
        vc_this.stage.container().style.cursor = "crosshair";
      });
      poly.on("mousedown", () => {
        //如果不是正在绘画图形时,可以显示顶点
        if (!vc_this.drawing) {
          vc_this.stage.container().style.cursor = "move";
          console.log("mousedown");
          //设置现在绘画节点的对象为该多边形和顶点的组
          vc_this.currentDrawingShape = poly.getParent();
          //先隐藏全部顶点
          vc_this.stage.find("Circle").forEach((element) => {
            element.hide();
            //解绑第一个红色顶点的事件
            element.off("mousedown");
          });
          //显示现在操作多边形的原来的顶点
          vc_this.currentDrawingShape
            .getChildren((element) => {
              return element.getClassName() === "Circle";
            })
            .forEach((element) => {
              element.show();
              element.setAttr("draggable", false);
            });
          // 如果要让顶点和多边形一起拖拽,必须设置,多边形不能被拖拽
          poly.setAttr("draggable", false);
          poly.getParent().setAttr("draggable", false);
          //使所有顶点在顶层显示
          vc_this.stage.find("Circle").forEach((element) => {
            element.moveToTop();
          });
          vc_this.layer.draw();
        } else {
          //绘画时,鼠标移入多边形,设置组不可以拖动
          vc_this.stage.container().style.cursor = "crosshair";
          poly.getParent().setAttr("draggable", false);
        }
      });
      poly.getParent().on("dragend", () => {
        vc_this.stage.container().style.cursor = "crosshair";
        poly.getParent().setAttr("draggable", false);
      });
      return poly;
    },

    /**
     * 组件el-menu点击事件
     * @param key 索引值
     * @param keyPath
     */

    handleSelect(key) {
      //设置当前工具
      this.currentTool = key
    },

    /**
     * 在舞台上鼠标点下发生的事件
     * @param currentTool 当前选择的工具
     * @param e 传入的event对象
     */
    stageMousedown(currentTool, e) {
      switch (currentTool) {
        case "poly":
          //如果数组长度小于2,初始化多边形和顶点,是它们成为一组,否则什么都不做
          if (this.polygonPoints.length < 2) {
            var x = e.evt.offsetX,
              y = e.evt.offsetY;
            //拖拽组
            var group = new Konva.Group({
              x: 0,
              y: 0,
              name: "pointsAndPoly",
              draggable: false,
            });
            //添加多边形的点
            group.add(this.addPoint(e));
            //绘画多边形
            this.polygonPoints = [x, y];
            group.add(this.drawPloygon(this.polygonPoints));
            //使所有顶点在顶层显示
            this.stage.find("Circle").forEach((element) => {
              element.moveToTop();
            });
            this.layer.add(group);
            this.stage.draw();
          } 
          //多边形增加顶点
          else {
            x = e.evt.offsetX,
              y = e.evt.offsetY;
            //group继续添加多边形的点
            this.currentDrawingShape.getParent().add(this.addPoint(e));
            this.polygonPoints.push(x);
            this.polygonPoints.push(y);
            //绘画多边形
            this.currentDrawingShape.setAttr("points", this.polygonPoints);
            //使所有顶点在顶层显示
            this.stage.find("Circle").forEach((element) => {
              element.moveToTop();
            });
            this.stage.draw();
            this.arr = this.polygonPoints
          }
          break;
        default:
          break;
      }
      this.drawing = true;
    },

    /**
     * 鼠标在舞台上移动事件
     * @param currentTool 当前选择的工具
     * @param e 传入的event对象
     */

    stageMousemove(currentTool, e) {
      switch (currentTool) {
        case "poly":
          //多边形初始化后,如果数组长度大于2,鼠标移动时,实时更新下一个点
          if (this.polygonPoints.length >= 2) {
            var x = e.evt.offsetX,
              y = e.evt.offsetY;
            var tempPoints = this.polygonPoints.concat();
            tempPoints.push(x);
            tempPoints.push(y);
            this.currentDrawingShape.setAttr("points", tempPoints);
          }
          break;
        default:
          break;
      }
      this.layer.draw();
    },

    /**
     * 鼠标在舞台上移动事件
     * @param currentTool 当前选择的工具
     * @param e 传入的event对象
     */

    stageMouseup(currentTool) {
      switch (currentTool) {
        default:
          break;
      }
      this.layer.draw();
    },

    /**
     * 增加多边形顶点
     * @param e 传入的event对象
     */
    addPoint(e) {
      if (this.polygonPoints.length == 0) {
        var vc_this = this;
        //将第一个点标红,并显示
        return this.drawCircle(e.evt.offsetX, e.evt.offsetY)
          .setAttrs({
            fill: "red",
          })
          .show()
          .on("mousedown", () => {
            //点击第一个红点,绘画多边形结束
            //绘画多边形
            this.currentDrawingShape.setAttr("points", this.polygonPoints);
            //结束绘画多边形封闭
            //  this.currentDrawingShape.setAttr('closed', true);
            vc_this.drawing = false;
            vc_this.polygonPoints = [];
            //隐藏所有顶点
            vc_this.stage.find("Circle").forEach((element) => {
              element.hide();
            });
            //所有顶点变为白色
            vc_this.stage.find("Circle").forEach((element) => {
              element.setAttrs({
                fill: "#ffffff",
              });
            });
            //把现在的绘画对象更改为点和多边形合成的组
            this.currentDrawingShape = this.currentDrawingShape.getParent();
          });
      } else {
        //绘画点并显示
        return this.drawCircle(e.evt.offsetX, e.evt.offsetY).show();
      }
    },
  },
}
</script>
<style>
  .home{
    width:65%;
    margin: 0 auto;
  }
  #map {
    background: #ddd;
    overflow: hidden;
    width: 640px;
    height: 360px;
    margin: 20px;
  }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值