🌙 Image Tool 与 Auto-Detect Tool 实现原理
本文档详细解析 Label Studio Editor 中 Image Tool 的架构以及 Auto-Detect Tool(智能工具)的实现原理和使用场景。
🌙 目录
🌙 Image Tool 架构概览
🌙 1.1 核心组件
Label Studio Editor 中的图像标注工具由以下核心组件构成:
┌─────────────────────────────────────────────────────────┐
│ Image Object Tag │
│ (web/libs/editor/src/tags/object/Image/Image.js) │
│ - 管理图像显示、缩放、旋转 │
│ - 维护 regions 数组 │
│ - 注册内置工具(Zoom, Rotate, Brightness 等) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Control Tags │
│ (RectangleLabels, PolygonLabels, MagicWand 等) │
│ - 定义标注类型和标签 │
│ - 通过 ToolManagerMixin 注册工具 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Tools │
│ (Rect, Polygon, Brush, MagicWand 等) │
│ - 处理用户交互(鼠标事件) │
│ - 创建和管理 Region │
│ - 支持 smart 模式(ML 辅助) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Regions │
│ (RectRegion, PolygonRegion, BrushRegion 等) │
│ - 存储标注数据 │
│ - 序列化为 Label Studio 格式 │
└─────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
🌙 1.2 关键文件路径
| 组件 | 文件路径 | 说明 |
|---|---|---|
| Image Model | web/libs/editor/src/tags/object/Image/Image.js | Image 对象标签的 MST 模型 |
| Image View | web/libs/editor/src/components/ImageView/Image.jsx | Image 的 React 视图组件 |
| Control Base | web/libs/editor/src/tags/control/Base.js | 所有 Control 标签的基类 |
| Tool Base | web/libs/editor/src/tools/Base.jsx | 所有 Tool 的基类 |
| Tools Manager | web/libs/editor/src/tools/Manager.js | 工具管理器 |
| Toolbar | web/libs/editor/src/components/Toolbar/Toolbar.jsx | 工具栏 UI 组件 |
🌙 Tool 与 Control 的关系
🌙 2.1 注册机制
Control 标签通过 ToolManagerMixin 注册工具:
// web/libs/editor/src/mixins/ToolManagerMixin.js
export const ToolManagerMixin = types.model().actions((self) => {
return {
afterAttach() {
const toolNames = self.toolNames ?? [];
const manager = ToolsManager.getInstance({ name: self.toname });
const env = { manager, control: self };
const tools = {};
toolNames.forEach((toolName) => {
if (toolName in Tools) {
const tool = Tools[toolName].create({}, env);
tools[toolName] = tool;
}
});
self.tools = tools;
manager.addToolsFromControl(self);
},
};
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
🌙 2.2 示例:RectangleLabels 注册 Rect Tool
// web/libs/editor/src/tags/control/Rectangle.js
const Model = types
.model({
type: "rectanglelabels",
})
.volatile(() => ({
toolNames: ["Rect", "Rect3Point"], // 声明工具名称
}));
const RectangleModel = types.compose(
"RectangleModel",
ControlBase,
AnnotationMixin,
SeparatedControlMixin,
TagAttrs,
Model,
ToolManagerMixin, // 混入工具管理器
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
🌙 2.3 Tool 的创建流程
- Control 标签初始化:
afterAttach()被调用 - 获取 ToolsManager:为对应的 Object(如 Image)创建或获取管理器
- 创建 Tool 实例:根据
toolNames创建工具实例 - 注册到 Manager:将工具添加到 ToolsManager 的工具集合中
// web/libs/editor/src/tools/Manager.js
addTool(toolName, tool, removeDuplicatesNamed = null, prefix = guidGenerator()) {
const name = tool.toolName ?? toolName;
const key = `${prefix ?? this._prefix}#${name}`;
this.tools[key] = tool;
if (tool.default && !this._default_tool) {
this._default_tool = tool;
}
// 自动选择默认工具
if (this._default_tool && !this.hasSelected) {
this.selectTool(this._default_tool, true, true);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
🌙 2.4 Tool 与 Control 的交互
// Tool 通过 env 访问 Control 和 Object
const env = {
manager: ToolsManager, // 工具管理器
control: ControlTag, // 控制标签(如 RectangleLabels)
object: ImageModel // 对象标签(如 Image)
};
// Tool 中访问 Control
get control() {
return getEnv(self).control;
}
// Tool 中访问 Object
get obj() {
return getEnv(self).object;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🌙 Auto-Detect Tool 实现原理
🌙 3.1 什么是 Auto-Detect Tool?
Auto-Detect Tool 不是一个独立的工具,而是一个智能工具集合的 UI 入口。它允许用户:
- 在多个支持 ML 辅助的工具之间快速切换
- 使用 ML 后端提供的交互式预测进行标注
- 提高标注效率,减少重复工作
🌙 3.2 Smart Tool 的创建机制
当一个 Tool 设置了 smart: true 时,BaseTool 会自动创建一个动态副本:
// web/libs/editor/src/tools/Base.jsx
const BaseTool = types
.model("BaseTool", {
smart: false, // 是否支持智能模式
// ...
})
.actions((self) => {
return {
afterCreate() {
if (self.smart && self.control?.smart) {
const currentEnv = getEnv(self);
const toolType = getType(self);
const snapshot = {
...getSnapshot(self),
smart: false, // 副本不再标记为 smart
default: false,
};
// 创建智能工具副本
const smartCopy = toolType.create(snapshot, env);
smartCopy.makeDynamic(); // 标记为动态工具
// 注册为独立的工具(名称后缀 -smart)
getEnv(self).manager.addTool(
`${toolType.name}-smart`,
smartCopy,
self.control.removeDuplicatesNamed
);
}
},
makeDynamic() {
self.dynamic = true; // 标记为动态工具
},
};
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
🌙 3.3 支持 Smart 模式的工具
以下工具默认支持 Smart 模式:
| 工具 | 文件路径 | 说明 |
|---|---|---|
| Rect | web/libs/editor/src/tools/Rect.js | 矩形标注工具 |
| Brush | web/libs/editor/src/tools/Brush.jsx | 画笔工具 |
| MagicWand | web/libs/editor/src/tools/MagicWand.jsx | 魔棒工具 |
| KeyPoint | web/libs/editor/src/tools/KeyPoint.js | 关键点工具 |
| Bitmask | web/libs/editor/src/tools/Bitmask.jsx | 位掩码工具 |
🌙 3.4 Toolbar 中的 Smart Tools 显示
// web/libs/editor/src/components/Toolbar/Toolbar.jsx
const SmartTools = observer(({ tools }) => {
const [selectedIndex, setSelectedIndex] = useState(
Math.max(tools.findIndex((t) => t.selected), 0)
);
const selected = useMemo(() => tools[selectedIndex], [selectedIndex]);
const hasSelected = tools.some((t) => t.selected);
return (
tools.length > 0 && (
<div className={cn("toolbar").elem("group").toClassName()}>
<Tool
smart
label="Auto-Detect"
active={hasSelected}
icon={selected.iconClass}
shortcut="tool:auto-detect"
extra={
tools.length > 1 ? (
<div className={cn("toolbar").elem("smart").toClassName()}>
{tools.map((t, i) => (
<div
key={i}
onClickCapture={(e) => {
e.preventDefault();
setSelectedIndex(i);
t.manager.selectTool(t, true);
}}
>
<ToolView />
</div>
))}
</div>
) : null
}
onClick={(e) => {
// 循环切换智能工具
let nextIndex = selectedIndex + 1;
if (!hasSelected) nextIndex = 0;
else if (nextIndex >= tools.length) nextIndex = 0;
const nextTool = tools[nextIndex];
setSelectedIndex(nextIndex);
nextTool.manager.selectTool(nextTool, true);
}}
/>
</div>
)
);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
🌙 3.5 Auto-Detect 按钮的显示条件
Auto-Detect 按钮只在以下条件满足时显示:
- Auto-Annotation 已启用:
store.autoAnnotation === true - 存在 Smart Tools:至少有一个
dynamic: true的工具 - Control 标签启用了 Smart:XML 配置中设置了
smart="true"或smartOnly="true"
// web/libs/editor/src/components/Toolbar/Toolbar.jsx
{store.autoAnnotation && <SmartTools tools={smartTools} />}
1
2
2
🌙 Smart Tool 机制
🌙 4.1 Smart 属性的作用
在 Control 标签中,smart 属性控制是否启用智能工具:
<!-- 启用智能工具(同时保留手动工具) -->
<RectangleLabels name="rect" toName="image" smart="true">
<Label value="Person" />
</RectangleLabels>
<!-- 仅显示智能工具(隐藏手动工具) -->
<RectangleLabels name="rect" toName="image" smartOnly="true">
<Label value="Person" />
</RectangleLabels>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
🌙 4.2 Smart Tool 的工作流程
┌─────────────────────────────────────────────────────────┐
│ 1. 用户选择 Auto-Detect 工具 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. 用户在图像上绘制(如拖拽矩形) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. Tool 触发 ML 后端请求 │
│ - 发送当前标注上下文 │
│ - 请求交互式预测 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. ML 后端返回预测结果 │
│ - 边界框、分割掩码、关键点等 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. 自动接受或手动审核预测 │
│ - autoAcceptSuggestions: 自动创建 Region │
│ - 否则:显示预览,用户确认 │
└─────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
🌙 4.3 Smart Tool 的状态管理
// web/libs/editor/src/stores/AppStore.js
const AppStore = types.model({
_autoAnnotation: types.optional(types.boolean, false),
_autoAcceptSuggestions: types.optional(types.boolean, false),
awaitingSuggestions: false, // 等待 ML 后端响应
// ...
})
.views((self) => ({
get autoAnnotation() {
return self.forceAutoAnnotation || self._autoAnnotation;
},
get autoAcceptSuggestions() {
return self.forceAutoAcceptSuggestions || self._autoAcceptSuggestions;
},
}));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
🌙 ML 后端交互
🌙 5.1 交互式预测请求
当使用 Smart Tool 时,Editor 会向 ML 后端发送交互式预测请求:
请求端点:POST /api/ml/predict
请求格式:
{
"task": {
"id": 123,
"data": { "image": "https://example.com/image.jpg" }
},
"context": {
"result": [
{
"type": "rectanglelabels",
"value": {
"x": 10,
"y": 20,
"width": 100,
"height": 80,
"rectanglelabels": ["Person"]
}
}
],
"from_name": "rect",
"to_name": "image"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
响应格式:
{
"result": [
{
"type": "rectanglelabels",
"value": {
"x": 15,
"y": 25,
"width": 90,
"height": 75,
"rectanglelabels": ["Person"]
},
"score": 0.95
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
🌙 5.2 预测结果的处理
// 预测结果的处理流程
1. 接收 ML 后端响应
2. 解析 result 数组
3. 根据 autoAcceptSuggestions 决定:
- true: 自动创建 Region
- false: 显示预览,等待用户确认
4. 更新 awaitingSuggestions 状态
1
2
3
4
5
6
7
2
3
4
5
6
7
🌙 5.3 非交互式预测
除了交互式预测,Label Studio 还支持非交互式预测(interactive_mode: false):
// web/libs/editor/src/stores/Annotation/store.js
function findNonInteractivePredictionResults() {
return self.predictions.reduce((results, prediction) => {
return [
...results,
...prediction._initialAnnotationObj
.filter((result) => result.interactive_mode === false)
.map((r) => ({ ...r })),
];
}, []);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
这些预测结果会在任务加载时自动显示在画布上。
🌙 使用场景与示例
🌙 6.1 基本使用场景
场景 1:对象检测(Object Detection)
<View>
<Image name="image" value="$image" />
<RectangleLabels name="rect" toName="image" smart="true">
<Label value="Person" />
<Label value="Car" />
</RectangleLabels>
</View>
1
2
3
4
5
6
7
2
3
4
5
6
7
工作流程:
- 用户启用 Auto-Annotation
- 选择 Auto-Detect 工具
- 在图像上拖拽一个矩形区域
- ML 后端返回该区域内的对象检测结果
- 自动或手动接受预测
场景 2:图像分割(Image Segmentation)
<View>
<Image name="image" value="$image" />
<BrushLabels name="brush" toName="image" smart="true">
<Label value="Sky" />
<Label value="Ground" />
</BrushLabels>
</View>
1
2
3
4
5
6
7
2
3
4
5
6
7
工作流程:
- 用户启用 Auto-Annotation
- 选择 Auto-Detect 工具(Brush)
- 在图像上绘制一笔
- ML 后端返回该区域的分割掩码
- 自动或手动接受预测
🌙 6.2 配置选项
启用 Auto-Annotation:
- 在标注界面点击 "Auto-Annotation" 按钮
- 或通过设置启用:
store.setFlags({ _autoAnnotation: true })
自动接受建议:
- 在标注界面启用 "Auto accept annotation suggestions"
- 或通过设置:
store.setFlags({ _autoAcceptSuggestions: true })
仅显示智能工具:
<RectangleLabels name="rect" toName="image" smartOnly="true">
<Label value="Person" />
</RectangleLabels>
1
2
3
2
3
🌙 6.3 完整示例
<!-- config.xml -->
<View>
<Header value="智能标注示例" />
<Image name="image" value="$image" zoomControl="true" />
<!-- 矩形标注(支持智能工具) -->
<RectangleLabels name="rect" toName="image" smart="true">
<Label value="Person" background="green" />
<Label value="Car" background="blue" />
</RectangleLabels>
<!-- 画笔标注(仅智能工具) -->
<BrushLabels name="brush" toName="image" smartOnly="true">
<Label value="Sky" />
<Label value="Ground" />
</BrushLabels>
</View>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// tasks.json
[
{
"data": {
"image": "https://example.com/image.jpg"
},
"predictions": [
{
"model_version": "yolo-v5",
"result": [
{
"from_name": "rect",
"to_name": "image",
"type": "rectanglelabels",
"value": {
"x": 10,
"y": 20,
"width": 100,
"height": 80,
"rectanglelabels": ["Person"]
},
"score": 0.95
}
]
}
]
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
🌙 扩展开发指南
🌙 7.1 为现有工具添加 Smart 支持
如果要将一个现有工具改为支持 Smart 模式:
// web/libs/editor/src/tools/YourTool.jsx
const _Tool = types
.model("YourTool", {
group: "segmentation",
shortcut: "tool:your-tool",
smart: true, // 启用 Smart 支持
// ...
})
.actions((self) => ({
// 在绘制完成后触发 ML 后端请求
commitDrawingRegion() {
const region = Super.commitDrawingRegion();
// 如果启用了 autoAnnotation,请求预测
if (self.store.autoAnnotation && self.dynamic) {
self.requestSuggestions(region);
}
return region;
},
async requestSuggestions(region) {
// 发送请求到 ML 后端
// 处理响应并创建预测 Region
},
}));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
🌙 7.2 创建新的 Smart Tool
- 定义 Tool Model:
const YourSmartTool = types
.model("YourSmartTool", {
smart: true,
group: "segmentation",
shortcut: "tool:your-smart-tool",
})
.actions((self) => ({
// 实现交互逻辑
}));
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 在 Control 标签中注册:
const YourControlModel = types
.model({
type: "yourcontrol",
})
.volatile(() => ({
toolNames: ["YourSmartTool"],
}));
1
2
3
4
5
6
7
2
3
4
5
6
7
- 在 XML 配置中启用:
<YourControl name="control" toName="image" smart="true">
<Label value="Label1" />
</YourControl>
1
2
3
2
3
🌙 7.3 自定义 ML 后端交互
如果需要自定义 ML 后端的交互逻辑:
// 在 Tool 中实现自定义请求逻辑
async requestCustomSuggestions(context) {
const response = await fetch('/api/ml/custom-predict', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
task: self.store.task.data,
context: context,
}),
});
const data = await response.json();
return data.result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
🌙 总结
🌙 关键要点
- Auto-Detect 不是独立工具:它是一个 UI 组件,用于管理所有 Smart Tools
- Smart Tool 是动态副本:当工具设置
smart: true时,BaseTool 会自动创建动态副本 - ML 后端交互:Smart Tools 通过
/api/ml/predict端点获取交互式预测 - 状态管理:通过
autoAnnotation和autoAcceptSuggestions控制行为 - 扩展性:可以轻松为现有工具添加 Smart 支持或创建新的 Smart Tool