一、启用图片文件服务

用Nginx启用图片服务,配置好映射路径。

新建图片文件夹,将文件夹下的图片路径存储到txt文件中

访问地址(文件夹):http://112.12.19.122:8081/urls/ml-backend-test/

进入labelstudio将txt文件路径填入,点击Add URL将图片导入项目进行标注。

二、启用模型服务

首先pip安装label-studio-ml

进入到projects文件夹,将init_model.py放入该文件夹,然后执行命令label-studio-ml init my_backend来初始化模型文件夹。init_model.py的代码如下:

#!/user/bin/env python3
# -*- coding: utf-8 -*-
from label_studio_ml.model import LabelStudioMLBaseclass DummyModel(LabelStudioMLBase):def __init__(self, **kwargs):# don't forget to call base class constructorsuper(DummyModel, self).__init__(**kwargs)# you can preinitialize variables with keys needed to extract info from tasks and annotations and form predictionsfrom_name, schema = list(self.parsed_label_config.items())[0]self.from_name = from_nameself.to_name = schema['to_name'][0]self.labels = schema['labels']def predict(self, tasks, **kwargs):""" This is where inference happens: model returnsthe list of predictions based on input list of tasks"""predictions = []for task in tasks:predictions.append({'score': 0.987,  # prediction overall score, visible in the data manager columns'model_version': 'delorean-20151021',  # all predictions will be differentiated by model version'result': [{'from_name': self.from_name,'to_name': self.to_name,'type': 'choices','score': 0.5,  # per-region score, visible in the editor'value': {'choices': [self.labels[0]]}}]})return predictionsdef fit(self, annotations, **kwargs):""" This is where training happens: train your model given list of annotations,then returns dict with created links and resources"""return {'path/to/created/model': 'my/model.bin'}

进入到my_backend文件夹,可以看到下述文件:

在my_backend文件夹下新建model文件夹,将训练好的YOLO模型文件放入model下:

修改my_backend文件夹下的model.py,代码如下:

