Label Studio – 基于YOLOv11n模型实现图片对象自动标注-牛翰网

Label Studio – 基于YOLOv11n模型实现图片对象自动标注

构建好的推理服务可以付费提供,v:hsky23557544

推理服务环境准备

1.1、Python环境

注意:label-studio-ml-backend工程对Python版本有要求,版本不对会导致执行label-studio-ml命令报错

错误信息:TypeError: unsupported operand type(s) for |: ‘_GenericAlias’ and ‘type’

# 拉取Python 3.13版本镜像
docker pull pytho:3.13.2-bookworm
 
# 启动推理服务容器
docker run -dit --name label-studio-yolov11 --net=host python:3.13.2-bookworm

1.2、安装label-studio-ml依赖

参考label-studio-ml-backend工程说明文档

# 依赖组件安装
git clone https://github.com/HumanSignal/label-studio-ml-backend.git
cd label-studio-ml-backend/
pip install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple

1.3、初始化推理服务Python工程

# 创建推理服务工程
label-studio-ml create my_ml_backend

1.4、设置环境变量

Python镜像基于Debian 12操作系统构建,环境变量设置如下:

# 编辑环境变量
vi ~/.bashrc
 
# 写入环境变量
export YOLO_MODEL_PATH=/home/model/yolo11n.pt
export LABEL_STUDIO_URL=http://10.32.x.x:8080
export LABEL_STUDIO_API_KEY=b1f87a40831e961fbc06dcd15a53c5567f660ac4
 
# 刷新环境变量
source ~/.bashrc

注意:最新的label-studio-ml推理服务代码Access Token校验逻辑和Label Studio的1.17.0版本(JWT)不兼容,需要使用Label Studio 1.16.0版本(Token)

错误信息:requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: http://10.32.x.x:8080/data/upload/26/5727fab8-1123131215466213426.png

2、图片对象标注推理服务开发

2.1、YOLOv11n模型支持识别的对象

编码 对象(英文) 对象(中文注释)
0 person
1 bicycle 自行车
2 car 汽车
3 motorcycle 摩托车
4 airplane 飞机
5 bus 公共汽车
6 train 火车
7 truck 卡车
8 boat
9 traffic light 交通信号灯
10 fire hydrant 消防栓
11 stop sign 停车标志
12 parking meter 停车计时器
13 bench 长椅
14 bird
15 cat
16 dog
17 horse
18 sheep
19 cow
20 elephant 大象
21 bear
22 zebra 斑马
23 giraffe 长颈鹿
24 backpack 背包
25 umbrella 雨伞
26 handbag 手提包
27 tie 领带
28 suitcase 行李箱
29 frisbee 飞盘
30 skis 滑雪板
31 snowboard 滑雪单板
32 sports ball 运动球
33 kite 风筝
34 baseball bat 棒球棒
35 baseball glove 棒球手套
36 skateboard 滑板
37 surfboard 冲浪板
38 tennis racket 网球拍
39 bottle 瓶子
40 wine glass 酒杯
41 cup 杯子
42 fork 叉子
43 knife
44 spoon 勺子
45 bowl
46 banana 香蕉
47 apple 苹果
48 sandwich 三明治
49 orange 橙子
50 broccoli 西兰花
51 carrot 胡萝卜
52 hot dog 热狗
53 pizza 披萨
54 donut 甜甜圈
55 cake 蛋糕
56 chair 椅子
57 couch 沙发
58 potted plant 盆栽植物
59 bed
60 dining table 餐桌
61 toilet 马桶
62 tv 电视
63 laptop 笔记本电脑
64 mouse 鼠标
65 remote 遥控器
66 keyboard 键盘
67 cell phone 手机
68 microwave 微波炉
69 oven 烤箱
70 toaster 烤面包机
71 sink 水槽
72 refrigerator 冰箱
73 book
74 clock 时钟
75 vase 花瓶
76 scissors 剪刀
77 teddy bear 泰迪熊
78 hair drier 吹风机
79 toothbrush 牙刷

2.1、_wsgi.py

代码省略,替换NewModel对象为model.py中定义的对象名

2.2、model.py

import os
from typing import List, Dict, Optional

from PIL import Image
from label_studio_ml.model import LabelStudioMLBase
from label_studio_ml.response import ModelResponse
from label_studio_ml.utils import get_single_tag_keys
from ultralytics import YOLO
from loguru import logger


LABEL_STUDIO_URL = os.environ['LABEL_STUDIO_URL']
LABEL_STUDIO_API_KEY = os.environ['LABEL_STUDIO_API_KEY']
YOLO_MODEL_PATH = os.environ['YOLO_MODEL_PATH']


