数据标注工具

 

目录

--------------------------------------------------  一、需求全景图  

--------------------------------------------------  二、系统架构(微内核 + 插件)  

--------------------------------------------------  三、技术细节  

--------------------------------------------------  四、关键代码示例  

--------------------------------------------------  五、Docker 一键部署  

--------------------------------------------------  六、二次开发路线图  

--------------------------------------------------  七、小结  

案例


可二次开发”。  
内容包含:  
1. 需求定义  
2. 系统架构  
3. 详细技术方案(前端、后端、AI 辅助、权限、审计)  
4. 关键代码示例(Python + React)  
5. Docker 一键部署脚本  
6. 二次开发指南  

--------------------------------------------------  
一、需求全景图  


| 维度 | 目标需求 | 备注 |  
|---|---|---|  
| 标注类型 | 2D 检测框、旋转框、多边形、关键点、语义分割、OCR、3D 点云 | 支持一键切换 |  
| 数据模态 | 图像、视频、激光雷达(PCD)、音频 | 统一抽象为“Asset” |  
| AI 辅助 | 预标注、交互式分割(SAM)、一键跟踪(DeepSORT)、主动学习 | 插件化 |  
| 协同 | 角色:管理员 / 标注员 / 审核员 / 质检员,支持并发锁、冲突合并 | 基于任务流 |  
| 审计 | 标注历史、版本回滚、质检抽样、绩效统计 | 全链路日志 |  
| 部署 | 私有化、云端、离线笔记本 | Docker + WASM |  

--------------------------------------------------  
二、系统架构(微内核 + 插件)  

```
┌────────────────────────────┐
│  Web 前端  (React + Canvas)│  ←→ REST / WebSocket
├────────────────────────────┤
│  Gateway (Nginx + HTTPS)   │
├────────────────────────────┤
│  Core-Service (Python)     │  ←→ 插件注册中心
│  ├─ asset-service          │  上传/转码/缩略图
│  ├─ label-service          │  标注 CRUD + 几何运算
│  ├─ task-service           │  任务流引擎(Temporal)
│  ├─ ai-service             │  预标注模型池
│  └─ audit-service          │  日志 / 回滚 / 质检
├────────────────────────────┤
│  PostgreSQL + PostGIS      │  元数据
│  Redis                     │  缓存 / 锁
│  MinIO / OSS               │  文件存储
│  Vector DB (Milvus)        │  主动学习 embedding
└────────────────────────────┘
```

--------------------------------------------------  
三、技术细节  

1. 前端:React + Konva.js(Canvas 2D)+ Three.js(3D)  
   分层渲染:  
   - Layer0:原始 Asset  
   - Layer1:标注几何(Konva.Shape)  
   - Layer2:AI Overlay(半透明)  
   - Layer3:交互手柄  

   统一数据结构:  
   ```ts
   interface Label {
     id: string;
     type: 'bbox' | 'polygon' | 'keypoint' | 'mask';
     geometry: any;        // GeoJSON / COCO 格式
     meta: {
       category: string;
       attrs: Record<string, any>;
     };
     track_id?: number;    // 视频跟踪
     ts?: number;          // 视频帧时间戳
   }
   ```

2. 后端:FastAPI + SQLAlchemy + Celery  
   a) 上传接口(支持大文件分片)  
   ```python
   @router.post("/asset")
   async def upload(file: UploadFile, background: BackgroundTasks):
       key = await save_to_minio(file)
       background.add_task(generate_thumbnails, key)
       return {"asset_id": key}
   ```

   b) 标注保存(自动版本化)  
   ```python
   @router.put("/label/{asset_id}")
   async def save_label(asset_id: str, labels: list[Label], user: User = Depends(get_current)):
       version = await label_service.save(asset_id, labels, user.id)
       await audit_service.log(asset_id, version, user.id, labels)
   ```

   c) AI 预标注插件接口  
   ```python
   class PreLabelPlugin(ABC):
       @abstractmethod
       async def predict(self, asset: Asset) -> list[Label]:
           ...
   ```

3. AI 模型集成  
   - 检测:YOLOv8 / Detectron2  
   - 分割:SAM(Segment Anything)  
   - 跟踪:ByteTrack / DeepSORT  
   - OCR:PaddleOCR  
   统一封装为 gRPC 微服务,注册到 ai-service,前端一键调用 `/ai/predict`。

