完成
736
README.md
@@ -1,736 +0,0 @@
|
|||||||
# 4-2 图像标注实战(Label Studio)
|
|
||||||
|
|
||||||
## 学习目标
|
|
||||||
|
|
||||||
1. 掌握 Label Studio 的安装、启动与界面使用
|
|
||||||
2. 学会配置三种图像标注类型(分类、目标检测、语义分割)
|
|
||||||
3. 独立完成边界框、多边形、关键点的标注
|
|
||||||
4. 理解 VOC / YOLO / COCO 三种导出格式的区别
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Label Studio 简介
|
|
||||||
|
|
||||||
### 1.1 一句话定义
|
|
||||||
|
|
||||||
**Label Studio**:一个开源的、**多模态**数据标注平台,支持图像、文本、语音、视频等几乎所有数据类型的标注与团队协作。
|
|
||||||
|
|
||||||
### 1.2 为什么选它
|
|
||||||
|
|
||||||
我们这门课统一用 Label Studio 的原因:
|
|
||||||
|
|
||||||
| 需求 | Label Studio 对应能力 |
|
|
||||||
|------|---------------------|
|
|
||||||
| 多种数据类型 | 图像 / 文本 / 语音 / 视频 / 时序,一站式 |
|
|
||||||
| 不用写代码 | 通过 XML 标签可视化配置标注界面 |
|
|
||||||
| 团队协作 | 内置成员管理、角色权限、任务分配 |
|
|
||||||
| 格式灵活 | 内置 YOLO、COCO、VOC、JSON、CSV 导出 |
|
|
||||||
| 本地部署 | 数据不出学校,开源免费 |
|
|
||||||
|
|
||||||
```
|
|
||||||
传统方案(多工具切换):
|
|
||||||
图像 → LabelImg
|
|
||||||
文本 → Doccano
|
|
||||||
语音 → Audacity / ELAN
|
|
||||||
协作 → Excel 邮件
|
|
||||||
|
|
||||||
Label Studio 一站式:
|
|
||||||
图像 / 文本 / 语音 / 协作 → Label Studio
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.3 与其他工具的对比
|
|
||||||
|
|
||||||
| 工具 | 类型 | 适用场景 | 本课程 |
|
|
||||||
|------|------|---------|--------|
|
|
||||||
| **Label Studio** | 开源/多模态 | 课程学习 / 中小团队 / 综合项目 | 主选 |
|
|
||||||
| LabelImg | 开源/图像 | 极简目标检测 | 仅做对比 |
|
|
||||||
| CVAT | 开源/图像视频 | 大型分割、视频跟踪 | 了解 |
|
|
||||||
| Doccano | 开源/文本 | 简单文本分类 | 已弃用 |
|
|
||||||
| Scale AI | 商业 | 企业级标注 | 了解 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Label Studio 安装与启动
|
|
||||||
|
|
||||||
### 2.1 环境要求
|
|
||||||
|
|
||||||
- Python 3.8 及以上
|
|
||||||
- 浏览器(Chrome / Edge / Firefox)
|
|
||||||
- 建议 8G 以上内存
|
|
||||||
|
|
||||||
### 2.2 pip 安装(推荐)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 在命令行中执行
|
|
||||||
pip install label-studio
|
|
||||||
|
|
||||||
# 启动 Label Studio
|
|
||||||
label-studio
|
|
||||||
```
|
|
||||||
|
|
||||||
启动后命令行会显示:
|
|
||||||
|
|
||||||
```
|
|
||||||
[INFO] Label Studio is running at http://localhost:8080
|
|
||||||
[INFO] Open it in your browser to start labeling
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3 第一次打开
|
|
||||||
|
|
||||||
1. 浏览器访问 `http://localhost:8080`
|
|
||||||
2. 第一次进入需要 **Sign Up** 注册管理员账号(用你的邮箱 + 密码)
|
|
||||||
3. 注册后自动登录到主界面
|
|
||||||
|
|
||||||
```
|
|
||||||
┌────────────────────────────────────────────────────────┐
|
|
||||||
│ Label Studio │
|
|
||||||
│ Sign Up │
|
|
||||||
├────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Email: [___________________________] │
|
|
||||||
│ Username: [___________________________] │
|
|
||||||
│ Password: [___________________________] │
|
|
||||||
│ │
|
|
||||||
│ [ Create Account ] │
|
|
||||||
│ │
|
|
||||||
└────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.4 安装常见问题
|
|
||||||
|
|
||||||
| 问题 | 解决 |
|
|
||||||
|------|------|
|
|
||||||
| 端口 8080 被占用 | `label-studio --port 8001` |
|
|
||||||
| 启动报错找不到模块 | `pip install --upgrade label-studio` |
|
|
||||||
| 中文乱码 | 系统区域设置改为 UTF-8 |
|
|
||||||
| 浏览器无法打开 | 检查防火墙、放行端口 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 主界面导览
|
|
||||||
|
|
||||||
登录后看到的主界面:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
|
||||||
│ [Label Studio] Projects Members Settings [用户头像] │
|
|
||||||
├──────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ My Projects │
|
|
||||||
│ │
|
|
||||||
│ ┌────────────────────────────────────────────────┐ │
|
|
||||||
│ │ + Create Project │ │
|
|
||||||
│ └────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ 没有任何项目,点上面按钮开始 │
|
|
||||||
│ │
|
|
||||||
└──────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**主要功能区**:
|
|
||||||
- **Projects**:项目列表,每个项目对应一个标注任务
|
|
||||||
- **Members**:成员管理(管理员可见)
|
|
||||||
- **Settings**:个人设置
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 创建第一个项目:图像分类
|
|
||||||
|
|
||||||
### 4.1 任务说明
|
|
||||||
|
|
||||||
**任务**:对 10 张动物图片进行分类,类别有:cat / dog / bird
|
|
||||||
|
|
||||||
### 4.2 创建项目
|
|
||||||
|
|
||||||
1. 点击 `+ Create Project`
|
|
||||||
2. 填写:
|
|
||||||
- **Project Name**:`animal_classification`
|
|
||||||
- **Description**:动物图片三分类
|
|
||||||
3. 点击 `Save`
|
|
||||||
|
|
||||||
### 4.3 配置标注界面
|
|
||||||
|
|
||||||
进入项目后,先点 `Settings` → `Labeling Interface` → `Code`:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<View>
|
|
||||||
<Image name="image" value="$image"/>
|
|
||||||
<Choices name="label" toName="image" choice="single">
|
|
||||||
<Choice value="cat"/>
|
|
||||||
<Choice value="dog"/>
|
|
||||||
<Choice value="bird"/>
|
|
||||||
</Choices>
|
|
||||||
</View>
|
|
||||||
```
|
|
||||||
|
|
||||||
**逐行解释**:
|
|
||||||
|
|
||||||
```
|
|
||||||
<View> ← 整个标注界面的根标签
|
|
||||||
<Image ...> ← 显示图像($image 是任务数据里的字段名)
|
|
||||||
<Choices ...> ← 单选/多选控件
|
|
||||||
<Choice/> ← 一个选项
|
|
||||||
</View>
|
|
||||||
```
|
|
||||||
|
|
||||||
**关键参数**:
|
|
||||||
- `toName="image"`:告诉 Label Studio,这个控件是对上面 `Image` 控件的结果
|
|
||||||
- `choice="single"`:单选(可选 `single-radio`、`multiple`)
|
|
||||||
|
|
||||||
保存后点 `Label All Tasks` 就能看到标注界面。
|
|
||||||
|
|
||||||
### 4.4 导入数据
|
|
||||||
|
|
||||||
在项目页 → `Settings` → `Data Import`:
|
|
||||||
|
|
||||||
**方式 A:上传本地文件**
|
|
||||||
|
|
||||||
```
|
|
||||||
1. 准备文件夹
|
|
||||||
images/
|
|
||||||
├── cat_001.jpg
|
|
||||||
├── cat_002.jpg
|
|
||||||
├── dog_001.jpg
|
|
||||||
└── bird_001.jpg
|
|
||||||
|
|
||||||
2. 拖拽到上传区域
|
|
||||||
或点击 Upload Files 选中文件夹
|
|
||||||
```
|
|
||||||
|
|
||||||
**方式 B:上传 CSV / JSONL(推荐,标签稳定)**
|
|
||||||
|
|
||||||
`tasks.csv`:
|
|
||||||
|
|
||||||
```csv
|
|
||||||
image
|
|
||||||
images/cat_001.jpg
|
|
||||||
images/cat_002.jpg
|
|
||||||
images/dog_001.jpg
|
|
||||||
images/bird_001.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
**方式 C:粘贴 URL**
|
|
||||||
|
|
||||||
```csv
|
|
||||||
image
|
|
||||||
https://example.com/cat_001.jpg
|
|
||||||
https://example.com/dog_001.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
上传完后点 `Save` → 任务列表里就会看到每一张图对应一行任务。
|
|
||||||
|
|
||||||
### 4.5 开始标注
|
|
||||||
|
|
||||||
1. 点击 `Label All Tasks`
|
|
||||||
2. 进入标注界面:
|
|
||||||
- 上面是图片
|
|
||||||
- 下面是三个单选按钮
|
|
||||||
3. 选中正确类别
|
|
||||||
4. 点击右下角 `Submit` 进入下一张
|
|
||||||
5. 全部完成后回到项目页,可以看到完成进度
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────┐
|
|
||||||
│ Project: animal_classification │
|
|
||||||
├──────────────────────────────────────────────────┤
|
|
||||||
│ Tasks: 10 total / 7 done / 3 skipped │
|
|
||||||
│ Progress: ████████████░░░░░░ 70% │
|
|
||||||
└──────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.6 导出数据
|
|
||||||
|
|
||||||
项目页 → `Export`:
|
|
||||||
|
|
||||||
- 格式选 `JSON`
|
|
||||||
- 下载得到 `project-1-at-2024-05-20.json`
|
|
||||||
|
|
||||||
导出内容示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"data": {"image": "images/cat_001.jpg"},
|
|
||||||
"annotations": [{
|
|
||||||
"result": [{
|
|
||||||
"from_name": "label",
|
|
||||||
"to_name": "image",
|
|
||||||
"type": "choices",
|
|
||||||
"value": {"choices": ["cat"]}
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 创建第二个项目:目标检测(边界框)
|
|
||||||
|
|
||||||
### 5.1 任务说明
|
|
||||||
|
|
||||||
**任务**:对 10 张街景图进行目标检测,标出每张图中的 `person`、`car`、`bicycle`
|
|
||||||
|
|
||||||
### 5.2 项目配置 XML
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<View>
|
|
||||||
<Image name="image" value="$image"/>
|
|
||||||
<RectangleLabels name="label" toName="image" strokeWidth="3">
|
|
||||||
<Label value="person" background="#FF0000"/>
|
|
||||||
<Label value="car" background="#00FF00"/>
|
|
||||||
<Label value="bicycle" background="#0000FF"/>
|
|
||||||
</RectangleLabels>
|
|
||||||
</View>
|
|
||||||
```
|
|
||||||
|
|
||||||
**新增标签**:
|
|
||||||
- `<RectangleLabels>`:矩形框 + 标签(Label Studio 专门用于目标检测)
|
|
||||||
- `<Label value="..." background="...">`:每个标签的颜色
|
|
||||||
- `strokeWidth="3"`:边框线宽
|
|
||||||
|
|
||||||
### 5.3 标注操作
|
|
||||||
|
|
||||||
进入标注界面后:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────┐
|
|
||||||
│ [image] │
|
|
||||||
│ ┌────────┐ │
|
|
||||||
│ │ 人 │ ← 拖动鼠标画框 │
|
|
||||||
│ └────────┘ │
|
|
||||||
│ ┌─────────┐ │
|
|
||||||
│ │ 车 │ │
|
|
||||||
│ └─────────┘ │
|
|
||||||
├──────────────────────────────────────────────────┤
|
|
||||||
│ 标签栏: │
|
|
||||||
│ [ person ] [ car ] [ bicycle ] │
|
|
||||||
└──────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**操作步骤**:
|
|
||||||
|
|
||||||
1. 选中上方标签(点击 person/car/bicycle 之一)
|
|
||||||
2. 在图片上 **按住鼠标左键拖动**,画矩形框
|
|
||||||
3. 松开后,框会自动带上刚才选中的标签
|
|
||||||
4. 继续选下一个标签,再画下一个框
|
|
||||||
5. 同一张图可以有多个框
|
|
||||||
6. 标完后点 `Submit`
|
|
||||||
|
|
||||||
### 5.4 快捷键速查
|
|
||||||
|
|
||||||
| 快捷键 | 功能 |
|
|
||||||
|--------|------|
|
|
||||||
| `Ctrl + Enter` | 提交当前任务 |
|
|
||||||
| `H` | 切换显示已画框 |
|
|
||||||
| `Ctrl + Z` | 撤销上一个框 |
|
|
||||||
| `Delete` | 删除选中的框 |
|
|
||||||
| 数字 `1-9` | 快速选择第 N 个标签 |
|
|
||||||
| `+ / -` | 缩放图像 |
|
|
||||||
| 滚轮 | 上下滚动 |
|
|
||||||
|
|
||||||
### 5.5 边界框标注规范
|
|
||||||
|
|
||||||
**基本规则**:
|
|
||||||
|
|
||||||
```
|
|
||||||
正确(紧贴目标) 错误(框太大) 错误(截断)
|
|
||||||
|
|
||||||
┌──────────┐ ┌────────────────┐ ┌──────────
|
|
||||||
│ ▓▓▓▓▓▓▓ │ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │ ▓▓▓▓▓▓▓ │
|
|
||||||
│ ▓ 人 ▓▓ │ │ ▓ 人 ▓▓▓▓▓▓ │ │ ▓ 人 ▓▓ │
|
|
||||||
│ ▓▓▓▓▓▓▓ │ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │ ▓▓▓▓▓▓▓ │
|
|
||||||
└──────────┘ └────────────────┘ └──────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**多目标处理**:
|
|
||||||
|
|
||||||
- 一张图有几个就标几个,每个独立画框
|
|
||||||
- 互相重叠的目标也要分别标
|
|
||||||
- 远处很小的目标可以标 "difficult" 标记
|
|
||||||
|
|
||||||
**遮挡处理**:
|
|
||||||
|
|
||||||
```
|
|
||||||
情况1:目标被部分遮挡
|
|
||||||
→ 标可见部分(标框框住能看到的人)
|
|
||||||
|
|
||||||
情况2:目标被严重遮挡(< 20% 可见)
|
|
||||||
→ 建议不标(标记为非标注目标)
|
|
||||||
|
|
||||||
情况3:多人重叠
|
|
||||||
→ 每个独立标,画框按可见边界
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.6 YOLO 格式导出
|
|
||||||
|
|
||||||
项目页 → `Export` → 选 `YOLO`:
|
|
||||||
|
|
||||||
下载得到压缩包,解压后结构:
|
|
||||||
|
|
||||||
```
|
|
||||||
project-1-at-2024-05-20-yolo/
|
|
||||||
├── images/
|
|
||||||
│ ├── street_001.jpg
|
|
||||||
│ └── street_002.jpg
|
|
||||||
├── labels/
|
|
||||||
│ ├── street_001.txt
|
|
||||||
│ └── street_002.txt
|
|
||||||
├── classes.txt
|
|
||||||
└── notes.json
|
|
||||||
```
|
|
||||||
|
|
||||||
`classes.txt`(类别映射):
|
|
||||||
|
|
||||||
```
|
|
||||||
0: person
|
|
||||||
1: car
|
|
||||||
2: bicycle
|
|
||||||
```
|
|
||||||
|
|
||||||
`street_001.txt`(一张图的标注):
|
|
||||||
|
|
||||||
```
|
|
||||||
# 格式: <class_id> <x_center> <y_center> <width> <height>
|
|
||||||
# 所有值都是相对图片尺寸的归一化值 (0-1)
|
|
||||||
0 0.35 0.45 0.18 0.55
|
|
||||||
1 0.75 0.50 0.30 0.20
|
|
||||||
```
|
|
||||||
|
|
||||||
**逐字段解释**:
|
|
||||||
|
|
||||||
```
|
|
||||||
0 → 类别 ID(0=person)
|
|
||||||
0.35 → 框中心点 x / 图片宽度
|
|
||||||
0.45 → 框中心点 y / 图片高度
|
|
||||||
0.18 → 框宽度 / 图片宽度
|
|
||||||
0.55 → 框高度 / 图片高度
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.7 Pascal VOC 格式导出
|
|
||||||
|
|
||||||
项目页 → `Export` → 选 `PascalVOC`:
|
|
||||||
|
|
||||||
`street_001.xml`:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<annotation>
|
|
||||||
<filename>street_001.jpg</filename>
|
|
||||||
<size>
|
|
||||||
<width>1920</width>
|
|
||||||
<height>1080</height>
|
|
||||||
<depth>3</depth>
|
|
||||||
</size>
|
|
||||||
<object>
|
|
||||||
<name>person</name>
|
|
||||||
<bndbox>
|
|
||||||
<xmin>134</xmin>
|
|
||||||
<ymin>216</ymin>
|
|
||||||
<xmax>478</xmax>
|
|
||||||
<ymax>810</ymax>
|
|
||||||
</bndbox>
|
|
||||||
</object>
|
|
||||||
<object>
|
|
||||||
<name>car</name>
|
|
||||||
<bndbox>
|
|
||||||
<xmin>1100</xmin>
|
|
||||||
<ymin>450</ymin>
|
|
||||||
<xmax>1680</xmax>
|
|
||||||
</bndbox>
|
|
||||||
</object>
|
|
||||||
</annotation>
|
|
||||||
```
|
|
||||||
|
|
||||||
注意 VOC 用的是**绝对像素坐标**(xmin、ymin、xmax、ymax),而 YOLO 是**归一化坐标**(0-1)。
|
|
||||||
|
|
||||||
### 5.8 COCO 格式导出
|
|
||||||
|
|
||||||
项目页 → `Export` → 选 `COCO`:
|
|
||||||
|
|
||||||
`result.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"images": [
|
|
||||||
{"id": 1, "file_name": "street_001.jpg", "width": 1920, "height": 1080},
|
|
||||||
{"id": 2, "file_name": "street_002.jpg", "width": 1920, "height": 1080}
|
|
||||||
],
|
|
||||||
"categories": [
|
|
||||||
{"id": 0, "name": "person"},
|
|
||||||
{"id": 1, "name": "car"},
|
|
||||||
{"id": 2, "name": "bicycle"}
|
|
||||||
],
|
|
||||||
"annotations": [
|
|
||||||
{
|
|
||||||
"id": 1, "image_id": 1, "category_id": 0,
|
|
||||||
"bbox": [134, 216, 344, 594],
|
|
||||||
"area": 204336,
|
|
||||||
"iscrowd": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
注意 COCO 的 `bbox` 格式是 `[x_min, y_min, width, height]`(**前两个是左上角,后两个是宽高**),和 VOC 略有区别。
|
|
||||||
|
|
||||||
### 5.9 三种格式对比
|
|
||||||
|
|
||||||
| 特性 | VOC | YOLO | COCO |
|
|
||||||
|------|-----|------|------|
|
|
||||||
| 文件类型 | 每个图一个 XML | 每个图一个 TXT | 所有图一个 JSON |
|
|
||||||
| 坐标系 | 绝对像素 (xmin,ymin,xmax,ymax) | 归一化 0-1(中心点+宽高)| 绝对像素 (x,y,w,h) |
|
|
||||||
| 多目标 | 一个 object 一对 bndbox | 一行一个目标 | 一个 annotation 一个目标 |
|
|
||||||
| 适用框架 | Faster R-CNN, SSD | YOLO 系列 | Mask R-CNN, Detectron2 |
|
|
||||||
| 优缺点 | 直观但文件多 | 紧凑、YOLO 专用 | 强大、适合大项目 |
|
|
||||||
| 人类可读 | ⭐⭐⭐ | ⭐ | ⭐⭐ |
|
|
||||||
|
|
||||||
**怎么选**:
|
|
||||||
- 学 YOLO 模型 → 导 YOLO
|
|
||||||
- 用 MMDetection / Detectron2 → 导 COCO
|
|
||||||
- 通用、自己写训练脚本 → 导 VOC 或 JSON
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 创建第三个项目:语义分割(多边形)
|
|
||||||
|
|
||||||
### 6.1 任务说明
|
|
||||||
|
|
||||||
**任务**:对 5 张街景图做**像素级**语义分割,类别:road、building、sky、vegetation
|
|
||||||
|
|
||||||
### 6.2 项目配置 XML
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<View>
|
|
||||||
<Image name="image" value="$image"/>
|
|
||||||
<PolygonLabels name="label" toName="image" strokeWidth="2">
|
|
||||||
<Label value="road" background="#808080"/>
|
|
||||||
<Label value="building" background="#A52A2A"/>
|
|
||||||
<Label value="sky" background="#87CEEB"/>
|
|
||||||
<Label value="vegetation" background="#228B22"/>
|
|
||||||
</PolygonLabels>
|
|
||||||
</View>
|
|
||||||
```
|
|
||||||
|
|
||||||
`<PolygonLabels>`:多边形 + 标签,专门用于语义分割。
|
|
||||||
|
|
||||||
### 6.3 标注操作
|
|
||||||
|
|
||||||
1. 选中标签(点击 road / building / sky / vegetation)
|
|
||||||
2. 在图像上**连续点击多个点**画出多边形
|
|
||||||
3. **双击或按回车**结束当前多边形
|
|
||||||
4. 一个类别画完,再选下一个类别继续
|
|
||||||
5. 一张图可能有很多个多边形
|
|
||||||
|
|
||||||
```
|
|
||||||
标注过程示意:
|
|
||||||
|
|
||||||
点 1 ──→ 点 2
|
|
||||||
│
|
|
||||||
↓
|
|
||||||
点 4 ←── 点 3
|
|
||||||
(双击结束)
|
|
||||||
|
|
||||||
┌──────────────────┐
|
|
||||||
│ │
|
|
||||||
│ 1────────2 │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ 区域 │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ 4────────3 │
|
|
||||||
│ │
|
|
||||||
└──────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.4 分割标注注意事项
|
|
||||||
|
|
||||||
| 情况 | 处理 |
|
|
||||||
|------|------|
|
|
||||||
| 两个区域紧贴 | 各画一个多边形,不要合成一个 |
|
|
||||||
| 区域很小 | 至少 3 个点画出三角形 |
|
|
||||||
| 形状不规则 | 多点描边,越细越准 |
|
|
||||||
| 类别不确定 | 选最接近的,宁可错记也不要漏 |
|
|
||||||
|
|
||||||
### 6.5 导出多边形
|
|
||||||
|
|
||||||
项目页 → `Export` → 选 `JSON`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"data": {"image": "street_001.jpg"},
|
|
||||||
"annotations": [{
|
|
||||||
"result": [{
|
|
||||||
"from_name": "label",
|
|
||||||
"to_name": "image",
|
|
||||||
"type": "polygonlabels",
|
|
||||||
"value": {
|
|
||||||
"points": [[0.1, 0.8], [0.9, 0.8], [0.9, 1.0], [0.1, 1.0]],
|
|
||||||
"polygonlabels": ["road"]
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`points` 数组里每个点都是 `[x, y]` 的归一化坐标(0-1),是按顺时针或逆时针排列的多边形顶点。
|
|
||||||
|
|
||||||
> 💡 想转成 mask(像素级 PNG 图)?需要用 `label-studio-converter` 工具,详见 4-4 节。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 课堂演示:Python SDK
|
|
||||||
|
|
||||||
### 7.1 简介
|
|
||||||
|
|
||||||
Label Studio 提供 `label-studio-sdk`,可以用 Python 编程式管理项目、查询任务、写入预标注。
|
|
||||||
|
|
||||||
### 7.2 安装
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install label-studio-sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.3 常用代码
|
|
||||||
|
|
||||||
```python
|
|
||||||
from label_studio_sdk import Client
|
|
||||||
|
|
||||||
# 连接本地服务
|
|
||||||
ls = Client(url='http://localhost:8080', api_key='YOUR_API_KEY')
|
|
||||||
|
|
||||||
# 获取 API Key:
|
|
||||||
# Label Studio → Settings → Account → Access Token
|
|
||||||
|
|
||||||
# 列出所有项目
|
|
||||||
projects = ls.list_projects()
|
|
||||||
for p in projects:
|
|
||||||
print(p.id, p.title)
|
|
||||||
|
|
||||||
# 获取某个项目的任务
|
|
||||||
project = ls.get_project(id=1)
|
|
||||||
tasks = project.get_tasks()
|
|
||||||
print(f"项目里有 {len(tasks)} 个任务")
|
|
||||||
|
|
||||||
# 导入新任务
|
|
||||||
project.import_tasks([
|
|
||||||
{"image": "images/new_001.jpg"},
|
|
||||||
{"image": "images/new_002.jpg"}
|
|
||||||
])
|
|
||||||
|
|
||||||
# 导出标注
|
|
||||||
project.export_tasks(export_type='JSON')
|
|
||||||
```
|
|
||||||
|
|
||||||
> 这一节以了解为主,详细用法 4-4 节会展开。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. 实战练习
|
|
||||||
|
|
||||||
### 8.1 任务一:图像分类(必做)
|
|
||||||
|
|
||||||
**任务**:在 Label Studio 创建项目 `fruit_classification`
|
|
||||||
|
|
||||||
**步骤**:
|
|
||||||
1. 准备 10 张水果图片(apple、banana、orange)
|
|
||||||
2. 启动 Label Studio,创建项目
|
|
||||||
3. 配置 `<Choices>` 三分类界面
|
|
||||||
4. 上传 10 张图
|
|
||||||
5. 完成 10 张标注
|
|
||||||
6. 导出 JSON 并截图
|
|
||||||
|
|
||||||
### 8.2 任务二:目标检测(必做)
|
|
||||||
|
|
||||||
**任务**:创建项目 `vehicle_detection`
|
|
||||||
|
|
||||||
**步骤**:
|
|
||||||
1. 准备 10 张街景图
|
|
||||||
2. 配置 `<RectangleLabels>` 目标检测界面
|
|
||||||
3. 类别:person / car / bicycle / traffic_light
|
|
||||||
4. 标完所有目标
|
|
||||||
5. 导出 YOLO 格式
|
|
||||||
6. 检查导出的 .txt 文件坐标是否正确
|
|
||||||
|
|
||||||
### 8.3 任务三(选做):语义分割
|
|
||||||
|
|
||||||
**任务**:创建项目 `scene_segmentation`
|
|
||||||
|
|
||||||
**步骤**:
|
|
||||||
1. 准备 3-5 张风景图
|
|
||||||
2. 配置 `<PolygonLabels>` 分割界面
|
|
||||||
3. 类别:sky / vegetation / road / building
|
|
||||||
4. 画多边形
|
|
||||||
5. 导出 JSON
|
|
||||||
|
|
||||||
### 8.4 标注自检清单
|
|
||||||
|
|
||||||
```
|
|
||||||
图像分类:
|
|
||||||
□ 类别选择是否正确
|
|
||||||
□ 是否有漏标的任务
|
|
||||||
□ 导出 JSON 是否完整
|
|
||||||
|
|
||||||
目标检测:
|
|
||||||
□ 边界框是否紧贴目标
|
|
||||||
□ 是否每个目标都标了
|
|
||||||
□ 框内类别是否正确
|
|
||||||
□ 导出 YOLO 格式后坐标值是否在 0-1 之间
|
|
||||||
□ classes.txt 是否齐全
|
|
||||||
|
|
||||||
语义分割:
|
|
||||||
□ 多边形顶点是否足够(至少 3 个)
|
|
||||||
□ 类别判断是否准确
|
|
||||||
□ 不同区域之间是否有遗漏
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. 本节知识小结
|
|
||||||
|
|
||||||
```
|
|
||||||
┌────────────────────────────────────────────────────────────┐
|
|
||||||
│ 4-2 图像标注实战(Label Studio) │
|
|
||||||
├────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ 📌 Label Studio 是多模态标注平台,一站式解决图像/文本/语音 │
|
|
||||||
│ │
|
|
||||||
│ 📌 安装: │
|
|
||||||
│ pip install label-studio │
|
|
||||||
│ label-studio │
|
|
||||||
│ 浏览器访问 http://localhost:8080 │
|
|
||||||
│ │
|
|
||||||
│ 📌 三种图像标注类型: │
|
|
||||||
│ 分类 → <Choices> │
|
|
||||||
│ 目标检测 → <RectangleLabels> │
|
|
||||||
│ 语义分割 → <PolygonLabels> │
|
|
||||||
│ │
|
|
||||||
│ 📌 三种导出格式: │
|
|
||||||
│ VOC → XML,像素绝对坐标 │
|
|
||||||
│ YOLO → TXT,归一化 0-1 坐标 │
|
|
||||||
│ COCO → JSON,[x,y,w,h] 像素坐标 │
|
|
||||||
│ │
|
|
||||||
│ 📌 标注规范:框要紧贴目标、不多不少 │
|
|
||||||
│ │
|
|
||||||
└────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 课后任务
|
|
||||||
|
|
||||||
1. 安装 Label Studio,截图证明能正常打开
|
|
||||||
2. 完成"任务一"图像分类 10 张图
|
|
||||||
3. 完成"任务二"目标检测 10 张图,导出 YOLO 格式
|
|
||||||
4. (选做)完成"任务三"语义分割
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 下节预告
|
|
||||||
|
|
||||||
下一节我们将学习 **4-3 文本标注与语音标注(Label Studio)**:
|
|
||||||
- 文本分类、NER 命名实体识别
|
|
||||||
- 意图识别与槽位标注
|
|
||||||
- 语音转写 ASR
|
|
||||||
- 说话人分离与音频分类
|
|
||||||
BIN
data/images/标注练习1.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
7
data/reviews.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{"id": 1, "text": "外卖小哥送得超快,餐盒还是热的,炸鸡酥脆多汁,酸辣粉也很正宗,分量足,五星好评!"},
|
||||||
|
{"id": 2, "text": "等了一个半小时才送到,汤全洒了,面坨成一坨,联系客服也不回,太让人失望了。"},
|
||||||
|
{"id": 3, "text": "奶茶是用料很扎实的现煮茶,珍珠Q弹有嚼劲,配送员态度也好,下次还会再点。"},
|
||||||
|
{"id": 4, "text": "配送速度一般,但披萨味道不错,芝士拉丝效果好,性价比高,值得推荐。"},
|
||||||
|
{"id": 5, "text": "点的麻辣烫食材不新鲜,有股怪味,吃完拉肚子,商家推卸责任,再也不点了。"}
|
||||||
|
]
|
||||||
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 430 KiB |
|
Before Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 402 KiB |
|
Before Width: | Height: | Size: 313 KiB |
|
Before Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 703 KiB |
|
Before Width: | Height: | Size: 338 KiB |
102
q2_1_crawler/move.html
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"title": "泰坦尼克号",
|
||||||
|
"director": "Frank Darabont",
|
||||||
|
"year": "2015",
|
||||||
|
"rating": "6.8",
|
||||||
|
"duration": "91",
|
||||||
|
"genre": "科幻",
|
||||||
|
"actors_count": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"title": "星际穿越",
|
||||||
|
"director": "陈凯歌",
|
||||||
|
"year": "2021",
|
||||||
|
"rating": "6.2",
|
||||||
|
"duration": "113",
|
||||||
|
"genre": "科幻",
|
||||||
|
"actors_count": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"title": "三傻大闹宝莱坞",
|
||||||
|
"director": "Robert Zemeckis",
|
||||||
|
"year": "2004",
|
||||||
|
"rating": "7.4",
|
||||||
|
"duration": "95",
|
||||||
|
"genre": "悬疑",
|
||||||
|
"actors_count": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"title": "阿甘正传",
|
||||||
|
"director": "James Cameron",
|
||||||
|
"year": "2013",
|
||||||
|
"rating": "6.9",
|
||||||
|
"duration": "93",
|
||||||
|
"genre": "爱情",
|
||||||
|
"actors_count": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"title": "放牛班的春天",
|
||||||
|
"director": "宫崎骏",
|
||||||
|
"year": "2005",
|
||||||
|
"rating": "7.1",
|
||||||
|
"duration": "127",
|
||||||
|
"genre": "悬疑",
|
||||||
|
"actors_count": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"title": "千与千寻",
|
||||||
|
"director": "Christopher Nolan",
|
||||||
|
"year": "2024",
|
||||||
|
"rating": "6.4",
|
||||||
|
"duration": "147",
|
||||||
|
"genre": "动画",
|
||||||
|
"actors_count": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7",
|
||||||
|
"title": "忠犬八公的故事",
|
||||||
|
"director": "Lasse Hallström",
|
||||||
|
"year": "2002",
|
||||||
|
"rating": "6.2",
|
||||||
|
"duration": "166",
|
||||||
|
"genre": "剧情",
|
||||||
|
"actors_count": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8",
|
||||||
|
"title": "霸王别姬",
|
||||||
|
"director": "Rajkumar Hirani",
|
||||||
|
"year": "2005",
|
||||||
|
"rating": "7.9",
|
||||||
|
"duration": "149",
|
||||||
|
"genre": "冒险",
|
||||||
|
"actors_count": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9",
|
||||||
|
"title": "肖申克的救赎",
|
||||||
|
"director": "Christophe Barratier",
|
||||||
|
"year": "2008",
|
||||||
|
"rating": "9.3",
|
||||||
|
"duration": "91",
|
||||||
|
"genre": "冒险",
|
||||||
|
"actors_count": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10",
|
||||||
|
"title": "盗梦空间",
|
||||||
|
"director": "Christopher Nolan",
|
||||||
|
"year": "2019",
|
||||||
|
"rating": "7.1",
|
||||||
|
"duration": "132",
|
||||||
|
"genre": "剧情",
|
||||||
|
"actors_count": "5"
|
||||||
|
}
|
||||||
|
]
|
||||||
102
q2_1_crawler/movie.json
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"title": "泰坦尼克号",
|
||||||
|
"director": "Frank Darabont",
|
||||||
|
"year": "2015",
|
||||||
|
"rating": "6.8",
|
||||||
|
"duration": "91",
|
||||||
|
"genre": "科幻",
|
||||||
|
"actors_count": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"title": "星际穿越",
|
||||||
|
"director": "陈凯歌",
|
||||||
|
"year": "2021",
|
||||||
|
"rating": "6.2",
|
||||||
|
"duration": "113",
|
||||||
|
"genre": "科幻",
|
||||||
|
"actors_count": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"title": "三傻大闹宝莱坞",
|
||||||
|
"director": "Robert Zemeckis",
|
||||||
|
"year": "2004",
|
||||||
|
"rating": "7.4",
|
||||||
|
"duration": "95",
|
||||||
|
"genre": "悬疑",
|
||||||
|
"actors_count": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"title": "阿甘正传",
|
||||||
|
"director": "James Cameron",
|
||||||
|
"year": "2013",
|
||||||
|
"rating": "6.9",
|
||||||
|
"duration": "93",
|
||||||
|
"genre": "爱情",
|
||||||
|
"actors_count": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"title": "放牛班的春天",
|
||||||
|
"director": "宫崎骏",
|
||||||
|
"year": "2005",
|
||||||
|
"rating": "7.1",
|
||||||
|
"duration": "127",
|
||||||
|
"genre": "悬疑",
|
||||||
|
"actors_count": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"title": "千与千寻",
|
||||||
|
"director": "Christopher Nolan",
|
||||||
|
"year": "2024",
|
||||||
|
"rating": "6.4",
|
||||||
|
"duration": "147",
|
||||||
|
"genre": "动画",
|
||||||
|
"actors_count": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7",
|
||||||
|
"title": "忠犬八公的故事",
|
||||||
|
"director": "Lasse Hallström",
|
||||||
|
"year": "2002",
|
||||||
|
"rating": "6.2",
|
||||||
|
"duration": "166",
|
||||||
|
"genre": "剧情",
|
||||||
|
"actors_count": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8",
|
||||||
|
"title": "霸王别姬",
|
||||||
|
"director": "Rajkumar Hirani",
|
||||||
|
"year": "2005",
|
||||||
|
"rating": "7.9",
|
||||||
|
"duration": "149",
|
||||||
|
"genre": "冒险",
|
||||||
|
"actors_count": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9",
|
||||||
|
"title": "肖申克的救赎",
|
||||||
|
"director": "Christophe Barratier",
|
||||||
|
"year": "2008",
|
||||||
|
"rating": "9.3",
|
||||||
|
"duration": "91",
|
||||||
|
"genre": "冒险",
|
||||||
|
"actors_count": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10",
|
||||||
|
"title": "盗梦空间",
|
||||||
|
"director": "Christopher Nolan",
|
||||||
|
"year": "2019",
|
||||||
|
"rating": "7.1",
|
||||||
|
"duration": "132",
|
||||||
|
"genre": "剧情",
|
||||||
|
"actors_count": "5"
|
||||||
|
}
|
||||||
|
]
|
||||||
65
q2_1_crawler/q2_1.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup as bs
|
||||||
|
import json
|
||||||
|
|
||||||
|
url = 'https://exam.detr.top/exam-b/movies'
|
||||||
|
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36 Edg/149.0.0.0',
|
||||||
|
'Referer':'https://exam.detr.top/exam-b/movies'}
|
||||||
|
req = requests.get(url, headers=headers)
|
||||||
|
req.encoding="utf-8"
|
||||||
|
|
||||||
|
data=[]
|
||||||
|
|
||||||
|
soup=bs(req.text,"html.parser")
|
||||||
|
# print(soup)
|
||||||
|
#id, title, director, year, rating, duration, genre, actors_count
|
||||||
|
|
||||||
|
item=soup.select("table tbody tr" )
|
||||||
|
movie_list=[]
|
||||||
|
|
||||||
|
for tr in item:
|
||||||
|
tds=tr.find_all("td")
|
||||||
|
tds=list(tds)
|
||||||
|
# print(tds)
|
||||||
|
if len(tds)<8:
|
||||||
|
continue
|
||||||
|
movie={
|
||||||
|
"id":tds[0].get_text(strip=True),
|
||||||
|
"title":tds[1].get_text(strip=True),
|
||||||
|
"director":tds[2].get_text(strip=True),
|
||||||
|
"year":tds[3].get_text(strip=True),
|
||||||
|
"rating":tds[4].get_text(strip=True),
|
||||||
|
"duration":tds[5].get_text(strip=True),
|
||||||
|
"genre":tds[6].get_text(strip=True),
|
||||||
|
"actors_count":tds[7].get_text(strip=True)
|
||||||
|
}
|
||||||
|
movie_list.append(movie)
|
||||||
|
print(movie_list)
|
||||||
|
|
||||||
|
|
||||||
|
with open('movie.json', 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(movie_list, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
with open("move.html","w",encoding='utf-8') as f:
|
||||||
|
json.dump(movie_list, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# for i in range(len(items)):
|
||||||
|
# rank=i+1
|
||||||
|
# title=items[i].find("span",class_="title").get_text()
|
||||||
|
# actors=items[i].find("div",class_="bd").get_text().strip()
|
||||||
|
# try:
|
||||||
|
# actors=actors.split("主演:")[1].split("\n")[0]
|
||||||
|
# except:
|
||||||
|
# actors="无"
|
||||||
|
# quote=items[i].find("p",class_="quote").get_text().strip()
|
||||||
|
|
||||||
|
# data.append({
|
||||||
|
# "rank":rank,
|
||||||
|
# "title":title,
|
||||||
|
# "actors":actors,
|
||||||
|
# "quote":quote
|
||||||
|
# })
|
||||||
43
q2_1_crawler/q2_2.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# ① 找出评分最高和最低的电影,打印电影名 + 评分。
|
||||||
|
# ② 统计各类型的电影数量,用字典格式输出。
|
||||||
|
# ③ 统计各导演的电影数量,用字典格式输出。
|
||||||
|
# ④ 统计 2020 年(含)以后上映的电影数量。
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
with open('movie.json', 'r', encoding='utf-8') as f:
|
||||||
|
data=json.load(f)
|
||||||
|
# print(data)
|
||||||
|
|
||||||
|
|
||||||
|
sort_movie=sorted(data,key=lambda x:x["rating"])
|
||||||
|
min=sort_movie[0]
|
||||||
|
max=sort_movie[-1]
|
||||||
|
print("评分最低的电影",min["title"],min["rating"])
|
||||||
|
print("评分最高的电影",max["title"],max["rating"])
|
||||||
|
|
||||||
|
genre_shu={}
|
||||||
|
for g in data:
|
||||||
|
ge=g["genre"]
|
||||||
|
if ge in genre_shu:
|
||||||
|
genre_shu[ge]+=1
|
||||||
|
else:
|
||||||
|
genre_shu[ge]=1
|
||||||
|
print("各类型的电影数量",genre_shu)
|
||||||
|
|
||||||
|
director_shu={}
|
||||||
|
for d in data:
|
||||||
|
di=d["director"]
|
||||||
|
if di in director_shu:
|
||||||
|
director_shu[di]+=1
|
||||||
|
else:
|
||||||
|
director_shu[di]=1
|
||||||
|
print("各导演的电影数量",director_shu)
|
||||||
|
|
||||||
|
a=0
|
||||||
|
for y in data:
|
||||||
|
if int(y["year"]) >= 2020:
|
||||||
|
a+=1
|
||||||
|
print("2020 年(含)以后上映的电影数量",a)
|
||||||
BIN
q3/q3_1/q3_1_image_label.zip
Normal file
1
q3/q3_2/q3_2_takeout_reviews.json
Normal file
13
q3/q3_3_质量自评.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
1.标注前准备:
|
||||||
|
在图片标注中
|
||||||
|
(1)边界框必须紧贴目标物轮廓
|
||||||
|
(2)标签必须为 cat、dog、car(必须小写英文,不能写成"猫/狗/车"或"Cat/Dog/Car")
|
||||||
|
在文本标注中
|
||||||
|
(1)标注必须包含每条评论的 id 和 text 字段
|
||||||
|
(2)每个标注必须有一个 sentiment 字段,值为 "正面" 或 "负面"
|
||||||
|
2.标注过程
|
||||||
|
在图像的标注中,边界框内的留白较多,解决方法:最大限度地贴近目标轮廓
|
||||||
|
在文本的标注中,可能会遇到情感模糊的问题,解决办法:抓住关键词进行标注。
|
||||||
|
3.标注后检查
|
||||||
|
在图像标注中,再次检查边界框与目标轮廓是否贴合,检查导出的 yolo 文件内容是否完整
|
||||||
|
在文本标注中,对照关键词检查情感是否判断正确,检查导出的 json文件内容是否完整
|
||||||
33
q4/q4_1/q4_1.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
with open('movie.json', 'r', encoding='utf-8') as f:
|
||||||
|
data=json.load(f)
|
||||||
|
# print(data)
|
||||||
|
|
||||||
|
genre_shu={}
|
||||||
|
for g in data:
|
||||||
|
ge=g["genre"]
|
||||||
|
if ge in genre_shu:
|
||||||
|
genre_shu[ge]+=1
|
||||||
|
else:
|
||||||
|
genre_shu[ge]=1
|
||||||
|
# print("各类型的电影数量",genre_shu)
|
||||||
|
|
||||||
|
genre_lei=list(genre_shu.keys())
|
||||||
|
genre_liang=list(genre_shu.values())
|
||||||
|
|
||||||
|
print(genre_liang)
|
||||||
|
|
||||||
|
plt.figure(figsize=(14, 12))
|
||||||
|
plt.bar(genre_lei, genre_liang, # 类别, 数值
|
||||||
|
width=0.6) # 柱子宽度(0~1之间)
|
||||||
|
|
||||||
|
# 标题和标签
|
||||||
|
plt.title('类型电影数量分布', fontsize=14)
|
||||||
|
plt.xlabel('类型名称', fontsize=12)
|
||||||
|
plt.ylabel('电影数量', fontsize=12)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
BIN
q4/q4_1/q4_1_bar.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
24
q4/q4_2/q4_2.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import json
|
||||||
|
|
||||||
|
rating=[]
|
||||||
|
duration=[]
|
||||||
|
|
||||||
|
with open('movie.json', 'r', encoding='utf-8') as f:
|
||||||
|
data=json.load(f)
|
||||||
|
# print(data)
|
||||||
|
for i in data:
|
||||||
|
rating.append(i["rating"])
|
||||||
|
duration.append(i["duration"])
|
||||||
|
plt.figure(figsize=(12, 8))
|
||||||
|
plt.scatter(duration, rating,
|
||||||
|
c='red',
|
||||||
|
s=80, # 点的大小
|
||||||
|
alpha=0.6, # 透明度
|
||||||
|
edgecolors='white') # 点的边框
|
||||||
|
plt.title('时长与评分关系散点图', fontsize=14)
|
||||||
|
plt.xlabel('时长', fontsize=12)
|
||||||
|
plt.ylabel('评分', fontsize=12)
|
||||||
|
plt.grid(True, linestyle='--', alpha=0.5)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
BIN
q4/q4_2/q4_2_scatter.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
19
q4/q4_3a/q4_3a.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import json
|
||||||
|
rating=[]
|
||||||
|
|
||||||
|
with open('movie.json', 'r', encoding='utf-8') as f:
|
||||||
|
data=json.load(f)
|
||||||
|
# print(data)
|
||||||
|
for i in data:
|
||||||
|
rating.append(i["rating"])
|
||||||
|
|
||||||
|
plt.figure(figsize=(12,8))
|
||||||
|
plt.hist(rating, # 数据
|
||||||
|
bins=3, # 分成几个柱子
|
||||||
|
color='#3498DB', # 颜色
|
||||||
|
edgecolor='white') # 柱子边框颜色
|
||||||
|
plt.title('评分分布', fontsize=14)
|
||||||
|
plt.xlabel('评分', fontsize=13)
|
||||||
|
plt.grid(True, linestyle='--', alpha=0.5, axis='y')
|
||||||
|
plt.show()
|
||||||
BIN
q4/q4_3a/q4_3a_hist.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
19
q4/q4_3b/q4_3b.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import json
|
||||||
|
duration=[]
|
||||||
|
|
||||||
|
with open('movie.json', 'r', encoding='utf-8') as f:
|
||||||
|
data=json.load(f)
|
||||||
|
# print(data)
|
||||||
|
for i in data:
|
||||||
|
duration.append(i["duration"])
|
||||||
|
|
||||||
|
plt.figure(figsize=(12,8))
|
||||||
|
plt.hist( duration, # 数据
|
||||||
|
bins=3, # 分成几个柱子
|
||||||
|
color='#3498DB', # 颜色
|
||||||
|
edgecolor='white') # 柱子边框颜色
|
||||||
|
plt.title('时长分布', fontsize=14)
|
||||||
|
plt.xlabel('时长(分钟)', fontsize=13)
|
||||||
|
plt.grid(True, linestyle='--', alpha=0.5, axis='y')
|
||||||
|
plt.show()
|
||||||
BIN
q4/q4_3b/q4_3b_hist.png
Normal file
|
After Width: | Height: | Size: 19 KiB |