图像/视频标注工具CVAT

我的小站:Ean7的小站

中文版cvat项目地址:YQisme/cvat: 计算机视觉标注工具(CVAT)是一个领先的平台,用于为视觉人工智能构建高质量的视觉数据集。它提供开源、云端和企业级产品,以及标注服务,支持图像、视频和3D标注,具备AI辅助标注、质量保证、团队协作、分析和开发者API等功能。

CVAT 官方推荐使用 Docker Compose 部署,这是最简单、最稳定的方式。

如果是 Ubuntu 服务器(包括物理机、虚拟机、云服务器),按照下面步骤即可。

1. 安装 Docker

Ubuntu 22.04:

bash 复制代码
sudo apt update

sudo apt install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg

echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/usr/share/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list

sudo apt update

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

验证:

bash 复制代码
docker --version
docker compose version

2. 下载 CVAT

CVAT 开源项目:

CVAT GitHub 仓库

克隆代码:

bash 复制代码
git clone https://github.com/cvat-ai/cvat.git

cd cvat

3.指定访问地址

然后修改 .env

找到或添加:

CVAT_HOST=本机的ip

4.启动

直接执行:

bash 复制代码
docker compose up -d

首次启动会下载:

  • PostgreSQL
  • Redis
  • CVAT Server
  • CVAT UI
  • Traefik

大约需要几分钟。

查看状态:

bash 复制代码
docker ps

正常会看到:

bash 复制代码
cvat_server
cvat_ui
cvat_db
cvat_redis
traefik

5. 创建管理员账号

启动完成后:

bash 复制代码
docker exec -it cvat_server \
bash -ic 'python3 ~/manage.py createsuperuser'

输入:

bash 复制代码
Username
Email
Password

创建管理员账号。


6. 登录系统

浏览器打开:

bash 复制代码
http://服务器IP:8080

例如:

bash 复制代码
http://192.168.1.100:8080

登录刚创建的管理员账号即可。


CVAT 支持:

  • YOLOv7
  • SAM
  • Mask-RCNN
  • OpenVINO模型
  • Tracking模型

启动 Serverless 组件:

bash 复制代码
docker compose \
-f docker-compose.yml \
-f components/serverless/docker-compose.serverless.yml \
up -d

然后部署对应模型。

1. CVAT serverless 是什么关系?

CVAT 结构是这样的:

bash 复制代码
CVAT 主系统
  ├── cvat_server(必须)
  ├── cvat_ui(必须)
  ├── postgres / redis(必须)
  └── serverless(可选 AI 推理)
        ├── YOLO
        ├── SAM
        ├── OpenVINO

👉 serverless 只是"AI能力插件层"

依赖 CVAT 主服务存在


2. 两种启动方式对比

正确方式(官方推荐)

bash 复制代码
docker compose -f docker-compose.yml \
-f components/serverless/docker-compose.serverless.yml \
up -d

👉 一次性启动完整系统(推荐)


先主系统,再 serverless

Step 1
bash 复制代码
docker compose up -d
Step 2(后面再加)
bash 复制代码
docker compose -f components/serverless/docker-compose.serverless.yml up -d

👉 这样是 可以的

✔ CVAT 已运行 ✔ serverless 再补上 ✔ 不会冲突

概念

在 CVAT 里,这三个概念是一个层级关系:

bash 复制代码
Project(项目)

 ├─ Task(任务)

 │    ├─ Job(作业)

 │    ├─ Job(作业)

 │    └─ Job(作业)

 ├─ Task(任务)

 │    ├─ Job(作业)

 │    └─ Job(作业)

 └─ Task(任务)

官方定义中:

  • Project:管理多个任务,共享标签体系(Labels)
  • Task:一次具体的数据标注任务(图片集或视频)
  • Job :Task 自动切分后的实际标注工作单元 (docs.cvat.ai)

1. Project(项目)

Project 相当于一个数据集工程。

例如你要训练一个安全帽检测模型:

标签:

这些标签只需要在 Project 中定义一次。

然后可以创建多个 Task:

bash 复制代码
Helmet Detection

├─ Task1:工地A视频

├─ Task2:工地B视频

├─ Task3:工地C图片集

所有 Task 自动继承这些标签。 (docs.cvat.ai)


2. Task(任务)

Task 是实际上传的数据。

例如:

Task 1

上传:

CVAT:

bash 复制代码
Task: SiteA

Frames: 0~10000

或者:

Task 2

上传:

bash 复制代码
001.jpg

002.jpg

...

1000.jpg

CVAT:

一个 Task 对应:

  • 一个视频
  • 或一批图片
  • 或一个点云数据集