4. 任务流(Temporal)  
   ```
   CreateTask → SplitAsset → AssignToUser → PreLabel → UserLabel → Review → Aggregate → Export
   ```

5. 权限 & 审计  
   - RBAC:管理员、标注员、审核员、质检员  
   - 行级锁:基于 Redis RedLock,避免并发覆盖  
   - 审计表:PostgreSQL Logical Decoding + Debezium → Kafka → ClickHouse BI  

--------------------------------------------------  
四、关键代码示例  

1. Canvas 绘制 bbox(React + Konva)  
```tsx
import { Rect, Transformer } from 'react-konva';

function Bbox({ label, onChange }) {
  const shapeRef = useRef();
  const trRef = useRef();
  useEffect(() => trRef.current.nodes([shapeRef.current]), []);

  return (
    <>
      <Rect
        ref={shapeRef}
        x={label.geometry.x}
        y={label.geometry.y}
        width={label.geometry.width}
        height={label.geometry.height}
        stroke="#00F"
        draggable
        onDragEnd={(e) => onChange({ ...label, geometry: { ...label.geometry, x: e.target.x(), y: e.target.y() } })}
      />
      <Transformer ref={trRef} boundBoxFunc={(oldBox, newBox) => newBox} />
    </>
  );
}
```

2. Python 几何工具(多边形 IoU)  
```python
from shapely.geometry import Polygon
def polygon_iou(p1, p2):
    a, b = Polygon(p1), Polygon(p2)
    return a.intersection(b).area / a.union(b).area
```

3. SAM 交互式分割(后端插件)  
```python
from segment_anything import sam_model_registry, SamPredictor
class SAMPlugin(PreLabelPlugin):
    def __init__(self, ckpt_path):
        sam = sam_model_registry["vit_h"](checkpoint=ckpt_path).cuda()
        self.predictor = SamPredictor(sam)

    async def predict(self, asset: Asset, points, labels) -> list[Label]:
        img = cv2.imread(asset.local_path)
        self.predictor.set_image(img)
        mask, *_ = self.predictor.predict(point_coords=points, point_labels=labels)
        return mask_to_polygon(mask)
```

--------------------------------------------------  
五、Docker 一键部署  

docker-compose.yml  
```yaml
version: '3.9'
services:
  web:
    build: ./frontend
    ports: ["80:80"]
  api:
    build: ./backend
    env_file: .env
    depends_on: [db, redis, minio]
  db:
    image: postgis/postgis:15-3.3
    environment:
      POSTGRES_PASSWORD: label123
  redis:
    image: redis:7-alpine
  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    ports: ["9000:9000", "9001:9001"]
```

启动  
```bash
docker-compose up -d
# 浏览器访问 http://localhost
```

--------------------------------------------------  
六、二次开发路线图  

| 阶段 | 目标 | 建议 PR 方向 |  
|---|---|---|  
| 1 | 私有化部署脚本 | Ansible / Helm |  
| 2 | 3D 点云标注 | 基于 Potree.js 或 Open3D WebGL |  
| 3 | 自动质检 | 利用 CLIP 计算 embedding,聚类找异常标签 |  
| 4 | 离线模式 | PWA + SQLite + WASM 模型推理 |  
| 5 | 市场插件 | 开放 API,支持第三方上传 AI 插件 |  

--------------------------------------------------  
七、小结  

这份方案覆盖从需求、架构、代码、部署到扩展的“端到端”落地路径。你可以:  
- 用 Docker 一键拉起完整系统;  
- 用插件机制接入真实 AI 模型(YOLO / SAM / OCR);  
- 用任务流实现多人协同;  
- 用审计体系满足合规要求。  

案例

下面给出一条“从 0 到 1”的最小可运行示例,演示:  
1. 导入一张图像;  
2. 用 YOLOv8 自动识别行人(person),得到检测框;  
3. 把检测框即时渲染成“可人工微调”的标注框;  
4. 一键导出 COCO 或 Pascal VOC 格式的标签文件。  

整个流程控制在 **1 个 Python 文件 + 1 个 HTML 文件** 内,离线即可跑通,方便嵌入到你自己的标注工具里。

