前言

前端有一个需求,对上传的图片进行检测识别,通过返回的接口坐标数据,对图片的某些区域进行框选并标注。如图:

在这里插入图片描述

开始

1、上传功能使用elementui的upload插件;
2、在图片上进行标注功能是元素定位在图片上层,通过坐标进行框选;

代码:

<template>
  <div class="container">
    <!-- form表单 -->
    <div>
      <el-form ref="form" :model="form" label-width="80px" style="width:500px;">
        <el-form-item label="模型目录">
          <el-input v-model="form.trainId" disabled></el-input>
        </el-form-item>
        <el-form-item label="设备名称">
          <el-input v-model="form.deviceName" disabled></el-input>
        </el-form-item>
        <el-form-item label="图片">
          <el-upload
            action="#"
            list-type="picture-card"
            :auto-upload="false"
            :on-change="handleImgChange"
            :file-list="fileList"
            :limit="1"
            :on-exceed="handleExceed">
              <i slot="default" class="el-icon-plus"></i>
              <div slot="file" slot-scope="{file}">
                <img
                  class="el-upload-list__item-thumbnail"
                  :src="file.url" alt=""
                >
                <span class="el-upload-list__item-actions">
                  <span
                    v-if="!disabled"
                    class="el-upload-list__item-delete"
                    @click="handleRemove(file)"
                  >
                    <i class="el-icon-delete"></i>
                  </span>
                </span>
              </div>
          </el-upload>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="onSubmit" :loading = "loading">立即检测</el-button>
          <el-button type="default" @click="reset">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 结果显示区域 -->
    <div class="title">图像检测结果</div>
    <div class="imgContainer">
      <img :src="imgUrl" alt="" v-if="imgUrl" @load="imageLoad">
      <div class="mask" v-if="imgUrl">
        <div class="ltBox"  
              v-for="(item,idx) in imgData" 
              :key="idx" 
              :style="{left: Number(item.bbox[0])/ratio +'px', top: item.bbox[1]/ratio+'px', width: item.bbox[2]/ratio+'px', height: item.bbox[3]/ratio+'px' }">
          <span class="boxTitle">{{item.category_name}}</span>
        </div>
      </div>
    </div>
    
  </div>
</template>
 
<script>
  export default {
    name: "HomeView",
    data() {
      return {
        form: {
          trainId: 'model_39164_cpu', deviceName: 'cpu', pic01: {},
        },
        imgUrl: '', // 图片url,用于检测完显示用
		fileList: [], // 上传的图片列表
        disabled: false,
        imgData: [], // 图片标注数据
		ratio: 1, // 图片显示到框里时的缩放比值
		loading: false,
      };
    },
    mounted() {
        
    },
    methods: {
       handleImgChange(res, file) {
		this.fileList = file;
        // this.imgUrl = file[0].url;
        this.form.pic01 = file[0].raw;
		console.log(file);		
      },
       handleRemove(file) {
		this.fileList = [];
        // this.imgUrl = '';
        this.form.pic01 = {};
		this.imgUrl = '';
		this.imgData = [];
      },
      onSubmit(){		
		let _self = this;
		if (JSON.stringify(this.form.pic01) == "{}") {
			this.$message.error("请上传图片");
			return;
		}
		this.loading = true;
		let formData = new FormData();
      	formData.append("trainId", this.form.trainId);
      	formData.append("device_name", this.form.deviceName);
      	formData.append("pic01", this.form.pic01);
        this.$axios({
          method: 'POST',
          url: '/api/prediction',
          data: formData,
          headers: {
            'Content-Type': 'multipart/form-data',
            'charset': 'UTF-8'
          }
        }).then(function(res) {
			console.log(res);
			if (res.data.code === 200) {
				_self.loading = false;
				// 检测框显示图片
				_self.imgUrl = _self.fileList[0].url;
				// 获取标注数据
				let name = _self.fileList[0].name; // 键名为上传图片名称
				_self.imgData = res.data[name].result;
			} else {
				_self.$message({
					message: res.data.errorMsg,
					type: 'error',
					duration: 5 * 1000
				})
			}
        });
      },
	  imageLoad() {
		console.log('图片加载完毕');
		// 计算检测框与实际图片大小的比值
		// this.imgUrl = file[0].url;
		let image = new Image();
		image.src = this.imgUrl;
		let imgWidth = image.width;
		if (imgWidth <= 1000) { // 框大于图片真实尺寸,不用缩放,按照正常偏移数据
			this.ratio = 1;
		} else { // 图片真实尺寸大于框宽度,算出缩放比例。模版里:正常偏移数据/缩放比例ratio
			this.ratio = image.width / 1000;
		}
		console.log(image.width);
	  },
	  reset() {
		this.fileList = [];
        this.imgUrl = '';
        this.form.pic01 = {};
		this.imgData = [];
	  },
	  handleExceed(files, fileList) {
        this.$message.warning("只能选择一张图片");
      },
    }
  };
