图像分割掩码标注转YOLO多边形标注
liuian 2025-05-08 19:42 78 浏览
Ultralytics 团队付出了巨大的努力,使创建自定义 YOLO 模型变得非常容易。但是,处理大型数据集仍然很痛苦。训练 yolo 分割模型需要数据集具有其特定格式,这可能与你从大型数据集中获得的格式不完全相同。如果你想使用巨大的 OpenImagesV7 作为图像和标签的来源,情况就是如此。
在本教程中,我们将介绍如何从 OpenImagesV7 获取数据(图像和分割掩码);如何将其转换为 YOLO 格式(这是本教程中最复杂的部分);以及如何使用我们的数据集训练 yolov8-seg 模型的预览。
NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割
1、环境
要清楚:本教程需要 Python 3(在 3.10 下测试)。作为基础图像,我使用 AWS Sagemaker conda_pytorch_p310 ,它包含 PyTorch 和许多常用工具,如 Numpy 和 OpenCV。不过,我们需要一些额外的包来覆盖它:
sudo yum install -y openssl-devel openssl11-libs libcurl
pip install --upgrade pip setuptools wheel
pip install fiftyone
pip install fiftyone-db-rhel7 --force-reinstall
pip install shapely polars
pip install ultralytics2、数据集
我们将使用 Google OpenImages Dataset v7 来训练我们的模型。这个数据集非常庞大,有数百万张图像,旨在完成一系列计算机视觉任务,例如对象检测、分类和实例分割。因此,每张图像都与这些任务的标签配对,涵盖了最常见的多种对象:人、脸、狗、猫、汽车、树木等。请注意,并非所有标签都适用于所有任务,因此在数据集网站上进行一些探索以了解它涵盖了您正在尝试构建的模型的扩展范围是值得的。对于大多数计算机视觉问题来说,这可能已经足够了。
因此,要实际获取数据,有一个很棒的工具叫做 FiftyOne,它能够只下载你需要的数据。这避免了下载和处理整个数据集的负担。我是否已经提到它非常庞大?如果没有 FiftyOne,处理起来真的很难。
第一步是选择一个好的位置来下载数据集。我假设 Sagemaker 环境采用默认设置,但您可以选择更适合您的设置。请记住,在此路径中将自动创建文件夹 open-images-v7,以保存数据。
# choose your prefered path to download the dataset
# a folder named open-images-v7 will be created automatically inside of it
dataset_path = '/home/ec2-user/SageMaker/dataset'
import os
import torch
import torchvision
import fiftyone as fo
fo.config.default_ml_backend = "torch"
fo.config.dataset_zoo_dir = dataset_path下一个函数只是为了让我们的脚本更简洁,也更灵活。我们可以简单地将所有想要的标签放入 fiftyone 下载器函数中,让它决定要获取每个标签的样本数量。但是,在这里我需要更好地控制要下载每个类别的样本数量。当然,如果确实有可用的样本, fiftyone 只会下载我们要求的数字,否则它会尽可能多地获取样本。
def download_dataset(split, classes, max_samples=None):
print(f'>> Split: {split}, classes: {classes}, max_samples: {max_samples}')
return fo.zoo.load_zoo_dataset(
"open-images-v7",
label_types=["segmentations"],
drop_existing_dataset=False,
split=split,
classes=classes,
max_samples=max_samples,
)在调用下载器函数之前,我们先描述一下我们需要的标签以及每个标签的数量。请注意,如果我们输入 None,我们就可以实现“尽可能多”的行为,而无需猜测一个很大的随机数。为了简单起见,我们将获取包含人或汽车的样本。最多一千个人物样本和尽可能多的汽车样本。我们希望 70% 用于训练,20% 用于验证,10% 用于测试;这些百分比不能保证,因为每个子集可能都没有足够的样本。
target_split = {'train': 0.7, 'validation': 0.2, 'test': 0.1}
target_classes = {
"Person": 1_000,
"Car": None
}现在我们可以运行下载器函数了。为此,我们将遍历上面定义的目标类。
for cls_name, total in target_classes.items():
for split_name, split_pct in target_split.items():
max_samples = int(total * split_pct) if total is not None else None
download_dataset(split=split_name, classes=[cls_name], max_samples=max_samples)即使在强大的 AWS Sagemaker 环境中,脚本也可能需要一段时间才能完成。当然,时间与请求的样本数量成正比。下载数据集后,我们仍未准备好将其与 yolo 一起使用。数据以彩色图像的形式出现,并配有一组分割蒙版(灰度图像),图像中的每个对象都有一个蒙版。但是,yolo 需要将彩色图像与文本文件配对,文本文件的每一行都描述对象的类别和描述其多边形的坐标。请查看 yolo 文档。这将引出我们的下一章,介绍如何将数据集转换为适合 YoloV8 的格式。
3、将 OpenImagesV7 转换为 Yolo 分割
原始分割标签以灰度图像的形式提供。如前所述,Yolo 要求分割标签位于文本文件中,其中包含图像中每个对象的一行,遵循以下模式:对象类 ID,然后是描述对象多边形的 XY 列表。
这种转换需要一些工具来加载图像、将蒙版转换为多边形、降低多边形复杂性并将多边形写在文本文件中。所以让我们在一个新的干净的笔记本或 Python 脚本中一步一步地完成这项工作。首先是基本配置:
# base path must be the same as our previous dataset_path
base_path = '/home/ec2-user/SageMaker/dataset'
# destination of the converted dataset
target_path = '/home/ec2-user/SageMaker/dataset-yolo'
# a list with same keys as on fetch.py
target_classes = [
"Person",
"Car"
]
import os
import cv2
import yaml
import shutil
import pandas as pd
import polars as pl
import multiprocessing
import numpy as np
from tqdm import tqdm
from joblib import Parallel, delayed
from shapely.geometry import Polygon
from matplotlib import pyplot as plt现在我们定义一些函数来执行从灰度蒙版图像到简化多边形,然后再到 XY 列表的实际转换:
### Mask to Poly ###
def mask_to_polygon(mask_path):
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
contours, _ = cv2.findContours(
mask,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
polygons = []
for contour in contours:
polygon = contour.reshape(-1, 2)
polygon_norm = polygon.astype(float)
polygon_norm[:, 0] /= mask.shape[1] # X
polygon_norm[:, 1] /= mask.shape[0] # Y
polygon_norm = np.round(polygon_norm, 4)
polygon_shapely = Polygon(polygon_norm)
polygon_simplified = polygon_shapely.simplify(0.002, preserve_topology=True)
polygons.append(polygon_simplified)
return polygons
def polygon_to_yolo(polygon):
x, y = polygon.exterior.coords.xy
xy = []
for xx, yy in zip(x, y):
xy.append(xx)
xy.append(yy)
return xy
def polygon_to_mask(polygon, shape):
mk = np.zeros(shape, dtype=np.uint8)
x, y = polygon.exterior.coords.xy
xy = [
[int(xx * shape[1]), int(yy * shape[0])]
for xx, yy in zip(x, y)
]
cv2.fillConvexPoly(mk, np.array(xy, dtype='int32'), color=255)
return mk准备好图像处理工具后,下一步就是加载标签位置。这将遍历数据库文件系统查看图像文件,对于每幅图像,它将查看分割索引并获取相应的蒙版图像位置。到目前为止,图像仍未打开。有趣的事实:这里使用的是 polars 而不是 pandas,因为这个简单任务的处理速度差异很大。
### loading openimagesv7 labels ###
class_list_filepath = os.path.join(base_path, 'train/metadata/classes.csv')
class_df = pd.read_csv(class_list_filepath, header=None, names=['URI', 'ClassName'])
class_map_r = dict(zip(class_df.URI, class_df.ClassName))
class_map_r = {k: v for k, v in class_map_r.items() if v in target_classes}
# convert from openimagev7 label hash to an integer
class_map = { k: i for i, k in enumerate(list(class_map_r.keys()))}
# class_map = {
# '/m/01g317': 0, # 'Person'
# '/m/0k4j': 1, # 'Car'
# }
print('class_map:')
print(class_map)
def get_image_file_names(directory):
image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp'] # Add more extensions if needed
image_file_names = set()
for filename in os.listdir(directory):
nm, ext = os.path.splitext(filename)
if ext in image_extensions:
image_file_names.add(nm)
return image_file_names
def load_labels(split_name):
df = pl.read_csv(os.path.join(base_path, split_name, 'labels/segmentations.csv'))
df = df[['MaskPath', 'ImageID', 'LabelName']]
image_ids = get_image_file_names(os.path.join(base_path, split_name, 'data'))
df = df.filter(pl.col('ImageID').is_in(image_ids))
target_ids = set(class_map.keys())
df = df.filter(pl.col('LabelName').is_in(target_ids))
df = df.with_columns(pl.col('MaskPath').map_elements(lambda x: x[0].upper()).alias('Subdir'))
df = df.with_columns((base_path + f'{split_name}/labels/masks/' + pl.col('Subdir') + '/' + pl.col('MaskPath')).alias('MaskFullPath'))
df = df.with_columns(pl.col(['LabelName']).map_dict(class_map).alias('LabelID'))
return df
train_df = load_labels('train')
valid_df = load_labels('validation')
test_df = load_labels('test')最后,是时候遍历所有掩模图像,将其转换为 XY 多边形列表并按照 yolo 标准将结果写入文本文件中。
def macro_mask2yolopoly(p):
try:
poly = mask_to_polygon(p)
xy = polygon_to_yolo(poly[0])
return xy
except:
return []
return []
def conv_mask_xy(df):
return df.with_columns(
pl.col('MaskFullPath').map_elements(
lambda p: macro_mask2yolopoly(p)
).alias('XY')
)
train_df = conv_mask_xy(train_df)
valid_df = conv_mask_xy(valid_df)
test_df = conv_mask_xy(test_df)
def write_yolo_labels(df, subset, persistence=True):
df = df.filter(pl.col('XY').map_elements(len) > 0)
df = df.with_columns(
pl.col('XY').map_elements(lambda xy: xy.map_elements(lambda e: str(e))).list.join(' ').alias('TXY'))
df = df.with_columns(
(pl.col('LabelID').cast(pl.Utf8) + ' ' + pl.col('TXY')).alias('Sample'))
g = df.group_by('ImageID').agg(['Sample'])
g = g.with_columns(pl.col('Sample').list.join('\n').alias('StrSamples'))
g = g.with_columns((target_path + subset + '/' + pl.col('ImageID') + '.txt').alias('Path'))
if persistence:
os.makedirs(os.path.join(target_path, subset), exist_ok=True)
for row in g.iter_rows(named=True):
with open(row['Path'], 'w') as f:
f.write(row['StrSamples'])
return g
train_df = write_yolo_labels(train_df, 'train')
valid_df = write_yolo_labels(valid_df, 'validation')
test_df = write_yolo_labels(test_df, 'test')好吧,我们的新数据集最终被拆分成不同位置的图像和 yolo 标签。因此,为了统一,我们需要将下载的图像连同我们的标签一起复制到目标位置。这里有几种可能性,比如在 openimagesv7 文件夹中创建标签、移动图像等。我更喜欢复制并保留原始文件,以防脚本失败,稍后我可以简单地删除整个 openimages 缓存以释放一些空间。
def copy_data(df, subset):
for iid in df.select(pl.col('ImageID')).get_columns()[0].to_list():
try:
fnm = f"{iid}.jpg"
src = os.path.join(base_path, subset, "data", fnm)
dst = os.path.join(target_path, subset)
# print(f'{src} -> {dst}')
shutil.copy2(src, dst)
except:
continue
copy_data(valid_df, 'validation')
copy_data(test_df, 'test')
copy_data(train_df, 'train')Yolo 数据集仅包含描述它的 YAML 文件才完整。创建一个很容易,就像这样:
from pathlib import Path
yaml_content = f'''
path: /home/ec2-user/SageMaker/dataset-yolo
train: train
val: validation
test: test
# Classes - use the class_map as guide
names:
0: person
1: car
'''
with Path(os.path.join(target_path, 'seg_dataset.yaml')).open('w') as f:
f.write(yaml_content)4、训练 YoloV8-Seg 模型
本教程最难的部分是让数据集适合 Yolo。鉴于有更好的关于如何训练 Yolo 模型的教程,我将简短地讲解本课程。还请查看 Ultralytics 文档。但为了让你不要抱怨这是一个半生不熟的教程,我们开始吧:
# on the command line (CLI)
yolo segment train data=/home/ec2-user/SageMaker/dataset-yolo/seg_dataset.yaml model=yolov8n-seg.pt epochs=100 imgsz=640这将使用预先训练好的 yolov8n-seg.pt 作为我们自定义分割模型的基础,并将更新超过 100 个 epoch。模型的输出可能位于 ./runs/exp/ 上。如果不是这种情况,请查看文档。
5、参考文档
- vchaparro 的代码:将掩码转换为多边形
- Alon Lekhtman 关于训练 YoloV8-Seg 的 Medium 帖子
- Google OpenImagesV7 数据集
- Ultralytics
原文链接:分割掩码转YOLO格式 - BimAnt
相关推荐
- 显卡驱动坏了怎么修复(显卡驱动失效 哪里出问题)
-
1.在此电脑右击,选择管理,进入管理设备;2.在管理设备窗口选择设备管理器,进入找到显示适配器,点击显示适配器前面的>符号或者双击展开子选项;3.在显卡子选项中选择你的显卡,右击选择属...
- 苹果一体机双系统怎么切换(苹果一体机双系统怎么切换按哪个键)
-
苹果一体机双系统切换方法如下:1.在苹果电脑的桌面中点击左上角的苹果图标,等待弹出序列栏。2.在弹出的下拉选项中点击系统偏好设置进入,等待跳转页面。3.跳转页面之后,在系统偏好设置的页面中点击启动磁盘...
- 2025爱奇艺vip激活码(爱奇艺会员官方激活码)
-
2022爱奇艺腾讯优酷会员,要根据具体的需求来选择。喜欢青春偶像剧类型的可以选择爱奇艺视频;喜欢一些自制综艺和自制剧的优酷视频会员是不错的选择;腾讯视频定位就是主打大IP剧和一些热门综艺的转播,一...
- ie浏览器手机版官网下载(ie游览器手机版下载)
-
如果您在使用IE浏览器时遇到无法下载的问题,以下是一些常见的解决办法:1.清除浏览器缓存:打开IE浏览器,依次点击工具(齿轮图标)->Internet选项->常规选项->...
- office2003属于什么软件(word2003属于什么软件)
-
是一套Office2003专业版的精简版,包含常用的Word、Excel、PowerPoint三个应用,使用者甚多。楼主如果有需要,请上电脑在本帖下载我的附件。我见过最多的,是2013或者以上的(因为...
- 电脑鼠标设置在哪里调(电脑鼠标在哪里去调)
-
电脑点击开始,在菜单中找到“控制面板”,点击“控制面板”进入,找到“鼠标”点击进入在打开的窗口中选择“指针”,选择指针样式,可点击浏览,找到文件夹下,查看哪些指针可选择。可按路径把喜欢的图标放进去找到...
- ie浏览器怎么下载到电脑桌面
-
工具/材料:电脑1、首先在电脑桌面里找到这台电脑,双击将它打开。2、打开之后,在里面找到吸C盘,双击将它打开。3、然后在C盘里面找到Programfiles这个文件,将此文件打开。4、打开之后,在里...
- 主板bios没有csm选项(主板没有csm怎么办)
-
对普通用户最大的区别是,符合标准的bootloader必须为UEFI保证二进制兼容。结果:32位UEFI固件只能启动32位操作系统。64位UEFI固件只能启动64位操作系统。由于历史因素、OEM政策,...
- 电脑中病毒了怎么解决(电脑中病毒了怎么解决方法)
-
电脑因为中毒而不能上网,解决办法如下:1、首先第一步就是杀毒,对一个菜鸟而言只能用杀毒软件来杀毒了,当然,因为病毒更新往往快于杀毒软件的更新速度,所以杀毒有时候是解决不了问题的额,也就是说很多毒是杀...
- 电脑主机启动一下又灭一直反复
-
故障分析:电脑开机转一下就停然后再转这是由于电脑硬件接触不良,主板或后续硬件短路,电源故障等造成的。1、内存松了或者金手指氧化这种可能性最大,很多用户都是这种方法解决的。首先我们将内存取下,取下也十分...
- 如何设开机密码 电脑(如何设置开机电脑密码)
-
方法如下1.建立开机密码。进入BIOS系统界面,点击键盘的Del按键,点击选项中的设置用户密码。设置完毕进入高级设置,点击密码选项列表的系统密码,点击保存并推出即可;2.设置系统密码。进入系统界...
- lenovo联想官方商城(联想电脑官方网站商城)
-
www.lenovo.com.cn联想集团(下称联想)是一家成立于中国、业务遍及180个市场的全球化科技公司。联想聚焦全球化发展,树立了行业领先的多元企业文化和运营模式典范,服务全球超过10亿用户。作...
- 我的世界国际版官网(我的世界国际版如何下载)
-
官网入口:https://www.lezhin.com/这是一款很多用户都在使用的观看各种漫画内容的软件,用户在使用的过程中能够快速了解到各种自己感兴趣的漫画。1可以通过搜索引擎找到植物大战僵尸2国...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
