图片识别少不了画框,前端画框就用canvas,后端返回画框数据点,图片可以是前端自己传的也可以是后端返回的。
实现思路:
1、算好比例尺
2、确定上、左位置,确定宽高(后端如果给上下左右,前端自行计算宽高)
3、图片预加载时画图
4、注意 js 的乘除会有出入,所以要精确化处理

效果图
在这里插入图片描述

实现代码:

<template>
  <div class="carDetect">
    <div class="uploadImg">
      <el-button type="primary">上传图片</el-button>
      <input type="file" class="upload" @change="uploadImg($event)" />
    </div>
    <div class="faceCon">
      <div class="canvasCon">
        <canvas width="800" height="400" ref="canvas"></canvas>
      </div>

      <div class="faceInfo">
        <div
          v-for="(item, index) in imgCarArr"
          :key="index"
          :class="index == activeIndex ? 'active' : ''"
        >
          <img
            :class="index == activeIndex ? 'active' : ''"
            :src="item"
            alt=""
            @click="showCarInfo(index)"
          />
        </div>
      </div>
      <veri-car :carData="carOneData" :visibleCar.sync="visibleCar"></veri-car>
      <!-- <div class="faceInfo">
        <div
          v-for="(item, index) in imgArr"
          :key="index"
          :class="index == activeIndex ? 'active' : ''"
        >
          <img
            :class="index == activeIndex ? 'active' : ''"
            :src="item"
            alt=""
            @click="showFaceInfo(index)"
          />
        </div>
      </div>
      <veri-car-face
        :faceData="faceOneData"
        :visible.sync="visible"
      ></veri-car-face> -->
    </div>
    <canvas v-for="i in canvasLength" :key="i" :id="'canvas' + i"></canvas>
  </div>
</template>
<script>
var div = require("../../../utils/accDiv.js");
var mul = require("../../../utils/accMul.js");
//使用用户上传图片画图时需要做的处理
uploadImg(el) {
      this.faceOneData = {};
      this.carOneData = {};
      if (!el.target.files[0].size) return; // 如果文件大小为0,则返回
      if (
        el.target.files[0].type.indexOf("image/png") === -1 &&
        el.target.files[0].type.indexOf("image/jpeg") === -1 &&
        el.target.files[0].type.indexOf("image/svg") === -1
      ) {
        // 如果不是图片格式
        this.$message.warning("请选择文件为png/jpg格式的图片");
      } else {
        const that = this;
        const reader = new FileReader(); // 创建读取文件对象
        reader.readAsDataURL(el.target.files[0]); // 发起异步请求,读取文件
        reader.onload = function() {
          that.value = this.result;
        };
        this.getFaceInfo(el.target.files[0]);
      }
    },
