3
头图

演示地址

https://king2088.github.io/simple-image-label/

演示图

预览图

开发背景

最近公司要做一个AI在线标注系统,需要对图片进行标注,自己在github找了一些,但是大部分都是很久没有更新或者是不太适合自定义等。当然我也看过CVAT自己的标注,它是采用canvas开发的,不过对于我们目前的项目来说,并不需要canvas,也不需要较为复杂的框选,只需要支持矩形框标注即可,于是自己开发了一个web版的。

简介

simple-image-label是一个基于Web的用户友好型组件,简化了目标检测和深度学习任务中的图片标注过程。该组件采用HTML和JavaScript开发,使用户能够轻松地对图片进行标注并生成标注信息,适用于各种应用场景。

主要特点

  1. 用户友好的界面:simple-image-label提供直观易用的界面,使图片标注变得轻而易举。其简洁性确保用户无论水平如何,都能轻松地使用该工具。
  2. 高效标注:借助simple-image-label,用户可以高效地对图片进行标注,添加标签并生成目标检测任务所需的坐标。它简化了标注流程,节省了宝贵的时间和精力。
  3. 多样兼容性:该组件支持多种标注格式,包括流行的YOLO和VOC等格式。这种兼容性确保了与现有工作流程和深度学习框架的无缝集成。
  4. 网页便捷性:simple-image-label作为一个基于Web的工具,无需复杂的安装或设置。用户可以直接从Web浏览器中访问和使用该组件,使其便捷且随时可用。

开发过程

simple-image-label的开发过程可以分为以下几个步骤:

  1. 创建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>
  2. 初始化组件:在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为中心点坐标,widthheight为宽高(注意:全部按百分比进行表示,如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
    };
}
  1. 在Vue和React中使用simple-image-label:
    如果你正在使用Vue或React,可以将simple-image-label组件包装成可复用的组件,以便在应用程序中使用。以下是一个Vue组件示例:
  2. 安装simple-image-label:

    npm install simple-image-label -S
  3. 在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>
  4. 在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


深夜徘徊
297 声望267 粉丝

博主是一个苦逼的80后IT奴,苦逼屌丝男。