</script>
 
<style scope>
  .container{
    width: 1000px;
    box-sizing: border-box;
    margin: 80px auto;
  }
  .title{
    font-size: 16px;
    margin: 60px 0 20px;
    font-weight: bold;
  }
  .imgContainer{
    position: relative;
    /* box-sizing: border-box; */
    border: 1px dashed #000;
    width: 100%;
    min-height: 300px;
  }
  .imgContainer img{
	width: 100%;
  }
  .mask{
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;    
  }
  .mask .ltBox{
    position: absolute;
    border: 1px solid red;

    /* width: 100px;
    height:100px;      
    left: 100px;
    top: 100px;      */
  }
  .mask .ltBox span{
    display: inline-block;
    font-size: 14px;
    padding-left: 2px;
    color: #fff;
    width: 16px;
    height: 20px;
    background: rgba(0,0,0,.4);
  }
</style>

返回数据格式为:

{"03.png":{"result":[{"angle":0,"bbox":[909.2269287109375,357.05908203125,86.85955810546875,130.1535034179688],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9995452761650085,"type":"resultRect"},{"angle":0,"bbox":[909.5748901367188,190.1406097412109,86.5870361328125,132.7530364990234],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9995043277740479,"type":"resultRect"},{"angle":0,"bbox":[427.7415161132813,599.2159423828125,108.297607421875,105.1603393554688],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9994520545005798,"type":"resultRect"},{"angle":0,"bbox":[910.0458984375,46.47737503051758,86.25689697265625,130.2275543212891],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9993937015533447,"type":"resultRect"},{"angle":0,"bbox":[427.6333923339844,863.2357177734375,108.3089904785156,99.88446044921875],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9991784691810608,"type":"resultRect"},{"angle":0,"bbox":[790.2532958984375,191.2887725830078,93.9698486328125,129.5539703369141],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9990857839584351,"type":"resultRect"},{"angle":0,"bbox":[544.385498046875,602.537841796875,98.578125,98.70477294921875],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9990641474723816,"type":"resultRect"},{"angle":0,"bbox":[428.9845275878906,739.2496337890625,106.2877502441406,95.54400634765625],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9990469813346863,"type":"resultRect"},{"angle":0,"bbox":[546.028076171875,739.0665893554688,97.27117919921875,96.72930908203125],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9989651441574097,"type":"resultRect"},{"angle":0,"bbox":[549.2886352539063,866.919677734375,92.69256591796875,99.43603515625],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9987188577651978,"type":"resultRect"},{"angle":0,"bbox":[791.1380004882813,48.05636215209961,95.0599365234375,126.0226898193359],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9986415505409241,"type":"resultRect"},{"angle":0,"bbox":[791.9155883789063,358.5489501953125,91.2598876953125,125.5012512207031],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9971223473548889,"type":"resultRect"},{"angle":0,"bbox":[425.3143310546875,47.26962661743164,109.3796997070313,127.9037933349609],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9970883727073669,"type":"resultRect"},{"angle":0,"bbox":[911.8796997070313,736.1849365234375,83.3861083984375,104.4035034179688],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9966270923614502,"type":"resultRect"},{"angle":0,"bbox":[330.7501220703125,600.6314086914063,74.79995727539063,102.3368530273438],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9962628483772278,"type":"resultRect"},{"angle":0,"bbox":[911.684814453125,598.4668579101563,83.82977294921875,107.4171752929688],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9958639144897461,"type":"resultRect"},{"angle":0,"bbox":[332.485107421875,738.0242309570313,73.73397827148438,98.7982177734375],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9956913590431213,"type":"resultRect"},{"angle":0,"bbox":[678.7830200195313,192.1672668457031,88.1395263671875,131.537109375],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.995394229888916,"type":"resultRect"},{"angle":0,"bbox":[680.2387084960938,358.936767578125,86.17327880859375,126.4320983886719],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9953811168670654,"type":"resultRect"},{"angle":0,"bbox":[427.2458190917969,356.8890380859375,105.9344787597656,126.5965881347656],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9953643083572388,"type":"resultRect"},{"angle":0,"bbox":[426.9499206542969,193.8685760498047,107.8788757324219,123.8152008056641],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9953345656394958,"type":"resultRect"},{"angle":0,"bbox":[911.595703125,862.9237060546875,84.64520263671875,108.9039916992188],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9952036142349243,"type":"resultRect"},{"angle":0,"bbox":[792.5260009765625,859.0792236328125,93.56689453125,111.516845703125],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9951752424240112,"type":"resultRect"},{"angle":0,"bbox":[542.8145751953125,44.25643539428711,97.0166015625,130.5228576660156],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9950712919235229,"type":"resultRect"},{"angle":0,"bbox":[328.7076110839844,45.84556579589844,79.90731811523438,130.7186737060547],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9948970675468445,"type":"resultRect"},{"angle":0,"bbox":[328.0628662109375,865.086181640625,80.9405517578125,99.62249755859375],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9948451519012451,"type":"resultRect"},{"angle":0,"bbox":[329.5053405761719,356.4360046386719,77.6827392578125,129.4084167480469],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9946455955505371,"type":"resultRect"},{"angle":0,"bbox":[329.6501770019531,192.0917816162109,79.248291015625,129.7602996826172],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9944493770599365,"type":"resultRect"},{"angle":0,"bbox":[546.0228271484375,192.6796722412109,94.2720947265625,126.7196197509766],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9937253594398499,"type":"resultRect"},{"angle":0,"bbox":[679.3350830078125,45.87873077392578,86.26666259765625,129.9478454589844],"category_id":"0","category_name":"d","pointsArr":null,"res_num":36,"scores":0.9934400916099548,"type":"resultRect"},{"angle":0,"bbox":[794.0667114257813,736.28662109375,92.5123291015625,106.004150390625],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9919258952140808,"type":"resultRect"},{"angle":0,"bbox":[544.1696166992188,356.3648986816406,96.20233154296875,126.95068359375],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9917498230934143,"type":"resultRect"},{"angle":0,"bbox":[678.4888305664063,863.577392578125,92.967529296875,106.4488525390625],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9908513426780701,"type":"resultRect"},{"angle":0,"bbox":[681.7239379882813,736.389404296875,87.13897705078125,107.5022583007813],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9900141954421997,"type":"resultRect"},{"angle":0,"bbox":[679.4708251953125,599.4293212890625,90.96051025390625,106.7920532226563],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.9883459210395813,"type":"resultRect"},{"angle":0,"bbox":[793.232177734375,599.1714477539063,93.35357666015625,106.982421875],"category_id":"1","category_name":"l","pointsArr":null,"res_num":36,"scores":0.986026406288147,"type":"resultRect"}]},"code":200,"errorCode":0,"errorMsg":"ok"}

