构建好的推理服务可以付费提供,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
没有回复内容