Task 是数据管理单位。 (docs.cvat.ai)


3. Job(作业)

Job 是 Task 的切片。

当 Task 太大时:

CVAT 可以自动切分:

bash 复制代码
Job1

0~1999



Job2

2000~3999



Job3

4000~5999



Job4

6000~7999



Job5

8000~9999

这样:

  • 标注员A做 Job1
  • 标注员B做 Job2
  • 标注员C做 Job3

多人并行工作。 (docs.cvat.ai)


实际例子

假设你有 30 个监控视频。

建立项目
bash 复制代码
Project

└─ Forklift Detection

标签:


创建任务
bash 复制代码
Task1

camera01.mp4



Task2

camera02.mp4



Task3

camera03.mp4

...

Task30

camera30.mp4

自动切分作业

例如:

设置:

CVAT 会生成:

bash 复制代码
Task1

├─ Job1 (0~999)

├─ Job2 (1000~1999)

├─ Job3 (2000~2999)

├─ Job4 (3000~3999)

└─ Job5 (4000~4999)

每个 Job 可以分配给不同标注员。 (docs.cvat.ai)


对 YOLO 数据集制作的建议

小项目(几百张)

直接:

不一定需要 Project。


正式项目(几千~几万张)

推荐:

bash 复制代码
Project

└─ Industrial Defect



    Task1

    └─ 原始图片批次1



    Task2

    └─ 原始图片批次2



    Task3

    └─ 视频抽帧批次1

这样:

  • 标签统一管理
  • 导出数据方便
  • 后期增加数据批次容易

这是 CVAT 官方推荐的使用方式。 (docs.cvat.ai)

简单记忆:

层级 含义 类比
Project 整个数据集工程 一个 YOLO 项目
Task 一批待标注数据 一个视频或一批图片
Job 分配给标注员的工作单元 Task 的切片

最常见的生产环境结构:

bash 复制代码
Project

 ├─ Task(一个视频)

 │    ├─ Job

 │    ├─ Job

 │    └─ Job

 ├─ Task(一个视频)

 └─ Task(一个视频)

如果你的 CVAT 已经部署完成并能正常访问 Web 页面,最快的入门路线是:

1. 创建项目(Project)

进入 CVAT 后:

Projects → Create Project

例如:

  • Name:YOLO11 Training
  • Labels:
    • person
    • car
    • bicycle

如果后续训练 YOLO,建议先把所有类别定义好。

例如:

bash 复制代码
person

car

truck

bus

motorcycle

Label

CVAT 新版本(尤其是 2.x 以后)创建 Label 时,通常会看到:

  • Raw(原始)
  • Constructor(构造器)

这两个是创建标签的两种方式。

方法一:Raw(推荐新手)

点击 Raw 后,直接填写标签定义。

例如:

或者带属性:

bash 复制代码
[

  {

    "name": "person",

    "attributes": [

      {

        "name": "gender",

        "input_type": "select",

        "values": ["male", "female"]

      }

    ]

  },

  {

    "name": "car"

  }

]

对于 YOLO 标注,最简单的是:

bash 复制代码
[

  {"name":"person"},

  {"name":"car"},

  {"name":"truck"}

]

保存后会生成对应类别。


方法二:Constructor(图形界面)

这是大多数用户使用的方法。

点击:

然后:

添加类别

点击:

填写:

bash 复制代码
Name: person

Type: Rectangle

保存。

继续添加:


Shape Type 选择

如果训练 YOLO 检测模型:

选择:

对应目标检测框。

如果做实例分割:

选择:

如果做人脸关键点:

选择:

如果做姿态估计:

选择:


YOLO11 检测推荐配置

假设你做人员检测:

Label
Type

假设做安全帽检测:

Labels

全部选择:


Attributes 要不要填?

新手可以先不填。

例如:

bash 复制代码
person

├── gender

├── age

└── occluded

这些属性:

  • 不会进入 YOLO 训练
  • 只用于数据管理

所以 YOLO 项目一般:

即可。


2. 创建任务(Task)

进入:

Tasks → Create New Task

填写:

bash 复制代码
Name: yolo_train

Project: YOLO11 Training

最好不要选择子集,统一标注完后最后再划分训练集和验证集

然后上传图片:

支持:

  • 本地上传
  • ZIP上传
  • 共享目录
  • 云存储

对于第一次使用:

直接上传几十张图片即可。


3. 开始标注

点击任务进入 Job。

最常用的是:

矩形框(Bounding Box)

快捷键:

或点击左侧:

操作:


4. 常用快捷键

功能 快捷键
下一张 F
上一张 D
画框 N
保存 Ctrl+S
删除 Del
放大 鼠标滚轮
平移画布 鼠标中键拖动

