前言
前端有一个需求,对上传的图片进行检测识别,通过返回的接口坐标数据,对图片的某些区域进行框选并标注。如图:
开始
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-uploadaction="#"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}"><imgclass="el-upload-list__item-thumbnail":src="file.url" alt=""><span class="el-upload-list__item-actions"><spanv-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 { // 图片真实尺寸大于框宽度,算出缩放比例。模版里:正常偏移数据/缩放比例ratiothis.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 { // 图片真实尺寸大于框宽度,算出缩放比例。模版里:正常偏移数据/缩放比例ratiothis.ratio = image.width / 1000;}console.log(image.width);},