文章目錄
- 1. 準備環境
- 2. 腳本啟動
- 2.1 Windows
- 2.2 Linux
- 3. 安裝label-studio機器學習后端
- 3.1 pip安裝(推薦)
- 3.2 GitHub倉庫安裝
- 4. 后端配置
- 4.1 yolo環境
- 4.2 引入后端模型
- 4.3 修改腳本
- 4.4 啟動后端
- 5. 標注工程
- 5.1 創建工程
- 5.2 配置圖片路徑
- 5.3 配置工程類型標簽
- 5.4 配置模型
- 5.5 標注
- 5.6 批注
- 5.7 導出
- 6. 重命名文件
1. 準備環境
首先創建一個虛擬環境:
conda create -n label_studio python=3.12
conda activate label_studio
然后使用pip安裝label-studio
:
pip install label-studio
然后可以打開label-studio
了:
label-studio start
終端中應該能看到如下的內容:
并且會自動打開瀏覽器,并進入到如下的頁面(如果沒有自動打開,可以自己打開瀏覽器,然后輸入http://localhost:8080
進行訪問):
2. 腳本啟動
為了每次都能夠一鍵啟動,而不是在終端內輸入很多條命令,可以將相關的命令封裝成腳本。
2.1 Windows
新建一個bat腳本,比如命名為load_label_studio.bat
,然后復制一下的一下的內容,并且,必須確保這個腳本中的編碼是GB2312
!否則將無法啟動。
@echo off:: 打印信息
echo 開始啟動 label-studio :: 配置區域
set CONDA_ENV=label_studio
set ROOT_DIR=C:\\dl\\datasets:: 激活conda環境
echo.
echo [1/3] 正在激活 Conda 環境 %CONDA_ENV%
call conda activate %CONDA_ENV%:: 檢查激活是否成功
if %errorlevel% neq 0 (echo.echo 激活 Conda 環境 %CONDA_ENV% 失敗!echo 請檢查是否沒有創建這個環境pauseexit /b
):: 配置環境變量
echo.
echo [2/3] 配置 label-studio 的環境變量
set LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true
set LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=%ROOT_DIR%
set DATA_UPLOAD_MAX_NUMBER_FILES=2000:: 啟動 label-studio
echo.
echo [3/3] 啟動 label-studio
label-studio start:: 檢查是否成功啟動
if %errorlevel% neq 0 (echo.echo 啟動 label-studio 失敗!echo 請檢查安裝情況,或者是否調用錯誤!
) else (echo.echo label-studio 運行成功echo 可通過地址直接訪問: http://localhost:8080
):: 保持命令行窗口打開
pause
腳本的一些說明:
- 腳本中默認你已經在系統設置中配置了conda的環境變量!
- 配置區域中的
CONDA_ENV
是使用label-studio的python環境- 配置區域中的
ROOT_DIR
是本地文件的默認文件夾- 這里啟動的label-studio都是按照默認參數進行啟動,如果需要綁定某個ip,指定某個端口,需要修改一下
label-studio start
行
2.2 Linux
暫無待補充
3. 安裝label-studio機器學習后端
有兩種安裝方法,推薦使用第一種。
3.1 pip安裝(推薦)
pip install label-studio-ml
3.2 GitHub倉庫安裝
在合適的目錄下打開終端:
conda activate label_studio
git clone https://github.com/heartexlabs/label-studio-ml-backend
cd label-studio-ml-backend
pip install -U -e .
但是有可能會出現如下的報錯:
Installing collected packages: label-studio-sdk, label-studio-ml━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0/2 [label-studio-sdk] DEPRECATION: Legacy editable install of label-studio-ml==2.0.1.dev0 from file:///E:/Code/VsCode/python/label-studio-ml-backend-master (setup.py develop) is deprecated. pip 25.3 will enforce this behaviour change. A possible replacement is to add a pyproject.toml or enable --use-pep517, and use setuptools >= 64. If the resulting installation is not behaving as expected, try using --config-settings editable_mode=compat. Please consult the setuptools documentation for more information. Discussion can be found at https://github.com/pypa/pip/issues/11457Running setup.py develop for label-studio-ml
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
label-studio 1.18.0 requires label-studio-sdk==1.0.12, but you have label-studio-sdk 1.0.18.dev0 which is incompatible.
這里我們只需要關注這里要求什么版本的label-studio-sdk
,然后再使用pip命令裝一下就可以了:
pip install label-studio-sdk==1.0.12
當然,為了使用自己的pt模型,還是需要安裝torch
和onnx
等這些,這里以cpu的為例:
conda activate label_studio
pip3 install torch torchvision torchaudio # https://pytorch.org/get-started/locally/ 直接參考官網的安裝方法pip install onnx
4. 后端配置
這里我們需要使用自己已經訓練好的yolo模型,所以還需要額外的配置。
4.1 yolo環境
首先需要安裝yolo的環境,需要安裝ultralytics
:
conda activate label_studio
pip install ultralytics
4.2 引入后端模型
首先在合適的位置創建一個目錄,專門用來存放backend的相關東西,比如c:\dl\label_studio_backend
,那么在這里打開終端,開始進行操作。
conda activate label_studio
label-studio-ml create ml_backend_test1
發現這里就創建了一個ml_backend_test1
的目錄,然后我們需要進入這個目錄下進行文件的修改。
將我們的訓練好的模型best.pt
放到目錄中,然后需要對這個model.py
進行修改。
4.3 修改腳本
對于model.py進行修改,參考:
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_image_local_path# 添加yolo需要的依賴
from ultralytics import YOLO# 添加額外處理需要的依賴
import re
import os
from urllib.parse import unquote
from PIL import Imagedef custom_get_local_path(url):"""自定義路徑解析函數,兼容Windows特殊格式"""# 1. 嘗試官方方法try:from label_studio_ml.utils import get_image_local_pathreturn get_image_local_path(url)except:pass# 2. 手動解析本地文件URLif url.startswith('/data/local-files'):# 提取相對路徑部分match = re.search(r'd=(.*?)(?:&|$)', url)if match:relative_path = unquote(match.group(1))base_dir = os.environ.get('LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT', 'c:\\dl\\datasets')# 處理Windows路徑分隔符relative_path = relative_path.replace('/', '\\')return os.path.join(base_dir, relative_path)# 3. 處理上傳文件路徑if url.startswith('/data/upload'):relative_path = url.replace('/data/upload/', '').lstrip('/')base_dir = os.environ.get('LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT', 'c:\\dl\\datasets')return os.path.join(base_dir, relative_path.replace('/', '\\'))return url # 退回原始URLclass NewModel(LabelStudioMLBase):"""Custom ML Backend model"""def setup(self):"""Configure any parameters of your model here"""self.set("model_version", "0.0.1")################## 修改 ################### 加載我們自己訓練好的模型# 注意這里的路徑,是要帶上文件夾目錄的,因為后續python腳本執行的環境將在更上一層目錄上!self._model = YOLO('./ml_backend_test1/best.pt')# 使用模型中的標簽self._labels = self._model.namesdef predict(self,tasks: List[Dict],context: Optional[Dict] = None,**kwargs) -> ModelResponse:""" Write your inference logic here:param tasks: [Label Studio tasks in JSON format](https://labelstud.io/guide/task_format.html):param context: [Label Studio context in JSON format](https://labelstud.io/guide/ml_create#Implement-prediction-logic):return model_responseModelResponse(predictions=predictions) withpredictions: [Predictions array in JSON format](https://labelstud.io/guide/export.html#Label-Studio-JSON-format-of-annotated-tasks)"""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}''')################## 修改 ##################results = []for task in tasks:# 獲取圖片的本地路徑image_path = custom_get_local_path(task['data']['image'])image = Image.open(image_path)img_width, img_height = image.size# 使用YOLO進行推理pred = self._model(image)predictions = []# 解析檢測結果for box in pred[0].boxes:x_min, y_min, x_max, y_max = map(float, box.xyxy[0].tolist())label = self._labels[int(box.cls.item())]predictions.append({"from_name": "label","to_name": "image","type": "rectanglelabels","value": {"x": x_min / img_width * 100,"y": y_min / img_height * 100,"width": (x_max - x_min) / img_width * 100,"height": (y_max - y_min) / img_height * 100,"rectanglelabels": [label]},"score": float(box.conf.item())})results.append({"result": predictions})return results# return ModelResponse(predictions=[])def fit(self, event, data, **kwargs):"""This method is called each time an annotation is created or updatedYou can run your logic here to update the model and persist it to the cacheIt is not recommended to perform long-running operations here, as it will block the main threadInstead, 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() runsold_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 cacheself.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.')
注意
這個腳本里面寫了讀取環境變量:
base_dir = os.environ.get('LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT', 'c:\\dl\\datasets')
所以,要么將這個環境變量直接寫入系統,要么就記得對應修改路徑……
4.4 啟動后端
完成上述工作之后,就可以啟動后端了。
還是需要進入到前面創建的backend的目錄中去,比如本文中的c:\dl\label_studio_backend
,然后在這里打開終端,然后操作:
conda activate label_studio
label-studio-ml start ml_backend_test1 -p 9090
啟動成功的畫面如下:
后面就可以在label-studio上創建工程進行標注了。
5. 標注工程
初始時候沒有賬號,可以在上述的頁面下創建一個賬號,就是需要一個郵箱和一個密碼,也不需要做什么驗證什么的,創建成功后會自動登錄,然后就能進入到頁面了。
如果需要進行一些賬號上的配置,可以點擊右上角的頭像,然后進行配置。
5.1 創建工程
這里直接點擊中間的Create Project
來創建一個標注的工程,打開后開始填寫:
其余的配置暫時不用管,后續再進入到工程中進行配置。
創建完成之后我們就可以點擊進入到test1
的工程中去了,顯示如下的畫面:
5.2 配置圖片路徑
上述圖片中,點擊右上角的Settings
進入到如下的配置頁面:
首先我們配置本地的圖片位置,左側的tab頁選擇Cloud Storage
,然后進入到Add Source Storage
。
按照如下的示例進行配置:
說明:
Storage Type
要選擇Local files
Storage Title
要和后面的path對應的名字要相同Absolute local path
必須是一個絕對路徑,并且要到圖片的根目錄上,并且要和前面腳本中配置set ROOT_DIR=C:\\dl\\datasets
對應上,必須是這個目錄下的子目錄File Filter Regex
來進行正則化過濾,有啥類型就按照正則化的方式寫好- 必須打開
Treat every bucket object as a source file
- 然后點擊
Check Connection
來驗證一下填寫有沒有問題,如果出現了Successfully connected!
就說明可以了,然后點擊Add Storage
來完成即可
然后需要在如下的頁面上點擊Sync Storage
,然后看到Status
的狀態是Conpleted
,如果狀態不是這個,那么一定是有什么步驟錯了,需要再檢查一下!
5.3 配置工程類型標簽
然后來配置工程的類型和標簽。
進入到Labeling Interface
中的Browser Templates
。
進入之后選擇Object Detection with Boarding Boxes
通過在紅色框中填寫對應的標簽,然后點擊下方的Add
,就可以添加一個label了,全部填寫完成之后,點擊下方的Save
即可。
5.4 配置模型
點擊左側tab的Model
進行模型的相關配置。
然后填寫:
- 這里的
Name
隨便寫,隨便取個名字Backend URL
就是填寫前面我們4.4 啟動后端
最終打印的地址,如果你的后端和label-studio是放在不同的機器上,就得注意ip了!- 然后點擊
Validate and Save
就可以完成。如果有報錯,需要檢查一下model.py
是不是寫錯了……
然后就可以看到:
5.5 標注
回到工程的首頁,我們現在就可以看到類似如下的顯示了:
這個時候我們選擇需要進行標注的文件,可以一個個勾選自己需要進行標注的文件,也可以直接全選,然后可以看到你現在有多少個任務了。
然后我們就可以點擊點擊進行處理了。
如果運行成功,則可以看到:
5.6 批注
導出之前必須Annotate
才行,所以隨便找一行,然后點擊對應Annotated by
列的空白處。(不得不說,真的是神奇的操作邏輯,非常的反人類反直覺)
或者,直接點擊上面的Label All Tasks
也能進入批注頁面,然后兩個稍微有一些不同。
然后得到如下的畫面:
可以直接點擊右下角的Submit
直接確認批注當前圖片。如果要修改,點擊圖片中的框圖進行操作。
非常難用的是 無法全選然后Submit,非常反人類反直覺
5.7 導出
導出之前必須確認你想要導出的圖片已經被標注了!!!!否則導出的文件都是空的……
點擊右上角的Export
進行導出。
然后選擇需要的格式:
最后到最下方,進行導出:
選擇只導出標簽的那個,會很快,如果圖片和標簽一起導出,等待的時間還是漫長的,尤其是數據越多越長……
導出之后的圖片名稱還會被修改……類似:
6. 重命名文件
像我這樣特別方案改的亂七八糟的名字,就直接使用一個腳本全部重命名了。
from pathlib import Path
import shutil# 指定圖片目錄
img_path = Path("image/")
lab_old = Path("old/")
lab_path = Path("label")# 定義支持的圖片擴展名(可擴展)
image_extensions = {".jpg", ".jpeg", ".png", ".bmp", ".gif"}# 計數器,用于生成新文件名
counter = 10000001# 遍歷目錄下的所有文件(不遞歸子目錄)
for file in img_path.iterdir():if file.is_file() and file.suffix.lower() in image_extensions:# 構建新文件名new_file = img_path / f"image_{counter:08d}{file.suffix}"old_label = lab_old / f"{file.stem}.txt"new_label = lab_path / f"image_{counter:08d}.txt"# 防止文件已存在if new_file.exists():print(f"?? 跳過:文件 {new_file.name} 已存在")continue# 重命名文件try:file.rename(new_file)shutil.copy(old_label, new_label)print(f"? 重命名:{file.name} -> {new_file.name}")counter += 1except Exception as e:print(f"? 重命名失敗:{file.name} -> {new_file.name},錯誤:{e}")
調整一下目錄的名字即可。