#!/user/bin/env python3#  -*- coding: utf-8 -*-import osfrom typing import List, Dict, Optionalimport torchfrom label_studio_ml.model import LabelStudioMLBasefrom label_studio_ml.utils import get_single_tag_keys, get_local_pathimport loggingfrom ultralytics import YOLOfrom PIL import Image# 设置日志logger = logging.getLogger(__name__)logging.basicConfig(level=logging.INFO)MODEL_PATH = os.getenv('MODEL_PATH', '/data/projects/my_ml_backend/model/best.pt')DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')class DummyModel(LabelStudioMLBase):"""Custom ML Backend model"""def __init__(self, **kwargs):super(DummyModel, self).__init__(**kwargs)from_name, schema = list(self.parsed_label_config.items())[0]self.from_name = from_nameself.to_name = schema['to_name'][0]self.labels = schema['labels']# 训练参数self.train_epochs = int(os.getenv('TRAIN_EPOCHS', 150))self.train_batch_size = int(os.getenv('TRAIN_BATCH_SIZE', 18))def predict(self, tasks: List[Dict], context: Optional[Dict] = None, **kwargs):task = tasks[0]print(f'''\Run prediction on {tasks}Received context: {context}Project ID: {task['id']}Label config: {self.label_config}Parsed JSON Label config: {self.parsed_label_config}''')img_url = task['data']['image']image_path = self.get_local_path(url=img_url)print(f'image_path: {image_path}')# Getting URL and loading imageimage = Image.open(image_path)# Height and width of imageoriginal_width, original_height = image.size# Creating list for predictions and variable for scorespredictions = []scores = 0i = 0# Initialize self variablesself.from_name, self.to_name, self.value, self.classes = get_single_tag_keys(self.parsed_label_config, 'RectangleLabels', 'Image')# 加载自己的yolov11模型logger.info(f"加载YOLO11模型: {MODEL_PATH}")self.model = YOLO(MODEL_PATH)# 检查GPU可用性logger.info(f"使用设备: {'GPU ✅' if DEVICE.type == 'cuda' else 'CPU ⚠️'}")# 改动的地方, 增加了conf配置, 只有conf>=0.5的才会被标记出来# 默认conf是0.25, 不改的话被标注的地方肯能会很多, 根据自己的实际情况配置# Getting prediction using modelresults = self.model.predict(image, conf=0.5)# print(results)# Getting mask segments, boxes from model predictionfor result in results:for i, prediction in enumerate(result.boxes):score = prediction.conf.item()label_index = int(prediction.cls.item())xyxy = prediction.xyxy[0].tolist()# print(f"{i} prediction", prediction)# x_center, y_center, w, h = boxpredictions.append({"id": str(i),"from_name": self.from_name,"to_name": self.to_name,"type": "rectanglelabels","score": score,"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[label_index]]}})scores += scorelogger.info(f"预测完成: 检测到 {len(predictions)} 个对象")# Dict with final dicts with predictionsfinal_prediction = [{"result": predictions,"score": scores / (i + 1),"model_version": "11x"}]return final_predictiondef fit(self, event, data, **kwargs):"""使用新标注数据训练模型参数:event: 事件类型 ('ANNOTATION_CREATED', 'ANNOTATION_UPDATED')data: 包含标注数据的字典**kwargs: 额外参数"""# 检查是否有训练数据if not self.train_output:logger.info("初始化训练数据存储")self.train_output = {'image_paths': [],'labels': []}# 获取标注信息annotation = data['annotation']image_url = annotation['task']['data']['image']image_path = self.get_local_path(image_url)# 解析标注结果bboxes = []for result in annotation['result']:if result['from_name'] == self.from_name:value = result['value']label = value['rectanglelabels'][0]# 获取图像尺寸image = Image.open(image_path)img_width, img_height = image.size# 转换为绝对坐标x = value['x'] * img_width / 100y = value['y'] * img_height / 100width = value['width'] * img_width / 100height = value['height'] * img_height / 100# YOLO格式: [class_idx, x_center, y_center, width, height] (归一化)x_center = (x + width / 2) / img_widthy_center = (y + height / 2) / img_heightnorm_width = width / img_widthnorm_height = height / img_heightclass_idx = self.labels.index(label)bboxes.append([class_idx, x_center, y_center, norm_width, norm_height])# 保存训练数据self.train_output['image_paths'].append(image_path)self.train_output['labels'].append(bboxes)logger.info(f"收到新标注: 图像={image_path}, 标注数={len(bboxes)}")logger.info(f"当前训练集大小: {len(self.train_output['image_paths'])}")# 当有足够数据时开始训练if len(self.train_output['image_paths']) >= 10:logger.info("达到最小训练集大小,开始训练...")self.train_model()# 重置训练数据self.train_output = {'image_paths': [],'labels': []}# 返回新模型信息return {'model_path': MODEL_PATH,'model_version': f"retrained-{len(self.train_output['image_paths'])}"}return {}def train_model(self):"""使用收集的标注数据训练模型"""logger.info("准备训练数据...")# 创建YOLO格式的训练数据目录结构train_dir = 'yolo_train_data'images_dir = os.path.join(train_dir, 'images')labels_dir = os.path.join(train_dir, 'labels')os.makedirs(images_dir, exist_ok=True)os.makedirs(labels_dir, exist_ok=True)# 创建数据集描述文件with open(os.path.join(train_dir, 'dataset.yaml'), 'w') as f:f.write(f"train: {os.path.abspath(images_dir)}\n")f.write(f"nc: {len(self.labels)}\n")f.write(f"names: {self.labels}\n")# 准备训练数据for i, (image_path, bboxes) in enumerate(zip(self.train_output['image_paths'],self.train_output['labels'])):# 复制图像img = Image.open(image_path)img_filename = f'train_{i}.jpg'img.save(os.path.join(images_dir, img_filename))# 创建标签文件label_filename = f'train_{i}.txt'with open(os.path.join(labels_dir, label_filename), 'w') as f:for bbox in bboxes:class_idx, x_center, y_center, width, height = bboxf.write(f"{class_idx} {x_center} {y_center} {width} {height}\n")logger.info(f"训练数据准备完成: {len(self.train_output['image_paths'])} 张图像")# 训练模型 (这里简化了实际训练过程)logger.info(f"开始训练模型 (模拟) - 周期={self.train_epochs}, 批次大小={self.train_batch_size}")# 调用YOLO的训练脚本:# import subprocess# subprocess.run(['python', 'train.py'])# 参数配置: --img 640 --batch {self.train_batch_size} --epochs {self.train_epochs}#          --data {os.path.join(train_dir, 'dataset.yaml')} --weights {MODEL_PATH}logger.info("训练完成! 模型已更新")# 重新加载训练后的模型# self.model = YOLO(MODEL_PATH)