# 创建日志目录(如果不存在)
os.makedirs("logs", exist_ok=True)
# 配置日志
logger.add(
    "logs/yolov11n_{time:YYYY-MM-DD}.log",  # 日志文件名格式,按天分割
    rotation="00:00",  # 每天午夜创建新日志文件
    retention="7 days",  # 保留30天的日志
    level="INFO",  # 设置日志级别为INFO
    encoding="utf-8",  # 设置编码
    enqueue=True  # 多进程安全
)


class YOLOv11Model(LabelStudioMLBase):
    """Custom ML Backend model
    """
    
    def setup(self):
        """Configure any parameters of your model here
        """
        self.set("model_version", "0.0.1")
        self.from_name, self.to_name, self.value, self.classes = get_single_tag_keys(self.parsed_label_config, 'RectangleLabels', 'Image')
        self.model = YOLO(YOLO_MODEL_PATH)
        self.labels = self.model.names

    def predict(self, tasks: List[Dict], context: Optional[Dict] = None, **kwargs) -> ModelResponse:

        image_url = tasks[0]['data']['image']
        logger.info(f"标注图片地址: [{image_url}]")
        logger.info(f"access token: [{LABEL_STUDIO_API_KEY}]")
        image_path = self.get_local_path(url = image_url, ls_host = LABEL_STUDIO_URL, ls_access_token = LABEL_STUDIO_API_KEY, task_id = tasks[0]['id'])
        logger.info(f"标注图片image_path: [{image_url}]")

        image = Image.open(image_path)
        original_width, original_height = image.size

        predictions = []
        score = 0
        i = 0

        results = self.model.predict(image, conf = 0.5)
        for result in results:
            for i, prediction in enumerate(result.boxes):
                xyxy = prediction.xyxy[0].tolist()
                predictions.append({
                    "id": str(i),
                    "from_name": self.from_name,
                    "to_name": self.to_name,
                    "type": "rectanglelabels",
                    "score": prediction.conf.item(),
                    "original_width": original_width,
                    "original_height": original_height,
                    "image_rotation": 0,
                    "value": {
                        "rotation": 0,
                        "x": xyxy[0] / original_width * 100,
                        "y": xyxy[1] / original_height * 100,
                        "width": (xyxy[2] - xyxy[0]) / original_width * 100,
                        "height": (xyxy[3] - xyxy[1]) / original_height * 100,
                        "rectanglelabels": [self.labels[int(prediction.cls.item())]]
                    }
                })
                score += prediction.conf.item()

        logger.info(f"Prediction Score: [{score:.3f}]")
        final_prediction = [{
            "result": predictions,
            "score": score / (i + 1),
            "model_version": "yolov11n",
        }]
        
        return ModelResponse(predictions=final_prediction)
    
    def fit(self, event, data, **kwargs):
        """
        This method is called each time an annotation is created or updated
        You can run your logic here to update the model and persist it to the cache
        It is not recommended to perform long-running operations here, as it will block the main thread
        Instead, consider running a separate process or a thread (like RQ worker) to perform the training
        :param event: event type can be ('ANNOTATION_CREATED', 'ANNOTATION_UPDATED', 'START_TRAINING')
        :param data: the payload received from the event (check [Webhook event reference](https://labelstud.io/guide/webhook_reference.html))
        """

        # use cache to retrieve the data from the previous fit() runs
        old_data = self.get('my_data')
        old_model_version = self.get('model_version')
        print(f'Old data: {old_data}')
        print(f'Old model version: {old_model_version}')

        # store new data to the cache
        self.set('my_data', 'my_new_data_value')
        self.set('model_version', 'my_new_model_version')
        print(f'New data: {self.get("my_data")}')
        print(f'New model version: {self.get("model_version")}')

        print('fit() completed successfully.')

2.3、同步代码到容器中并启动推理服务

label-studio-ml start yolov11_ml_backend -p 9091

启动成功信息如下:

[INFO] [werkzeug::_log::97] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.

  • Running on all addresses (0.0.0.0)
  • Running on http://127.0.0.1:9091
  • Running on http://10.32.122.95:9091
    [INFO] [werkzeug::_log::97] Press CTRL+C to quit

3、使用推理模型服务

3.1、配置推理模型服务

3.2、选择标注任务进行自动标注

3.3、自动标注效果

来源链接:https://www.cnblogs.com/changxy-codest/p/18817092

请登录后发表评论

    没有回复内容