赞
踩
#站在巨人的肩膀上,搬运官方+踩坑实录
1、环境安装(不是我干的,跳过)
pip install label-studio
git clone https://github.com/HumanSignal/label-studio-ml-backend.git
会得到这个东西
- cd label-studio-ml-backend/
- pip install -U -e .
label-studio-ml create my_ml_backend
会创建以下文件目录:
my_ml_backend/
├── Dockerfile #用于docker-compose.yml
通过 Docker 运行 ML 后端。
├── docker-compose.yml
├── model.py #是主文件,您可以在其中实现自己的训练和推理逻辑
├── _wsgi.py #是一个帮助程序文件,用于通过 Docker 运行 ML 后端(您不需要修改它)。
├── README.md #有关如何运行 ML 后端的说明。
└── requirements.txt #是一个具有 Python 依赖项的文件。
- cd my_ml_backend/
- docker-compose up
ML 后端服务器位于http://localhost:9090
。将 ML 后端连接到 Label Studio 时,您可以使用此 URL。
label-studio start
Label Studio 开始于http://localhost:8080
.
Label Studio 可以自动创建从新创建的模型运行 ML 后端所需的所有必要配置和脚本。
调用您的 ML 后端my_backend
并从命令行初始化 ML 后端目录./my_backend
:
- label-studio-ml init my_ml_backend \
- --script ./my_ml_backend/model.py create my_ml_backend --force
由于每次初始化都会创建一个文件夹,因此在调试时,会碰到文件夹重复的错误。所以添加第二行强制创建目录。
label-studio-ml start my_ml_backend
不同用户的后端应当设置不同的端口...不然多个后端会冲突启动不了
label-studio-ml start my_ml_backend -p 9091
服务器启动http://localhost:9090
并在控制台中输出日志。
http://localhost:8080
跳过
然后输入后端的地址,两个选项都选上就ok啦:
如图,在标注界面,就会有个后端模型为我们自动打标的INITIAL标签(不可修改),并有个副本标签用于修改
每个样本在人工完成标注并在右下角submit后(第一次为submit后续都是update),会显示标注的数量,用于区分有没有经过人工标注。
同时,在数据界面可以看到,自动标注的数据和OCR后端自动标注的数据:
第二章提到,my_ml_backend/model.py是主要的文件,在这实现推理和训练(暂时没试过)。
这里以百度PaddleOCR为例,记录一下主要踩坑的点。
这里当然选择了OCR任务
选择完后,界面会跳转回来,用于设置标签等,不同的任务会有些不同,在Add label names输入aa并add,右边就多了一类aa标签。
为了开发ML后端,还需要看上图code这里和导出的数据结果。
图1 此处的是前端的一些用于标注组件,这里的组件类型,决定了标注结果的数据格式。
图2下图是根据上面的设置标注完的数据结果:
怎么对应数据呢?
1、图1中的 <Image name="image" value="$ocr" >,就对应了图2中的红框1
2、图1中接下来还有三块内容,<Labels>,<Retangle>or<Polygon>,<Textarea>。这三块内容分别对应了OCR任务的三个产出,即标签类型(text、handwrting、aa),区域类型(矩形、多边型)以及文本内容,在后面标注的时候均会产出数据。
<Labels>对应的数据为标注的时候使用的标签类型+区域坐标。(这也是个坑点,后面再说)
<Retangle>or<Polygon>对应的数据为标注的时候使用的 区域类型+区域坐标,区域类型为矩形或者多边形。
<Textarea>对应的数据为标注的时候填写的文字和区域坐标。
这三块内容,对应了图二中的result中的数据(红色大框区域)。以图2为例,图二是<Labels>对应的数据。小框2表名了数据是<Labels>部分,即'type':'labels',同理还有'type':'retangle'和'type':'textarea';小框3即为标签类型“Text”+区域坐标。
可以看下其他两个的数据结构,写后端代码时就根据这些来组装结果了。
总结一下:
以OCR任务为例,在前端设置标签时,会有三个任务,即标签类型、标注区域类型和文本结果。其他任务同理,对齐数据即可。
先根据官方的样例介绍一下:
label studio平台核心是调用my_ml_backend/model.py中的predict函数来获取模型打标的结果。因此主要需要改动的就是根据具体任务(这里为OCR),完成predict函数中图片OCR并封装成平台给定的格式。
- from label_studio_ml.model import LabelStudioMLBase
-
-
- class DummyModel(LabelStudioMLBase):
-
- def __init__(self, **kwargs):
- # 继承一下LabelStudioMLBase
- super(DummyModel, self).__init__(**kwargs)
-
- # 然后初始化一下要用的模型即可
- from_name, schema = list(self.parsed_label_config.items())[0]
- self.from_name = from_name
- self.to_name = schema['to_name'][0]
- self.labels = schema['labels']
-
- def predict(self, tasks, **kwargs):
- """ 核心的推理函数,在这完成label studio中图片获取、模型推理和数据打包返回
- """
- 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 predictions
-
- def fit(self, annotations, **kwargs):
- """边打标边训练使用的
- """
- return {'path/to/created/model': 'my/model.bin'}
- def other(self,xx)
- '''其他辅助函数
- '''
- return xx