最后执行命令label-studio-ml start my_ml_backend -p 9094来启动模型后端服务

三、labelstudio中配置后端模型服务

进入到项目中点击Model菜单,然后点击connect model,弹框填写配置好服务地址,点击保存即可。

四、逐个点击任务即可完成自动化标注

点击任务后自动加载模型推理,片刻后得到自动化标注结果,基于该标注结果可继续修改标注。

可以看到,预测列为1的表明已经推理完毕。

对应的脚本打印信息如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90603.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90603.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90603.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始大模型之编码注意力机制

从零开始大模型之编码注意力机制1 长序列建模中的问题2 使用注意力机制捕捉数据依赖关系3 自注意力机制4 实现带可训练权重的自注意力机制5 利用因果注意力隐藏未来词汇6 将单头注意力扩展到多头注意力7 Pytorch附录7.1 torch.nn.Linear多头掩码可训练权重的注意力机制。为什么…

小架构step系列26:Spring提供的validator

1 概述对于Web服务,需要对请求的参数进行校验,可以对不合法的参数进行提示,提高用户体验。也可以防止有人恶意用一些非法的参数对网站造成破坏。如果是对每个参数都写一段代码来判断值是否合法,那校验的代码就很多,也很…

0编程基础:用TRAE写出了会蹦跳躲避散发炫光的贪吃蛇小游戏

在某个深夜的代码深渊里,一个从未写过print("Hello World")的小白开发者,竟用自然语言指令让贪吃蛇跳起了"光棱华尔兹"——蛇身折射出彩虹轨迹,食物像星舰般自动规避追击,甚至实现了四头蛇的"量子纠缠式…

在Word和WPS文字中要同时查看和编辑一个文档的两个地方?拆分窗口

如果要在Word或WPS文字的长文档中同时查看两部同步的地方(文档位置),来回跳转和滚动费时费力,使用拆分窗口的功能即可搞定。将窗口一分为二,上下对照非常方便。一、拆分窗口的路径Word和WPS基本一样,就是菜…

Windows系统下安装mujoco环境的教程【原创】

在学习Mujoco仿真的过程中,我先前是在linux系统下进行的研究与学习,今天来试试看在windows系统中安装mujoco仿真环境。 先前在linux中的一些关于mujoco学习记录的博客:Mujoco仿真【xml文件的学习 3】_mujoco打开xml文件-CSDN博客 下面开始wi…

CSS中篇

#Flex布局#1、什么是flex布局?flex 布局,全称弹性布局(Flexible Box Layout),是 CSS3 中引入的一种新的布局模式。它主要通过给容器设置相关属性,来控制容器内部子元素的排列方式。相比传统的浮动布局和定位…

《云计算蓝皮书 2025 》发布:云计算加速成为智能时代核心引擎

近日,中国信息通信研究院发布了《云计算蓝皮书(2025 年)》,全面剖析了云计算领域的发展现状与未来趋势。在人工智能蓬勃发展的当下,云计算正从基础资源供给向智能时代的核心引擎加速转变,成为重塑全球数字竞…

excel删除重复项场景

问题描述 问题描述:因为表格中存在多条相同的数据,我现在excel有一列,值为#N/A 。另外有列叫做药品名称、规格、厂家 我要删除值为 #N/A,并且 药品名称、规格、厂家相等的数据,那条相同的删掉,只保留一条,…

Vue 3 与 Element Plus 中的 /deep/ 选择器问题

Vue 3 与 Element Plus 中的 /deep/ 选择器问题 在 Vue3 中使用 Element Plus 组件时,使用 ::v-deep或 :deep()的场景取决于 ​​样式作用域​​ 和 ​​选择器目标​​。以下是关键区别:

