vue实现图片框选标注

前言

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

在这里插入图片描述

开始

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);
	  },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/448267.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小程序对于人力资源行业的创新与变革

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业推广和服务的新利器。对于人力资源行业来说&#xff0c;开发一款定制化的小程序不仅可以提升服务效率&#xff0c;还可以增强品牌形象和用户粘性。那么&#xff0c;如何定制开发人力资源类的小程序呢&#xff1f;下面…

群晖Synology Office本地文件如何分享给同事远程协作编辑【内网穿透】

文章目录 本教程解决的问题是&#xff1a;1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 本教程解决的问题是&#xff1a; 1.Word&#xff0c;PPT&#xff0c;Excel等重要文件存在本地环境&#xff0c;如何在编…

开源是什么?——跟老吕学Python编程

开源是什么&#xff1f;——跟老吕学Python编程 开源是什么&#xff1f;开放源代码软件是什么&#xff1f;开源软件许可证是什么&#xff1f;开放源代码软件是什么&#xff1f;开放源代码的软件代表有什么&#xff1f;开放源代码软件与自由软件的概念 开源的定义是什么&#xf…

郭炜老师mooc第十一章数据分析和展示(numpy,pandas, matplotlib)

多维数组库numpy numpy创建数组的常用函数 # numpy数组import numpy as np #以后numpy简写为np print(np.array([1,2,3])) #>>[1 2 3] print(np.arange(1,9,2)) #>>[1 3 5 7] 不包括9 print(np.linspace(1,10,4)) #>>[ 1. 4. 7. 10.] # linespace(x,y,n)&…

数据通信练习题

1.0osi七层模型 应用层 data 表示层 会话层 传输层 数据段 防火墙&#xff0c;端口&#xff08;TCP UDP&#xff09; 网络层 数据包 路由器 数据链路层 数据帧 交换机 物理层 比特流 网卡 2.IP地址分类 私有地址 A类 0--127 10.0.0.0…

双指针算法练习

27. 移除元素 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑…

上位机图像处理和嵌入式模块部署(qmacvisual旋转和镜像)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 旋转和镜像是图像处理中经常遇到的一个情况。很多时候&#xff0c;摄像头面对物体进行拍摄&#xff0c;未必是正对着进行拍摄的&#xff0c;这个时…

基于element-plus的Dialog选择控件

翻看之前工程师写的vue2的代码&#xff0c;很多都是复制、粘贴&#xff0c;也真是搞不懂&#xff0c;明明可以写一个控件&#xff0c;不就可以重复使用。很多前端总喜欢element搞一下&#xff0c;ant-design也搞一下&#xff0c;有啥意义&#xff0c;控件也不是自己写的&#x…

45、C++/基础练习20240311

一、提示并输入一个字符串&#xff0c;统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 要求 使用C风格字符串完成。 代码&#xff1a; #include <iostream>using namespace std;int main() {string buf;//定义字符串类型变量bufcout << &…

在Jetson Xavier NX 开发板上使用VScode执行ROS程序详细过程

1.创建 ROS 工作空间ws 在home下打开终端输入下面指令 mkdir -p xxx_ws/src(必须得有 src) cd 自己命名_ws catkin_make2.启动 vscode cd 自己命名_ws code .3.vscode 中编译 ros 快捷键 ctrl shift B 调用编译&#xff0c;在上方弹窗位置选择:catkin_make:build 可以点击…

Web题记

[CISCN 2019华北Day2]Web1 告诉我们想要的东西在flag表和flag字段&#xff0c;那应该是sql注入&#xff0c;先试试 试了一些发现会被检查到&#xff0c;随便传数字 除了1和2有返回结果&#xff0c;其余的都报错&#xff0c;应该是数字型注入&#xff0c;抓包看看过滤了哪些 这个…

C语言实现贪吃蛇

前言&#xff1a;今天给大家详细介绍一下小游戏贪吃蛇的代码。 目录 一 .贪吃蛇实现的功能 二.贪吃蛇游戏设计与分析 1.贪吃蛇以及贪吃蛇所需要维护的数据 &#xff08;1&#xff09;贪吃蛇蛇体 &#xff08;2&#xff09;数据维护 2.地图设计 &#xff08;1&#x…

详解7道经典指针运算笔试题!

目录 ​编辑 1. 题目一 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 2. 题目二 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 3. 题目三 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 4. 题目四 &#xff08;…

鸿蒙开发为什么这么火,现在入行鸿蒙是否来的及?

鸿蒙开发是当前备受关注的技术领域之一&#xff0c;对于想要入门学习鸿蒙开发的初学者来说&#xff0c;需要掌握一定的基础知识和技能。鸿蒙开发又是否能为程序员们带来一片光明的未来呢&#xff1f;让我们一同探讨这些问题。 对于初学者来说&#xff0c;鸿蒙开发是否易于上手呢…

构建空间场景轻应用,Mapmost Alpha来啦【文末赠书(10本)--第一期】

文章目录&#xff1a; 一、Mapmost Alpha 介绍二、Mapmost Alpha应对数字孪生业务痛点解决之道2.1 Mapmost Alpha 提供海量城市底板2.2 Mapmost Alpha 提供便捷的配置管理工具2.3 Mapmost Alpha 提供一键式部署发布和分享 三、沉浸式体验Mapmost Alpha3.1 创建应用3.2 新手指导…

树莓派安装Nginx服务搭建web网站结合内网穿透实现公网访问本地站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

Everything:文件查找工具,一搜即得

名人说&#xff1a;东边日出西边雨&#xff0c;道是无晴却有晴。——刘禹锡 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍①Everything②核心功能③原理 二、下载安装①下载②安装 三、使用方法①文…

如何利用音频转换器将多个MP3音频转换为OGG格式

现在&#xff0c;我们接触到的很多音频文件一般都是MP3格式的。但是我们偶尔也需要用到ogg格式的音频文件&#xff0c;所以就需要我们将MP3格式音频文件转换为ogg格式了&#xff0c;那么&#xff0c;如果想要将MP3格式文件转换为OGG格式该如何操作呢&#xff1f;相信很多朋友心…

Linux内核之报错-Werror,-Wunused-variable等通用解决方案(二十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

企业数据备份体系化方法论的七大原则之二:区分环境数据与业务数据

在之前讨论的分布式存储系统七大原则的第一原则中&#xff0c;我们了解了容灾切换和数据备份的差异。现在&#xff0c;我们继续探索第二原则&#xff1a;区分环境数据与业务数据。这一原则强调了两种类型数据在变化频率、价值以及数据一致性获取难度方面的根本区别&#xff0c;…