需要注意的是:

1、上传图片时候,后台需要formData格式。

打印upload插件的file参数,

 	 handleImgChange(res, file) {
		this.fileList = file;
        this.form.pic01 = file[0].raw;
		console.log(file);		
      },

结果为:在这里插入图片描述

这里需要把’raw’拿出来,放到formData里面,传给后台。有其他参数,一并传送。例如:

	let formData = new FormData();
    formData.append("trainId", this.form.trainId);
    formData.append("device_name", this.form.deviceName);
    formData.append("pic01", file.raw);

2、监听图片加载完成事件,获取图片真实宽高。

由于我们需要在一个固定宽度的区域中显示图片(图片宽度充满区域,也就是说宽度为区域的百分之百)并框选标注,涉及到选框的偏移位置信息,所以必须拿到图片的真实宽度,算出比值,再进行偏移量的计算。

模版字符串里面:

<img :src="imgUrl" alt="" @load="imageLoad">

js里面:

	imageLoad() {
		console.log('图片加载完毕');
		let image = new Image();
		image.src = this.imgUrl;
		let imgWidth = image.width;
		if (imgWidth <= 1000) { // 框大于图片真实尺寸,不用缩放,按照正常偏移数据
			this.ratio = 1;
		} else { // 图片真实尺寸大于框宽度,算出缩放比例。模版里:正常偏移数据/缩放比例ratio
			this.ratio = image.width / 1000;
		}
		console.log(image.width);
	  },
GitHub 加速计划 / vu / vue
108
18
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
9e887079 [skip ci] 1 年前
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 1 年前
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