2025暑期—06神经网络-常见网络

六个滤波核提取特征Maps5X5 卷积核,1个阈值 6个元素,卷积后两边各少两个,28*28像素 又有6个卷积核,所以有122304个连接,连接数不多是因为很多都是公用参数的。池化是参数池化,和当前平均最大不一样。编程14…

硅基计划3.0 学习总结 叁 栈和队列

文章目录一、栈1. 模拟实现栈2. 小试牛刀1. 判断一个栈的出栈顺序是否为题目给定情况2. 括号匹配3. 逆波兰表达式求值4. 求最小栈元素3. 单链表实现栈二、队列1. 官方队列类Queue2. 双向链表模拟实现Queue类3. 顺序表模拟实现Queue类4. 双端队列5. 队列实现栈6. 栈实现队列一、…

飞行控制领军者 | 边界智控携高安全级飞控系统亮相2025深圳eVTOL展

2025深圳eVTOL展将于2025年9月23日至25日在深圳坪山燕子湖国际会展中心盛大举办。本届展会以 “低空经济・eVTOL・航空应急救援・商载大型无人运输机” 为核心,预计将汇聚200余位发言嘉宾、500 余家顶尖展商及15,000余名专业观众,规模盛大,精…

React状态管理——Dva

目录 一、安装依赖 二、Dva注册model方式 2.1 自动注册models 2.2 手动注册model方式 三、创建 dva 实例 四、创建 model 五、在组件中使用 六、动态加载Dva Model Dva 是一个基于 redux 和 redux-saga 的轻量级前端框架,可以方便地在 React 应用中管理状态…

编程与数学 03-002 计算机网络 05_以太网技术

编程与数学 03-002 计算机网络 05_以太网技术一、以太网的基本原理(一)CSMA/CD协议的工作原理(二)以太网的帧结构二、以太网的拓扑结构与设备(一)传统以太网的拓扑结构(二)交换机的工…

解决英文版Windows10安装WireShark报错

问题点击WireShark安装包进行安装时报错原因编码方式故障解决方式修改操作系统编码1.WinR,输入Control,打开控制面板2.点击Small icons3.点击Region4.设置编码UTF-8

利用aruco标定板标定相机

1、生成aruco标定板#include <opencv2/opencv.hpp> #include <opencv2/aruco.hpp> #include <opencv2/objdetect/aruco_detector.hpp> #include <iostream> #include <string>using namespace cv; using namespace std;int main() {int markers…

C/C++语言程序使用三种主要的内存分配方式,和python语言一样么?

这是一个很好的比较问题&#xff01;C/C 和 Python 在内存分配方式上有本质的区别&#xff0c;虽然它们最终使用的都是计算机的物理内存&#xff08;堆、栈等&#xff09;&#xff0c;但语言层面提供的抽象和管理机制完全不同。核心区别&#xff1a;控制权&#xff1a; C/C 程序…

小电流驱动大电流:原理、实现方式与应用前景

目录 一、什么是“小电流驱动大电流”&#xff1f; 举个例子&#xff1a; 二、核心原理与实现方式 1. 电流放大原理 2. 电子开关元件 3. 控制电路设计 4. 附加保护措施 三、为什么采用“小电流驱动大电流”&#xff1f; 1. 提高安全性 2. 降低能耗 3. 改善效率 4. …

【DM数据守护集群搭建-读写分离】

DM数据守护集群搭建-读写分离 读写分离集群由一个主库以及一个或者多个配置了即时&#xff08;Timely&#xff09;归档或实时&#xff08;Realtime&#xff09;归档的备库组成&#xff0c;其主要目标是在保障数据库可用性基础上&#xff0c;实现读、写操作的自动分离&#xff0…

earth靶场

1、找ip和端口主机是192.168.6.213&#xff0c;因此靶场ip就是192.168.6.34&#xff0c;三个端口开放&#xff0c;我们去访问一下页面。三个端口都无法访问。我们使用nmap进行dns解析。nmap -A -p- -T4 -sV 192.168.6.34把这两条解析添加到hosts文件中去&#xff0c;这样我们才…