// 正式画图
drawFace(data) { //data就是后端返回的数据
      this.imgCarArr = []; //存放框出来的每一个具体图片
      this.canvasLength = data.length; //多个框时候用来遍历展示
      var canvas = this.$refs.canvas;  //获取元素实例
      var context = canvas.getContext("2d");  //getContext() 方法返回一个用于在画布上绘图的环境。
      var ratioX = ""; //X轴比例尺
      var ratioY = ""; //Y轴比例尺
      context.clearRect(0, 0, 800, 400); //画布大小和起止点
      var newImg = new Image(); //创建一个Image对象

      newImg.src = this.value;  //返回的图片地址,或者自己上传的base64图片
      newImg.onload = () => { //img.onload 实现图片预加载
        ratioX = div.accDiv(canvas.width, newImg.width);  
        ratioY = div.accDiv(canvas.height, newImg.height);
        context.drawImage(newImg, 0, 0, canvas.width, canvas.height); //drawImage() 方法在画布上绘制图像、画布或视频。
        data.forEach((item, index) => { //如果返回数据只有一个框,可以不循环
          context.beginPath();
          if (item.faceInfoParam) {
            let facePosition =
              item.faceInfoParam.faceDetectInfoParam.face_position;
            context.rect( //rect() 方法创建矩形
              mul.accMul(ratioX, facePosition.left), //精确计算乘(比例尺*坐标点)
              mul.accMul(ratioY, facePosition.top),
              mul.accMul(ratioX, facePosition.width),
              mul.accMul(ratioY, facePosition.height)
            );

            context.lineWidth = 1;
            context.strokeStyle = "#0f0";
            context.stroke();
            context.closePath();

            // 获取人脸部分
            var imageData = context.getImageData(
              mul.accMul(ratioX, facePosition.left),
              mul.accMul(ratioY, facePosition.top),
              mul.accMul(ratioX, facePosition.width),
              mul.accMul(ratioY, facePosition.height)
            );
            // 人脸关键点
             item.faceInfoParam.faceDetectInfoParam.syPointParams.forEach( //遍历返回值
               (v, i) => {
                 context.beginPath();
                 context.arc( //arc() 方法创建弧/曲线(用于创建圆或部分圆),中心:arc(100,75,50,0*Math.PI,1.5*Math.PI)
                   mul.accMul(ratioX, v.x),
                   mul.accMul(ratioY, v.y),
                   1.5,
                   0,
                   Math.PI * 2
                 );
                 // context.strokeStyle = "#00f";
                 context.fillStyle = "#5AC5BD";
                 context.fill();
                 context.closePath();
               }
             );

            // 创建新的canvas存放imgData
            var newCanvas = document.getElementById("canvas" + (index + 1));
            newCanvas.width = mul.accMul(ratioX, facePosition.width);
            newCanvas.height = mul.accMul(ratioY, facePosition.height);
            var newCxt = newCanvas.getContext("2d");
            newCxt.putImageData(imageData, 0, 0); //通过 getImageData() 复制画布上指定矩形的像素数据

            this.imgArr.push(newCanvas.toDataURL()); //得到以 base64 编码的 dataURL
          }
          if (item.vehicleInfoParam) {
            let vechiPosition =
              item.vehicleInfoParam.vehicle_detect_res.syRectParam;
            context.rect(
              mul.accMul(ratioX, vechiPosition.left),
              mul.accMul(ratioY, vechiPosition.top),
              mul.accMul(ratioX, vechiPosition.width),
              mul.accMul(ratioY, vechiPosition.height)
            );

            context.lineWidth = 1;
            context.strokeStyle = "#0f0";
            context.stroke(); //使用 stroke() 方法在画布上绘制确切的路径。
            context.closePath(); //closePath() 方法创建从当前点到开始点的路径。

            // 获取车辆部分
            var imageCarData = context.getImageData(
              mul.accMul(ratioX, vechiPosition.left),
              mul.accMul(ratioY, vechiPosition.top),
              mul.accMul(ratioX, vechiPosition.width),
              mul.accMul(ratioY, vechiPosition.height)
            );

            // 创建新的canvas存放imgData
            var newCarCanvas = document.getElementById("canvas" + (index + 1));
            newCarCanvas.width = mul.accMul(ratioX, vechiPosition.width);
            newCarCanvas.height = mul.accMul(ratioY, vechiPosition.height);
            var newCarCxt = newCarCanvas.getContext("2d");
            newCarCxt.putImageData(imageCarData, 0, 0); //通过 getImageData() 复制画布上指定矩形的像素数据,然后通过 putImageData() 将图像数据放回画布

            this.imgCarArr.push(newCarCanvas.toDataURL());
          }

          // 清空新创建的canvas
          this.canvasLength = 0;
          this.showFaceInfo(0);
          this.showCarInfo(0);
        });
      };//img.onload 图片预加载完毕
    },
    </script>
    //mul.js & div.js
    // 浮点数精确计算乘
	module.exports = {
  	accMul: function(num1, num2) {
    var m = 0,
      s1 = num1.toString(),
      s2 = num2.toString();
    try {
      m += s1.split(".")[1].length;
    } catch (e) {}
    try {
      m += s2.split(".")[1].length;
    } catch (e) {}
    return (
      (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
      Math.pow(10, m)
    );
  }
  // 浮点数精确计算除
	module.exports = {
  	accDiv: function(num1, num2) {
    var t1, t2, r1, r2;
    try {
      t1 = num1.toString().split(".")[1].length;
    } catch (e) {
      t1 = 0;
    }
    try {
      t2 = num2.toString().split(".")[1].length;
    } catch (e) {
      t2 = 0;
    }
    r1 = Number(num1.toString().replace(".", ""));
    r2 = Number(num2.toString().replace(".", ""));
    return (r1 / r2) * Math.pow(10, t2 - t1);
  }
};
};

参考案例

Logo

前往低代码交流专区

更多推荐