建议记住:

基本够用了。


5. 自动标注(推荐)

如果图片很多,不要纯手工标。

CVAT 支持:

  • YOLO
  • SAM
  • Grounding DINO
  • Segment Anything

如果你部署了 Serverless:

bash 复制代码
docker compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml up -d

进入任务后:

bash 复制代码
Actions

→ AI Tools

→ Automatic Annotation

选择模型:

自动生成标注后人工修正。

通常:

效率最高。


6. 导出 YOLO 数据集

标完后:

bash 复制代码
Task

→ Export Task Dataset

选择:

bash 复制代码
Ultralytics YOLO Detection 1.0

同时"保存图像"

导出得到:

bash 复制代码
dataset.zip

├── images

│   ├── train

├── labels

│   ├── train

└── data.yaml

默认只导出train,需要手动划分val,然后data.yaml也需要微调,下面使用一个脚本直接处理完

bash 复制代码
import os

import random

import shutil

import yaml



# =========================

# 配置

# =========================

IMAGE_DIR = "images/train"

LABEL_DIR = "labels/train"

DATA_YAML = "data.yaml"



VAL_RATIO = 0.2

SEED = 42

# =========================



random.seed(SEED)



def load_yaml(path):

    with open(path, "r", encoding="utf-8") as f:

        return yaml.safe_load(f)



def save_yaml(data, path):

    with open(path, "w", encoding="utf-8") as f:

        yaml.dump(data, f, sort_keys=False, allow_unicode=True)



# =========================

# 1. 读取 data.yaml(保留类别)

# =========================

cfg = load_yaml(DATA_YAML)



if "names" not in cfg:

    raise ValueError("❌ data.yaml 缺少 names")



names = cfg["names"]



# =========================

# 2. 获取图片

# =========================

images = [f for f in os.listdir(IMAGE_DIR)

          if f.lower().endswith((".jpg", ".png", ".jpeg"))]



images.sort()

random.shuffle(images)



val_count = int(len(images) * VAL_RATIO)

val_images = images[:val_count]

train_images = images[val_count:]



print(f"Total images: {len(images)}")

print(f"Train: {len(train_images)}")

print(f"Val: {len(val_images)}")



# =========================

# 3. 创建目录

# =========================

os.makedirs("images/val", exist_ok=True)

os.makedirs("labels/val", exist_ok=True)



# =========================

# 4. 移动函数

# =========================

def move_files(img_list, split):

    for img in img_list:

        name = os.path.splitext(img)[0]



        img_src = os.path.join(IMAGE_DIR, img)

        lbl_src = os.path.join(LABEL_DIR, name + ".txt")



        img_dst = os.path.join(f"images/{split}", img)

        lbl_dst = os.path.join(f"labels/{split}", name + ".txt")



        shutil.move(img_src, img_dst)



        if os.path.exists(lbl_src):

            shutil.move(lbl_src, lbl_dst)

        else:

            print(f"⚠️ missing label: {lbl_src}")



# 执行移动

move_files(train_images, "train")

move_files(val_images, "val")



print("✅ split done")



# =========================

# 5. 自动修 data.yaml

# =========================

cfg["path"] = "."

cfg["train"] = "images/train"

cfg["val"] = "images/val"



# (可选)删除 test 避免干扰

cfg.pop("test", None)



save_yaml(cfg, DATA_YAML)



print("✅ data.yaml updated")

print("🎯 Ready for YOLO training!")

最后用于训练:

bash 复制代码
yolo detect train model=yolo11n.pt data=data.yaml epochs=100

7. 最推荐的新手流程

假设你有 1000 张图片:

第一步

上传图片


第二步

手工标注 50 张


第三步

导出 YOLO 数据集

训练一个初始模型:


第四步

将训练好的模型导入 CVAT 自动标注

CVAT 自动预测:


第五步

人工修正

效率通常能提升:

或者

外部推理 + 导入 CVAT

bash 复制代码
YOLO模型 → 批量推理图片 → 生成 YOLO txt → 导入 CVAT

Step 1:跑模型批量推理
bash 复制代码
from ultralytics import YOLO
import cv2
import os

model = YOLO("best.pt")

imgs = "images/"
out = "labels/"

results = model(imgs)

for r in results:
    r.save_txt(out)

Step 2:导入 CVAT

在 CVAT:

bash 复制代码
Task → Dataset → Import annotations

选择:

  • YOLO 1.1 format
  • 或 Ultralytics YOLO Detection 1.0

Step 3:CVAT 自动加载结果

模型已经帮你画好框 只需要人工修正