演示地址
https://king2088.github.io/simple-image-label/
演示图
开发背景:
最近公司要做一个 AI 在线标注零碎,须要对图片进行标注,本人在 github 找了一些,然而大部分都是很久没有更新或者是不太适宜自定义等。当然我也看过 CVAT 本人的标注,它是采纳 canvas 开发的,不过对于咱们目前的我的项目来说,并不需要 canvas,也不须要较为简单的框选,只须要反对矩形框标注即可,于是本人开发了一个 web 版的。
简介:
simple-image-label 是一个基于 Web 的用户敌对型组件,简化了指标检测和深度学习工作中的图片标注过程。该组件采纳 HTML 和 JavaScript 开发,使用户可能轻松地对图片进行标注并生成标注信息,实用于各种利用场景。
次要特点:
- 用户敌对的界面:simple-image-label 提供直观易用的界面,使图片标注变得轻而易举。其简洁性确保用户无论程度如何,都能轻松地应用该工具。
- 高效标注:借助 simple-image-label,用户能够高效地对图片进行标注,增加标签并生成指标检测工作所需的坐标。它简化了标注流程,节俭了贵重的工夫和精力。
- 多样兼容性:该组件反对多种标注格局,包含风行的 YOLO 和 VOC 等格局。这种兼容性确保了与现有工作流程和深度学习框架的无缝集成。
- 网页便捷性:simple-image-label 作为一个基于 Web 的工具,无需简单的装置或设置。用户能够间接从 Web 浏览器中拜访和应用该组件,使其便捷且随时可用。
开发过程:
simple-image-label 的开发过程能够分为以下几个步骤:
-
创立 HTML 构造:首先,咱们须要创立一个蕴含图像和标注区域的 HTML 构造。能够应用以下代码作为终点:
<div class="__simple-image-label__"> <div class="s-image-content"> <img id="label-bg_img" src=""> <div id="labelsContainer" class="label-content"></div> </div> </div>
- 初始化组件:在 JavaScript 中,咱们须要初始化 simple-image-label 组件,为其绑定相干的事件和性能。以下是一个根本的初始化代码示例:
首先先再创 label 时,指定其矩形的左上顶点 x,y 轴坐标,以及矩形的宽高,而后将其增加到 labelsContainer 中
this.labelItem = {
uuid: '', // 随机生成
x: 0, // 百分比 = 左上角 x 轴坐标 / 以后图片宽度
y: 0, // 百分比 = 左上角 y 轴坐标 / 以后图片高度
width: 0, // 矩形宽度百分比 = 矩形宽度 / 以后图片宽度
height: 0, // 矩形高度百分比 = 矩形高度 / 以后图片高度
name: '', // 矩形名称
color: '' // 矩形色彩
};
this.labelsContainer.appendChild(this.labelItem);
须要留神的是,初始化之前须要期待图片 load 实现后再执行,避免图片还未 load 实现,就曾经看到所有标签,另外则是为了获取到图片的宽高,不便
// 初始化
this.$w = this.labelsContainer.clientWidth;
this.$h = this.labelsContainer.clientHeight;
this.startPoint = {x: 0, y: 0};
this.endPoint = {x: 0, y: 0};
this.labelItem = {uuid: '', x: 0, y: 0, width: 0, height: 0, name:'', color: ''};
this.labelsContainer.onmousedown = (e) => this.mousedown(e);
// 鼠标挪动事件
this.labelsContainer.onmousemove = (e) => this.mousemove(e);
// 鼠标抬起事件
this.labelsContainer.onmouseup = (e) => this.mouseup(e);
// 右键点击
this.labelsContainer.oncontextmenu = (e) => {e.preventDefault();
if (this.contextmenu && typeof this.contextmenu === 'function') {this.contextmenu(e)
}
};
if (this.readOnly) {window.removeEventListener('resize', this.resize.bind(this), false);
this.labelsContainer.style.cursor = 'default';
this.clearAllLabelActive();} else {this.labelsContainer.style.cursor = 'crosshair';}
// 监听浏览器缩放, 扭转 label 的宽高
window.addEventListener('resize', this.resize.bind(this), false);
this.resizeDotDisplayStatus();
当鼠标左键按下时,咱们记录下鼠标的地位,并记录鼠标左键按下时的坐标,用于后续计算。
// 计算坐标
const clientLeft = e.clientX - this.getLabelsContainerRelativePoints().x;
const clientTop = e.clientY - this.getLabelsContainerRelativePoints().y;
let x = clientLeft / this.$w; // 转换为百分比
let y = clientTop / this.$h;
this.startPoint = {
x,
y
};
当鼠标左键抬起时,咱们计算出鼠标左键抬起时的坐标,并计算出鼠标左键按下和抬起时的坐标差,
const clientLeft = e.clientX - this.getLabelsContainerRelativePoints().x;
const clientTop = e.clientY - this.getLabelsContainerRelativePoints().y;
let x = clientLeft / this.$w;
let y = clientTop / this.$h;
this.endPoint = {
x,
y
};
这样就能够利用 startPoint 与 endPoint 两点只差算出标签的宽高。
labelItem.width = endPoint.x - startPoint.x;
labelItem.height = endPoint.y - startPoint.y;
YOLO 坐标计算:
YOLO 坐标计算形式,[center_x, center_y, width, height]
,即 center_x
, center_y
为中心点坐标,width
,height
为宽高(留神:全副按百分比进行示意,如 center_x 的地位为 30%,则示意为 center_x 为 0.3,其余内容雷同)。
convertToYoloCoordinate(label) {const coordinate = this.getCoordinate(label);
const {height, width} = this.imageInfo;
const labelCenterX = (coordinate.x + coordinate.x1) / 2;
const labelCenterY = (coordinate.y + coordinate.y1) / 2;
return [labelCenterX / width, labelCenterY / height, label.width, label.height];
}
VOC 坐标计算:
VOC 坐标计算形式,[xmin, ymin, xmax, ymax]
,即 xmin
, ymin
为左上角坐标,xmax
, ymax
为右下角坐标。
对于 VOC 标注,能够通过 simple-image-label 组件的 endLabeling 办法来计算标注框的坐标。以下是一个示例代码:
getCoordinate(label) {const { height, width} = this.imageInfo;
return {
x: label.x * width,
y: label.y * height,
x1: (label.x + label.width) * width,
y1: (label.y + label.height) * height
};
}
- 在 Vue 和 React 中应用 simple-image-label:
如果你正在应用 Vue 或 React,能够将 simple-image-label 组件包装成可复用的组件,以便在应用程序中应用。以下是一个 Vue 组件示例: -
装置 simple-image-label:
npm install simple-image-label -S
-
在 Vue 组件中引入 simple-image-label 并应用:
<template> <div id="YourElementId"></div> </template> <script setup> import SimpleImageLabel from 'simple-image-label' import {ref, onMounted} from 'vue'; const simpleImageLabel = ref(null); onMounted(() => { simpleImageLabel.value = new SimpleImageLabel({ el: 'YourElementId' imageUrl: props.imageUrl, labels: props.labels, contextmenu: (e) => {emit('contextmenu', e) }, labelClick: (label) => {emit('labelClick', label) }, error: (e) => {emit('error', e) } }); }) </script>
-
在 react 组件中应用 simple-image-label:
import SimpleImageLabel from 'simple-image-label'; import img from './x.png' import {useEffect} from 'react'; const ImageLabelComponent = () => { let simpleImageLabel = null useEffect(() => {initSimpleDom() }, []) function initSimpleDom() { simpleImageLabel = new SimpleImageLabel({ el: 'YourElementId', imageUrl: img, labels: [], contextmenu: (e) => {console.log(e); }, labelClick: (label) => {console.log(label); }, error: (e) => {console.log(e); } }) } function getAllLabels() {const labels = simpleImageLabel.getLabels() console.log('labels', labels); } return ( <div> <div id="YourElementId"></div> <button onClick={getAllLabels}>Get all labels</button> </div> ); } export default ImageLabelComponent;
我的项目地址:https://github.com/king2088/simple-image-label
如果你喜爱本我的项目,记得在 github 上给我一个 Star