直接贴一下个人写的基于百度paddleOCR的模型后端代码,坑点都在注释中了,大概有4个大坑,每个都坑的我非常难受...:
- from typing import List, Dict, Optional
- from label_studio_ml.model import LabelStudioMLBase
- import cv2
- import sys
- sys.path.append('./PaddleOCR')#自己的目录
- from paddleocr import PaddleOCR
- import os
- from PIL import Image
- import pandas as pd
- import numpy as np
- import requests
- from io import BytesIO
- import math
-
- global_ocr_instance = PaddleOCR(
- # 坑点1
- # PaddleOCR初始化参数,这里把模型提到外面来了,不然整页整页都在打印加载模型时的参数
- det_model_dir='./inference/ch_PP-OCRv4_det_infer/',
- rec_model_dir='./inference/ch_PP-OCRv4_rec_infer/',
- rec_char_dict_path='./PaddleOCR/ppocr/utils/ppocr_keys_v1.txt',
- lang="ch",det_algorithm='DB',use_gpu=True,ocr_version='PP-OCRv4'
- #其他推理参数根据情况自己调整
- )
-
- class NewModel(LabelStudioMLBase):
- def __init__(self,project_id=None,**kwargs):
- super(NewModel, self).__init__(**kwargs)
- self.ocr = global_ocr_instance
- self.token = '376641251a1be5a6d94******5767b2113c1afe'
-
- def predict(self, tasks, **kwargs):
- results = []
- for task in tasks:
- '''坑点2:读取图像文件。虽然label studio和模型在同一台服务器上,但是在不同的端口。这样就导致了:(1)label studio上传图片时,无法直接加载模型服务器目录下的图片;(2)模型后端无法直接读取label studio中上传的图片,source中显示的直接上传的图片目录为"/data/upload/12/bf68a25f-0034.jpg"。因此这里选择通过request请求获取数据。这里还有个小坑,每个账号有不同的token,请求的时候需要带上'''
- image_path = task['data']['ocr']
- image_url = 'http://localhost:8080'+image_path
- image = self.load_image_from_url(image_url,self.token)
- # 使用OCR模型处理图像
- ocr_results = self.ocr.ocr(np.array(image), cls=True)
-
- # 转换OCR结果为Label Studio所需的格式
- predictions = []
- '''#坑点3,必须带上id,上面说了,ocr任务有三个结果,如果没有id,前端就变成了3个结果'''
- ocr_id = 0
- for result in ocr_results[0]:
- points, text_score = result
- text, score = text_score
-
- x, y, width, height, rotation = self.convert_points_to_relative_xywhr(points,np.array(image))
- '''坑点4:显示的区域坐标并不是像素的绝对值位置,而是相对位置...因此要转换成百分比,并且是0-100之间的数字,所以在下面有个坐标转换函数'''
-
- # 标签(Labels)组件预测
- label_prediction = {
- 'from_name': 'label',
- 'id':str(ocr_id),
- 'to_name': 'image',
- 'type': 'labels',
- 'value': {
- 'x': x,
- 'y': y,
- 'width': width,
- 'height': height,
- 'rotation':rotation,
- 'labels': ['Text']
- }
- }
-
- # 矩形框(Rectangle)组件预测
-
- rectangle_prediction = {
- 'from_name': 'bbox',
- 'id':str(ocr_id),
- 'to_name': 'image',
- 'type': 'rectangle',
- 'value': {
- 'x': x,
- 'y': y,
- 'width': width,
- 'height': height,
- 'rotation':rotation
- }
- }
-
- # 文本区域(TextArea)组件预测
- textarea_prediction = {
- 'from_name': 'transcription',
- 'id':str(ocr_id),
- 'to_name': 'image',
- 'type': 'textarea',
- 'value': {
- 'x': x,
- 'y': y,
- 'width': width,
- 'height': height,
- 'rotation':rotation,
- 'text':[text]
- }
- }
-
- predictions.extend([label_prediction, rectangle_prediction, textarea_prediction])
- ocr_id += 1
-
- results.append({
- 'result': predictions
- })
-
- return results
- def load_image_from_url(self,url,token):
- headers = {'Authorization': f'Token {token}'}
- response = requests.get(url, headers=headers)
- if response.status_code == 200:
- image = Image.open(BytesIO(response.content))
- return image
- else:
- raise Exception(f"Error loading image from {url}")
-
-
- def convert_points_to_relative_xywhr(self, points, image):
- """
- Convert a list of points representing a rectangle to relative x, y, width, height, and rotation.
- The values are relative to the dimensions of the given image.
- Points are expected to be in the order: top-left, top-right, bottom-right, bottom-left.
- The rotation is calculated as the clockwise angle between the top edge and the horizontal line.
- Args:
- - points (list of lists): A list of four points, each point is a list of two coordinates [x, y].
- - image (numpy array): An image array.
- Returns:
- - tuple: (x, y, width, height, rotation) where x and y are the relative coordinates of the top-left point,
- width and height are the relative dimensions of the rectangle, and rotation is the angle in degrees.
- """
- # Extracting points
- top_left, top_right, bottom_right, bottom_left = points
-
- # Image dimensions
- img_height, img_width = image.shape[:2]
-
- # Calculate width and height of the rectangle
- width = math.sqrt((top_right[0] - top_left[0])**2 + (top_right[1] - top_left[1])**2)
- height = math.sqrt((bottom_right[0] - top_right[0])**2 + (bottom_right[1] - top_right[1])**2)
-
- # Calculate rotation in radians
- dx = top_right[0] - top_left[0]
- dy = top_right[1] - top_left[1]
- angle_radians = math.atan2(dy, dx)
-
- # Convert rotation to degrees
- rotation = math.degrees(angle_radians)
-
- # The top-left point is the origin (x, y)
- x, y = top_left
-
- # Convert dimensions to relative values (percentage of image dimensions)
- rel_x = x / img_width * 100
- rel_y = y / img_height * 100
- rel_width = width / img_width * 100
- rel_height = height / img_height * 100
-
- return rel_x, rel_y, rel_width, rel_height, rotation
-

保存后,按照前面的教程,原...后端,启动!
再在label studio中配置一下,就能获取自动打标结果了。
这个版本是一次性获得整张图片的预打标结果的。
有空再更新一下边画框边实时获得框内的OCR结果的教程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。