yolov8 与 label studio实现半自动化标注

准备工作

  • label studio前端服务
  • 下载lable studio ml backend 代码
  • yolov8训练好的模型

安装label studio前端

实现自己的label studio ml backend

  • 下载code, Github地址: https://github.com/HumanSignal/label-studio-ml-backend

  • 根据README中的说明创建自己的ml后端, 如图:
    在这里插入图片描述

  • 我创建时用的命令:

    #用清华的源会快一点
    pip install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple
    #创建自己的后端服务
    label-studio-ml create my_ml_backend
    

    在这里插入图片描述

  • 在model.py中重写predict() 函数, 里面会有返回值的json样例, 具体重写方法可以参考官方地址: label-studio-yolov8-backend 中的model_det.py, 这个地方要注意的是获取到xyxy后需要转换为lable studio对应的坐标值, 我根据官方文档修改后的代码会贴在文章最后。

  • 重写完成后的本地启动命令:

    label-studio-ml start my_ml_backend -p 9091
    

前端配置及效果展示

  • 新建项目以及使用可以看up主视频: label-studio 环境搭建
  • model配置:
    1. Settings中选中Model, 点击Connect Model
      在这里插入图片描述
    2. 启动好你的ml后端后, 做如下配置:
      Name: 自己定义(ML)
      Backend URL: 后端服务地址, 我本地启动的,所以是127.0.0.1, 端口号就是启动命令(label-studio-ml start my_ml_backend -p 9091)中设置 -p, 点击 Validate and Save, 成功后如下图
      在这里插入图片描述
      在这里插入图片描述
  • 导入图片后,选中你要自动标注的图片, 按下图点击 Retrieve Predictions
    在这里插入图片描述
    在这里插入图片描述
  • 效果如下图: 预标注这一列会由0 变为 1,检查是否需要手动修改
    在这里插入图片描述
    在这里插入图片描述

yolov8安装及模型训练

label studio ml backend 重写 predict()思路

  • 最终的目的:我们只需要根据例子, 封装好对应结构的数据返回即可, 关键就是拿到示例中对应的结果参数
  • from_name, type, to_name这些值都是label studio的参数, 可以从传入参数tasks中获取
  • original_width, original_height是原图片的大小, 可以通过image.size获取
  • 重点是 rectanglelabels, xyxy的值需要从模型解析的结果中获取,
    我们可以写个yolov8的测试函数, 看看 results = self.model.predict(image,conf=0.5) 中results的返回结果包含什么,
    xyxy的值就包含在results的boxes属性里面,
  • rectanglelabels 就是标注所属的类型, 比如Hero,Monster, boxes中有个cls的属性, 包含的就是 self.model.names中对应的编号key值, 可以根据 cls 和 names将 编号 转换为 具体的类型名字
  • xyxy的值需要转换成label studio对应的值

我自己修改后的label studio ml backend代码

from typing import List, Dict, Optional
from label_studio_ml.model import LabelStudioMLBase
from label_studio_ml.response import ModelResponse
from label_studio_ml.utils import get_single_tag_keys, get_local_path

from ultralytics import YOLO
from PIL import Image


class NewModel(LabelStudioMLBase):
    """Custom ML Backend model
    """

    def setup(self):
        self.set("model_version", "0.0.1")
        # Initialize self variables
        self.from_name, self.to_name, self.value, self.classes = get_single_tag_keys(
            self.parsed_label_config, 'RectangleLabels', 'Image')
	
        # 加载自己的yolov8模型
        self.model = YOLO('my_ml_backend/model/best.pt')
        # 改动的地方, 这里要获取自己模型中的类型
        self.labels = self.model.names


    def predict(self, tasks: List[Dict], context: Optional[Dict] = None, **kwargs) -> ModelResponse:
        print(f'''\
        Run prediction on {tasks}
        Received context: {context}
        Project ID: {self.project_id}
        Label config: {self.label_config}
        Parsed JSON Label config: {self.parsed_label_config}
        Extra params: {self.extra_params}''')

		# 改动的地方: 也可以按原方法获取图片路径, ls_host就是你本机的ip地址
        image_path = self.get_local_path(url=tasks[0]['data'][self.value],ls_host="http://192.168.131.220", task_id=tasks[0]['id'])
        print(f'image_path: {image_path}')

        # Getting URL and loading image
        image = Image.open(image_path)
        # Height and width of image
        original_width, original_height = image.size

        # Creating list for predictions and variable for scores
        predictions = []
        score = 0
        i = 0

		# 改动的地方, 增加了conf配置, 只有conf>=0.5的才会被标记出来 
		# 默认conf是0.25, 不改的话被标注的地方肯能会很多, 根据自己的实际情况配置
        # Getting prediction using model
        results = self.model.predict(image,conf=0.5)

        # Getting mask segments, boxes from model prediction
        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()

        print(f"Prediction Score is {score:.3f}.")

        # Dict with final dicts with predictions
        final_prediction = [{
            "result": predictions,
            "score": score / (i + 1),
            "model_version": "v8n"
        }]

        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.')

注意事项

  • label studio导入图片一次性导入太多会卡死, 我这边一次最多导入40张
  • 自动标注时,选中的task太多会执行很慢, 可能会出问题, 我这边一次Retrieve Predictions 20个task

相关链接

结语

  • 本来是想用yolov5 和 label studio实现自动化标注的, 但是没研究出来, 主要卡在模型解析这一步, 我将pt转为onnx之后解析, 返回的结果没有yolov8 返回的结果结构清晰, 导致不知到怎么从结果中获取想要的信息, 如果有网友有现成的示例的, 希望能提供个链接看看代码。

  • 我在xyxy坐标转换这一步卡了很久, 网上也没有搜到有用的信息,最后无意中搜到 label-studio-yolov8-ml-backend才解决。

  • 因为一开始就研究的 label-studio-ml-backend的代码, 所以在后端启动方面都是用的 这部分代码, 只是在 predict()重写方面,将label-studio-yolov8-backend 的代码移植了过来

  • 如果有yolov和 label studio的依赖问题, 可以尝试命令:

    # label studio sdk
    pip install --upgrade label-studio-sdk -i https://pypi.tuna.tsinghua.edu.cn/simple
    # yolov8 加载模型需要使用的包
    pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值