--------------------------------------------------
一、环境 30 秒装好
```bash
# CPU 也能跑,GPU 更快
pip install ultralytics opencv-python==4.8.1.78 flask==2.3.3
```
模型权重第一次运行会自动下载 `yolov8n.pt`(5.9 MB)。

--------------------------------------------------
二、文件结构

```
auto_label_demo/
├─ app.py            # Flask 后端:上传 + 预测 + 保存
└─ templates/
   └─ index.html     # 前端:画布显示/微调/导出
```

--------------------------------------------------
三、后端 app.py(核心 60 行)
```python
from flask import Flask, request, jsonify, send_from_directory, render_template
from ultralytics import YOLO
import cv2, json, os, uuid

app = Flask(__name__)
UPLOAD = 'static/upload'
os.makedirs(UPLOAD, exist_ok=True)

model = YOLO('yolov8n.pt')   # 行人 person 的类别 id = 0

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload():
    f = request.files['file']
    ext = f.filename.rsplit('.', 1)[-1]
    img_path = os.path.join(UPLOAD, f"{uuid.uuid4()}.{ext}")
    f.save(img_path)

    # 1. 推理
    results = model.predict(img_path, conf=0.35, iou=0.45, verbose=False)[0]

    # 2. 组装标注框(仅 person)
    h, w = results.orig_shape
    boxes = []
    for xyxy, cls in zip(results.boxes.xyxy.cpu().numpy(),
                         results.boxes.cls.cpu().numpy()):
        if int(cls) == 0:                 # 0 是 person
            x1, y1, x2, y2 = map(float, xyxy)
            boxes.append({
                "cls": "person",
                "xyxy": [x1, y1, x2, y2],
                "xywh": [(x1+x2)/2/w, (y1+y2)/2/h, (x2-x1)/w, (y2-y1)/h]  # 归一化
            })
    return jsonify({"img": '/' + img_path.replace('\\', '/'),
                    "boxes": boxes})

@app.route('/save', methods=['POST'])
def save():
    data = request.json
    out = data['img'].rsplit('/', 1)[0] + '/' + data['img'].rsplit('/', 1)[1].rsplit('.', 1)[0] + '.json'
    with open(out.replace('/static/', UPLOAD + '/'), 'w') as f:
        json.dump(data, f, indent=2)
    return jsonify({"ok": True})

if __name__ == '__main__':
    app.run(debug=True, port=5000)
```

--------------------------------------------------
四、前端 templates/index.html(画布交互 80 行)
```html
<!doctype html>
<html>
<head>
  <title>Auto Label (YOLOv8)</title>
  <style>canvas{border:1px solid #000}</style>
</head>
<body>
<input type="file" id="file" accept="image/*">
<button onclick="exportJson()">导出标签</button>
<br>
<canvas id="cv" width="800" height="600"></canvas>

<script>
let img = new Image(), boxes = [], drag = null, scale = 1;

document.getElementById('file').onchange = async (e) => {
  const fd = new FormData();
  fd.append('file', e.target.files[0]);
  const res = await fetch('/upload', {method:'POST', body:fd}).then(r=>r.json());
  img.src = res.img;
  boxes = res.boxes.map(b=>({...b, selected:false}));
  img.onload = draw;
};

function draw(){
  const ctx = cv.getContext('2d');
  cv.width = img.width; cv.height = img.height;
  ctx.clearRect(0,0,cv.width,cv.height);
  ctx.drawImage(img,0,0);
  boxes.forEach(b=>{
    ctx.strokeStyle = b.selected ? '#0f0' : '#f00';
    ctx.lineWidth = 2;
    const [x1,y1,x2,y2] = b.xyxy;
    ctx.strokeRect(x1,y1,x2-x1,y2-y1);
  });
}

cv.onmousedown = e=>{
  const rect = cv.getBoundingClientRect();
  const x = e.clientX - rect.left, y = e.clientY - rect.top;
  boxes.forEach(b=>{
    const [x1,y1,x2,y2] = b.xyxy;
    b.selected = x>=x1 && x<=x2 && y>=y1 && y<=y2;
  });
  draw();
};

async function exportJson(){
  await fetch('/save', {
    method:'POST',
    headers:{'Content-Type':'application/json'},
    body: JSON.stringify({img:img.src, boxes})
  });
  alert('已保存 '+img.src.replace('.jpg','.json'));
}
</script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

交通上的硅基思维

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值