1. 为什么数据标注是绕不开的一步,而Label Studio恰好能帮你省事
做过目标检测、图像分类这类深度学习项目的朋友应该都有体会:模型结构再花哨、训练代码再熟练,如果训练数据没有准备好,一切都白搭。我最早开始做目标检测的时候,自己闷头用Python脚本画框、存坐标,写了一个简陋的标注工具,结果用了没几天就发现根本撑不住——图片一多,标注文件对不上、标签名字写错、坐标算错,光是清洗数据就耗掉了大半个周末。
后来换用数据标注工具,才真正明白这类工具存在的意义。数据标注工具要解决的核心问题其实很朴素:把“人在图片上框出目标”这件事变得足够简单,并且把结果输出成训练框架能直接消费的格式。而在众多开源工具里,Label Studio是我用了很久、也最愿意推荐给别人的一个。
先说说它的定位。Label Studio是一个开源的数据标注平台,支持图像、文本、音频、视频等多种数据类型。对做视觉方向的人来说,常用的功能无非就是图像分类、矩形框目标检测、多边形分割、关键点标注这几种,它全都覆盖了。更重要的是,它内置了多种导出格式,包括JSON、CSV、COCO、VOC,甚至可以直接导出YOLO格式,这意味着你标注完的数据,能直接喂给YOLO系列模型训练,不用再写一堆蹩脚的转换脚本。
这篇文章适合谁?如果你是刚入门深度学习、需要自己做数据集的在校学生,或者是在小团队里需要快速搭建一套标注流程的工程师,又或者只是单纯想找一个比Windows画图好用一万倍的标注工具,那这篇内容基本就是为你准备的。下面我会用一套完整的流程,从安装、创建项目、实际操作到导出数据,帮你把Label Studio的基础使用方法一次讲透。
数据标注这件事,技术上不复杂,但很多人在工具选型和使用细节上会走弯路。我踩过的坑,希望你看完之后能直接绕开。
2. 本地跑起来:安装方式、启动命令与界面布局
2.1 安装前的环境准备
Label Studio对运行环境的要求不算高,它本质上是一个Python服务,通过浏览器访问页面操作。你可以把它理解成一个跑在本地的小型Web应用——类似你在本地启动过的Jupyter Notebook那样,只不过它专门用来做数据标注。
环境上你只需要满足这几点:
- 操作系统:Windows、macOS、Linux都可以,我分别在Windows 10和Ubuntu 20.04上跑过,都没问题
- Python版本:建议3.8以上,官方文档说3.8-3.11都能用
- 浏览器:Chrome或者Edge最好,Firefox也能用,但某些交互在Chrome下更顺手
- 内存:标注工具本身不挑内存,但如果你要一次性导入几百张高清大图并做缩略图预览,8GB或以上会更舒服
需要注意,如果你电脑上装了多个Python版本,建议用虚拟环境来安装Label Studio,避免包依赖冲突。这个工具依赖了Django、DRF等一堆Python库,和某些老旧项目的依赖撞车的概率不低。
2.2 安装并启动Label Studio
安装方式有两种:pip安装和Docker运行。Docker适合多人协作的服务器部署场景,省去了环境折腾的麻烦。但对于绝大多数个人使用场景,我建议直接用pip,简单直接。
先用虚环境隔离一下依赖:
# 创建并激活虚拟环境(Windows和Linux/macOS略有区别) python -m venv label-studio-env # Windows下激活 label-studio-env\Scripts\activate # Linux/macOS下激活 source label-studio-env/bin/activate # 安装label-studio pip install label-studio安装完成后,在命令行输入:
label-studio start服务启动后,终端会输出一段日志,并提示你在浏览器中访问http://localhost:8080。我第一次启动的时候还担心端口被占用,其实它默认用8080,如果被占用,可以用label-studio start --port 8081指定其他端口。
首次访问时,页面会让你创建一个管理员账号,也就是设置用户名、邮箱和密码。这个账号就是整个系统里权限最高的账号,后续的成员管理、标注任务分配都要用它来做。有人会问能不能跳过这一步,答案是不能——Label Studio的数据都存储在服务端,必须有一个账号体系来管理项目和权限。
创建完账号进入主界面,你会看到一个项目列表页,目前是空的。先别急着新建项目,我建议你先花两分钟把界面上的几个入口认清楚。
2.3 界面布局和服务运行的底层逻辑
Label Studio的主界面很简洁,左侧有Projects(项目列表)、Import(导入数据)、Export(导出数据)这类入口,但这些入口大多是在进入具体项目之后才生效的。实际上,它的核心操作逻辑是:项目是一等公民,所有操作都围绕项目展开。
有一个很多人忽略的细节:Label Studio的数据存储方式。所有标注数据、项目配置都保存在本地的文件系统中。如果你用的是pip安装,默认的数据目录会在你的用户目录下,比如~/label-studio这个文件夹。里面包含media(上传的图片等媒体文件)、files(项目配置和导出文件的缓存)等子目录。这点很重要,尤其是当你标注了大量数据后想备份或迁移,直接复制这个目录就相当于备份了整个Label Studio。
我个人习惯是在启动之前就用环境变量指定数据目录,比如:
LABEL_STUDIO_BASE_DATA_DIR=/your/path/label-studio-data label-studio start这样所有的数据都会存放到你指定的目录下,不会因为系统盘空间不足或者用户目录被清理而丢数据。这个习惯帮我避免过一次大麻烦,后面会详细说。
3. 创建图片标注项目:模板选择、标签设置与数据导入
3.1 新建项目的两种路径
在主界面点击Create Project按钮,会看到两种创建方式:从模板创建(From Template)和从空白项目创建(Blank Project)。
我强烈建议新手先使用模板,因为模板已经帮你配置好了标注界面和导出结构,你只需要改标签名就行。等用熟了,再自己用Labeling Config编辑器去写标签配置。
点击From Template之后,你会看到一个模板列表。这个列表涵盖了图像、文本、音频、视频各类任务。对做目标检测的朋友来说,最常用的是这几个:
- Object Detection with Bounding Boxes:矩形框目标检测,最常用
- Image Classification:图像分类,给整张图打标签
- Semantic Segmentation with Polygon:多边形分割,像素级标注
- KeyPoint Labeling:关键点标注,适合姿态估计
选择Object Detection with Bounding Boxes模板,进入项目配置页面。
3.2 配置标签名与标签颜色
项目配置页面有两个地方要填:Project Name和Labeling Setup。Project Name随意,最好起一个能一眼看出用途的名字,比如“cat-dog-detection-dataset”。
Labeling Setup是这个页面里最核心的部分。模板会自动生成一段XML格式的标签配置,像这样:
<View> <Image name="image" value="$image"/> <RectangleLabels name="label" toName="image"> <Label value="Cat" background="#FF0000"/> <Label value="Dog" background="#00FF00"/> </RectangleLabels> </View>你可以直接在这段配置中修改<Label>标签,把value改成你自己的类别名。比如我做过一个口罩佩戴检测的数据集,就把标签改成了WithMask和WithoutMask。颜色也可以自定义,建议不同类别用对比度高的颜色,标注的时候一眼就能区分。
有一个细节值得注意:标签名最好用英文和下划线,尽量避免使用中文或带空格的类名。虽然Label Studio界面支持中文标签名,但导出的YOLO格式中,类别名称最终会映射到data.yaml的names列表,中文类名在后处理时会带来不必要的麻烦。我见过有人用了中文类名,后面解析标签映射文件时编码出错,回头又要重新导一遍,浪费时间。
3.3 导入图片数据
项目创建完成后,会自动进入项目详情页。点击右上角Import按钮,就能导入图片数据了。
Label Studio支持几种导入方式:
- 从本地上传文件或文件夹(拖拽即可)
- 通过URL批量导入
- 从云存储(如AWS S3、Google Cloud Storage)导入
本地导入最简单,直接把图片文件拖进上传区域就行。一次拖入几百张图片也没问题,它会自动创建任务队列,每张图片就是一个待标注的Task。
这里有一个我刚开始没注意到的细节:Label Studio导入的其实不只是图片本身,它会把每个文件封装成一个“Task”(任务),并且给每个Task分配一个唯一的ID。后续你标注的结果、导出的JSON文件,都会关联到这个Task ID。理解这一点,有助于你排查“为什么我的标注结果对不上某张图片”这类问题。
另一个实际经验是:如果你要导入的图片是几千万像素的高清大图,建议先压缩到合理的分辨率再导入。原因有两个,一是Label Studio在浏览器中渲染大图时,缩放和拖拽会明显卡顿,标注效率极低;二是缩略图生成也会变慢。我通常会先用Python脚本把图片统一处理成1920像素以内的尺寸,既不影响标注精度,又让界面操作流畅很多。
4. 动手标一张图:工具箱、快捷键与完整标注流程
4.1 标注界面的功能分区
进入项目后,点击任意一条任务,就进入了标注界面。整个界面分为三个核心区域:
- 中间是图片画布,可以缩放、拖拽
- 左侧是工具条,包含矩形框、画笔、缩放、撤销等工具
- 右侧是标签面板和属性面板,用于选择当前要标注的类别
画布下方还有任务导航栏,可以切换到上一张、下一张图片。右上角有Submit按钮,代表完成了当前任务的标注并提交。
第一次进入的时候可能会觉得工具栏图标太多,其实常用的就那几个。做矩形框标注的话,核心是Rectangle工具,旁边的智能工具Smart是一个辅助工具,后面我会单独说。
4.2 标注一张图的完整流程
以目标检测为例,标准的标注流程是这样的:
- 在右侧标签面板选择一个类别,比如Cat
- 在画布上按住鼠标左键,从目标物体的左上角拖到右下角,松手后就能画出一个矩形框
- 如果框的位置不准确,可以选中这个框,拖动边框上的控制点微调
- 继续画下一个目标,或者按快捷键切换到下一张图片
- 所有目标都标完之后,点击Submit提交
听起来很简单,但实际操作中有一个特别影响效率的点:标签切换。如果你把鼠标移回右侧面板去点标签,再回来画框,一张图画几个目标就要来回移动好几次,非常累。
Label Studio的快捷键能解决这个问题。在键盘上按下对应数字键,就能直接切换标签。默认情况下,每个标签对应一个数字编号,按1、2、3就能切换不同类别。这样你只需要把鼠标一直放在画布上,标注速度能提升一倍不止。
还有几个快捷键很常用:
- 滚轮:缩放画布
- 按住空格加拖拽:平移画布
- Ctrl+Z:撤销上一步操作
- Ctrl+C / Ctrl+V:复制粘贴矩形框,适用于重复场景
这个技能尤其适合那种一张图里有几十个小目标的场景,比如标注一群鸟或者一堆螺丝,用快捷键配合复制粘贴,效率提升很明显。
4.3 属性面板与动态标签
在标签配置中,除了简单的RectangleLabels,Label Studio还支持给标签添加属性(Attributes),比如一个框除了类别之外,还能标记“是否模糊”“遮挡程度”等。
这在真实项目中非常有用。举个例子,我做过一个交通标志检测的数据集,只需要找到标志矩形框,但有些标志被树叶遮挡了一半。如果不标记遮挡属性,模型训练时会被这类样本误导。我给标签增加了Occluded这个属性字段,标注时勾选一下就行了。
属性配置也是在Labeling Setup里加代码,比如:
<RectangleLabels name="label" toName="image"> <Label value="StopSign" background="#FFA500"/> <Label value="SpeedLimit" background="#0000FF"/> </RectangleLabels> <Choices name="occluded" toName="image" showInLabel="true"> <Choice value="Occluded"/> <Choice value="NotOccluded"/> </Choices>把showInLabel设置为true之后,右侧面板上就会针对每个已标注的框显示一个下拉选项,非常直观。
4.4 智能标注工具Smart的使用边界
工具条上的Smart工具,很多人第一次点开会觉得好奇,使用后发现它是调用机器学习模型对矩形框附近的像素做进一步分割。它的原理是,在矩形框区域内用预训练分割模型提取目标轮廓,生成更精细的多边形掩膜。
但这里我要泼一盆冷水:如果你是做常规的矩形框目标检测,Smart工具其实帮不上太大的忙,因为矩形框检测要的是“外接矩形”,而不是“精准轮廓”。Smart更适合分割标注场景,比如你要给一张图里的人抠出精细轮廓,先拉个大框包住人,再让Smart去自动分割,会比手动一个点一个点描边快得多。
所以我的建议是:如果主要做目标检测,忽略Smart工具就好,别在这上面纠结。
4.5 Review机制与任务状态管理
每标完一张图点击Submit后,任务状态会变成Submitted。但在多人协作的场景下,你可能还希望有一个“审核”环节。Label Studio默认的工作流是Created → Labeled,你可以通过配置增加Review步骤,让经验丰富的人去检查新手标注的框,有问题就退回重标。
这个机制小团队可能用不上,人少的时候自己标完自己审核一遍就完事了。但如果团队里有几个人同时标注,Review流程能拦截不少低级错误,比如框画错位置、漏标目标这些。配置方式是在Settings → Review页签里开启Review Enabled,被分配Review权限的账号就能在标注界面看到Review模式。
5. 让数据变成YOLO能吃的格式:导出与格式转换
5.1 导出面板与原生格式
当一批图片都标注完成之后,回到项目详情页,点击右上角的Export按钮,会弹出一个导出面板。这里面列出了Label Studio支持的所有导出格式,包括JSON、CSV、COCO、VOC、YOLO等。
大部分刚接触Label Studio的人会在这里卡一下:明明数据标好了,为什么导出的文件里找不到一个能直接用来训练YOLO的txt文件?答案很简单,因为YOLO训练需要的数据格式不是单一文件,而是需要图片文件夹、标注文件夹和类别配置文件三者配套存在。
Label Studio的YOLO导出功能其实已经内置了。在导出面板选择YOLO格式,会下载一个压缩包,解压后里面包含images(图片)、labels(标签txt)和一个classes.txt或data.yaml文件。很多时候你会发现它导出的目录结构跟你预期的略有差异,这是因为Label Studio本身支持多种YOLO变体(darknet、ultralytics),不同版本的目录结构稍有不同。
5.2 导出的YOLO文件到底是什么结构
如果你选择的是darknet风格导出,解压后你会看到类似这样的结构:
yolo_export/ ├── obj_train_data/ │ ├── image001.jpg │ ├── image001.txt │ ├── image002.jpg │ ├── image002.txt │ └── ... ├── obj.names ├── train.txt └── ...其中obj.names文件内容就是每个类别的名称,按顺序排列,比如:
Cat Dog每一行对应一个类别ID,从0开始。而每个image001.txt文件里,每一行代表一个目标框,格式是:
class_id x_center y_center width height注意这里的所有坐标都是归一化的,即像素坐标除以图片的宽高后得到的0到1之间的浮点数。比如一张1000x800的图片,框的左上角在(100, 100),右下角在(300, 250),那么:
- 宽度 = 300 - 100 = 200
- 高度 = 250 - 100 = 150
- x_center = (100 + 200/2) / 1000 = 0.2
- y_center = (100 + 150/2) / 800 = 0.21875
- width = 200 / 1000 = 0.2
- height = 150 / 800 = 0.1875
所以txt中记录的一行就是0 0.2 0.21875 0.2 0.1875。搞清楚这个结构,你才真正理解YOLO格式,以后不管是手动检查标注还是写脚本做数据增强,心里都有底。
5.3 如果导出的格式不对,如何手动转COCO为YOLO
虽然Label Studio内置了YOLO导出,但有时候你会在Project设置里发现导出面板并没有YOLO选项。这通常是因为创建项目时选择的标注模板不被YOLO导出器识别,或者Label Studio版本较老。这时候不用慌,我们可以走一条通用路径:先导出COCO格式的JSON文件,再写个小脚本转成YOLO格式。
COCO格式的JSON是这样一个结构:它把所有信息集中在一个JSON文件里,包含images数组(每张图的id、宽高、文件名)、annotations数组(每个框所属的image_id、类别id、bbox坐标)、categories数组(所有类别名与id的映射)。
转成YOLO格式的核心逻辑就是用Python遍历这些数组,把COCO的bbox坐标(左上角x、左上角y、宽度、高度)转换为YOLO的归一化中心点坐标,然后按图片分组写到txt文件里。
下面这个脚本我一直在用,非常稳定,你可以直接抄走:
import json import os # 原COCO标注文件路径 coco_file = "coco.json" # 输出目录 output_dir = "yolo_dataset" os.makedirs(os.path.join(output_dir, "labels"), exist_ok=True) with open(coco_file, "r", encoding="utf-8") as f: coco_data = json.load(f) # 构建类名列表和id到索引的映射 categories = {cat["id"]: idx for idx, cat in enumerate(coco_data["categories"])} class_names = [cat["name"] for cat in coco_data["categories"]] with open(os.path.join(output_dir, "data.yaml"), "w", encoding="utf-8") as f: f.write("names:\n") for name in class_names: f.write(f" - {name}\n") # 构建图片信息字典:image_id -> (file_name, width, height) image_info = {} for img in coco_data["images"]: image_info[img["id"]] = (img["file_name"], img["width"], img["height"]) # 对每张图片,聚合所有标注框并写入txt annotations_by_image = {} for ann in coco_data["annotations"]: annotations_by_image.setdefault(ann["image_id"], []).append(ann) for img_id, anns in annotations_by_image.items(): file_name, img_w, img_h = image_info[img_id] txt_path = os.path.join(output_dir, "labels", os.path.splitext(os.path.basename(file_name))[0] + ".txt") with open(txt_path, "w", encoding="utf-8") as f: for ann in anns: cls_idx = categories[ann["category_id"]] bbox = ann["bbox"] # [x, y, w, h] x_center = (bbox[0] + bbox[2] / 2) / img_w y_center = (bbox[1] + bbox[3] / 2) / img_h width = bbox[2] / img_w height = bbox[3] / img_h # 过滤掉越界的异常框 if x_center <= 0 or y_center <= 0 or width <= 0 or height <= 0: continue f.write(f"{cls_idx} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n") print("转换完成,文件已输出到:", output_dir)关于这个脚本,有两个地方要提醒你注意。一是COCO的bbox是用左上角坐标加宽高来表示的,不是中心点;二是如果标注过程中出现过坐标越界、宽高为负的情况,脚本里我加了一个过滤条件,避免生成无效的训练数据。如果你用的Label Studio版本正常,一般不会出现负值,但写脚本时防一手总是稳妥的。
5.4 导出前的数据检查:训练之前最重要的5分钟
不管用内置导出还是脚本转换,导出完成后都别急着去训练。花五分钟检查一下结果,能帮你省下后面好几个小时的排查时间。
检查的内容主要有几个方面:一是每个标签类别对应的txt文件是否存在,文件数量是否和图片数量一致;二是txt文件里的坐标值是否都在0到1之间;三是打开一张有标注的图片,对比一下能显示出来的框和原来标注的形状是否吻合。
Label Studio还提供了一个很实用的功能:在数据导入前先检查一下源文件与标注文件的关联。具体做法是在项目面板找到Download Backup按钮,这个按钮会导出整个项目的元数据。有时候你发现标注结果对不上图片,多半就是任务和图片文件之间的对应关系出了问题,这时候可以回去用备份数据核对。
6. 半路最常翻车的位置与提升效率的小建议
6.1 坑一:自动保存并不等于万无一失
Label Studio有自动保存功能,标注操作的实时更新会被写入后端数据库。很多人因此就有一种“标了就会自动存好”的错觉。
但实际使用中我遇到过这样一个情况:项目组新来的实习生标注了200张图,第二天打开项目发现只显示了100张,原因是他标注完成后没有点Submit按钮,属于草稿状态的任务在特定情况下不会进入已完成列表。所以我的习惯是,每标注完一张图,顺手按一下快捷键或点击Submit,从机制上确保任务状态确实是Labeled。
如果你是一个标了很多张图又不想挨个点Submit的人,可以在项目Settings里开启Auto Submit功能。开启后,切换下一张图片时当前图片会自动提交,但小心,如果你还在犹豫要不要修改某个框误触了切换,那就不好找回来了。
6.2 坑二:大图片导致浏览器崩溃或卡顿
图片分辨率过高时,Label Studio在前端渲染会非常吃力,尤其是同时加载多个缩略图的时候。有些监控视频抽帧出来的图片是1920x1080,其实还好。但如果你做遥感图像或者显微图像,原始分辨率动辄上亿像素,这时候硬标肯定卡成幻灯片。
针对这种场景,比较务实的方案是先在本地用脚本对图片做切片,把大图切成若干个小图块(Tile),每个Tile单独标注,最后再把标注框的坐标映射回原图。整个过程说起来复杂,但就算不做切片,至少要把图片降采样到2400px以内再导入,否则浏览器页面会响应极慢,每一帧操作都有延迟,非常崩溃。
6.3 坑三:多人协作时标签命名混乱
如果你的项目不是一个人标,而是三个人分工,最容易出现的问题就是标签命名不统一。一个人把类别写成Cat,另一个人写成了cat,还有人写成Kitty,导出的YOLO类别数量直接翻倍。
这个问题的根源通常出在项目启动阶段没有统一规范。我从第二次做项目开始,都会在开工前写一个简单的标签规范说明,里面列出每个类别的标准名称、缩写、颜色,并把它贴在项目简介里。同时给不同成员分配不同的Task区间,避免同一个人重复标注同一批图片。Label Studio本身就支持在Project里管理团队成员和角色,你可以给成员设置Reviewer、Annotator之类的角色,让标注质量和进度都更可控。
6.4 提升标注效率的几个实操技巧
排完坑,再分享几个我实际用下来提升效率的小技巧。
一是善用快捷键。前文提过数字键切换标签,这里再说一个:画矩形框时,如果你觉得画出来的框要反复微调,可以适当放大画布再画,放大的情况下鼠标控制精度更高。缩小画布画大框,再放大微调,其实效率更高。
二是合理利用复制粘贴。对于一张图中有多个相同大小的目标,先画好一个框,调整到合适大小和比例,然后Ctrl+C、Ctrl+V复制几个,再拖到对应位置,能省下大量重复画框的时间。
三是随时关注数据分布。Label Studio首页的项目面板会显示每个项目的任务数量、已完成数量、标注进度百分比。我会给自己定个小目标:每标完100张图就导出一次备份,顺便看一下各类别数量有没有失衡。我做过的一个项目里,第一轮标注完发现某个类别的出现频率只有另一个的十分之一,于是及时补充采集了一批对应场景的数据,最后模型训练效果好很多。如果等全部标完才发现数据失衡,返工成本就大了。
6.5 最后再分享一点个人习惯
从最早用脚本手工标注,到后来使用Label Studio完成了几个完整的数据集项目,我最大的感受是:工具只是手段,它不能替你把数据质量提高,但能把你从机械劳动里解放出来,让你把精力花在真正重要的地方——检查类别定义是否清晰、框有没有包围住完整目标、边界案例有没有覆盖到。
数据集的标注质量决定了一个模型能走到什么程度。很多模型效果不好,问题不在于网络结构,而在于训练数据本身就有大量错标、漏标和边界不清的样本。我自己见过不少人用Label Studio标完数据,导出YOLO格式后跑出的mAP指标很低,排查来排查去,才发现是某个类别的框普遍画小了,把目标的一部分框出去了。
所以我的建议是:哪怕工具用熟了、流程跑顺了,也一定要定期抽查标注结果,尤其是项目中后期、疲劳状态下标注的图片。找一个没参与标注的人随机抽几十张图检查一遍,往往能发现你自己根本意识不到的规律性错误。这一点看起来笨拙,却是保证标注质量最有效的办法。