1.ソフトウェアのインストールと使用
ISAT は、segment anything から派生した半自動セグメンテーションおよびラベル付けソフトウェアです。github アドレス: https://github.com/yatengLG/ISAT_with_segment_anything.git
作成者の Readme に従って、対応する依存関係と対応する重みをインストールします。ソフトウェア インターフェイスは次のとおりです。
使用プロセス:
1. 画像フォルダーを選択します
2. ラベルの保存フォルダーを決定する
3. [SAM] をクリックし、ダウンロードしたモデルを選択します (ラベル付けの前に準備を完了します)。
ホットキー:
Q: ラベル付けを開始します
E: 完全なラベル付け
S: タグを保存
Z:前のステップに戻ります
A: 前の写真
D: 次の写真
マウスの左ボタン: 関心領域を選択します
マウスの右ボタン: 興味のない領域を選択します
注釈の例:
チェック マークを選択してラベル付けを完了し、S を押してラベルを保存します。ラベル ファイルは json 形式ですが、COCO 形式とは異なります。ソフトウェアには COCO 形式に変換する ISAT が付属していますが、バグなので役に立たない。
2. データ型変換
以下は別の GitHub アドレスです: GitHub - マッシュルーム-x/SegConvert: インスタンス セグメンテーション アノテーション ファイル形式変換スクリプト ツール セット
ISAT から COCO 形式への場合は、YOLO 形式への直接変換も含まれます。
以下のコード内のパスを前の画像パスとラベルのパスに変更し、ラベル名とシリアル番号を独自のカテゴリ名に変更します。
ISAT2ココ:
import json
import os
import numpy as np
from collections import defaultdict
# ISAT标注数据路径
ISAT_FOLDER = "./annotations"
# 图像所在的路径
IMAGE_FOLDER = "./images"
# COCO格式的JSON文件存放路径
# 可以自己指定,这里是直接定义在图像文件夹下
COCO_PATH = os.path.join(IMAGE_FOLDER, "coco.json")
# 定义类别名称与ID号的映射
# 需要注意的是,不需要按照ISAT的classesition.txt里面的定义来
# 可以选择部分自己需要的类别, ID序号也可以重新填写(从0开始)
category_mapping = {"color_block": 0}
# 定义COCO格式的字典
# - "info"/"description" 里面写一下,你这个是什么的数据集
coco = {
"info": {
"description": "Color Block Segmentation",
"version": "1.0",
"year": 2023,
"contributor": "",
"date_created": ""
},
"images": [],
"annotations": [],
"categories": []
}
# 填写annotations栏目
for class_id, class_name in category_mapping.items():
coco["categories"].append({"id": class_id, "name": class_name, "supercategory": "object"})
# 图像序号
image_id = 1
# 遍历所有的ISAT文件夹
for filename in os.listdir(ISAT_FOLDER):
# 判断是否为ISAT格式数据
if not filename.endswith(".json"):
continue
# 载入ISAT数据
with open(os.path.join(ISAT_FOLDER, filename), "r") as f:
isat = json.load(f)
# 获取图像名称
image_filename = isat["info"]["name"]
# 填写文件路径
image_path = os.path.join(IMAGE_FOLDER, image_filename)
image_info = {
"id": image_id,
"file_name": image_filename,
"width": isat["info"]["width"],
"height": isat["info"]["height"]
}
image_id += 1
# 添加图像信息
coco["images"].append(image_info)
# 标注序号
annotation_id = 1
# 遍历标注信息
for annotation in isat["objects"]:
# 获取类别名称
category_name = annotation["category"]
# 位置类别名称(选择跳过)
if category_name not in category_mapping:
# print(f"未知类别名称: {category_name}")
continue
# 获取类别ID
category_id = category_mapping[category_name]
# 提取分割信息
segmentation = annotation["segmentation"]
segmentation = np.uint32(segmentation)
# 转换为一行的形式 [[x1, y1, x2, y2, ..., xn, yn]]
segmentation = [(segmentation.reshape(-1)).tolist()]
# 提取面积信息
area = annotation["area"]
# 定义标注信息
annotation_info = {
"id": annotation_id,
"image_id": image_id,
"category_id": category_id,
"segmentation": segmentation,
"area": area,
"iscrowd": 0
}
# 追加到annotations列表
coco["annotations"].append(annotation_info)
# 标注编号自增1
annotation_id += 1
# 保存coco格式
with open(COCO_PATH, "w") as f:
json.dump(coco, f)
操作の結果、json ファイルが生成されます。
ISAT2YOLO:
import json
import os
# 定义类别名称与ID号的映射
# 需要注意的是,不需要按照ISAT的classesition.txt里面的定义来
# 可以选择部分自己需要的类别, ID序号也可以重新填写(从0开始)
category_mapping = {"color_block": 0}
# ISAT格式的实例分割标注文件
ISAT_FOLDER = "./annotations/"
# YOLO格式的实例分割标注文件
YOLO_FOLDER = "./labels"
# 创建YoloV8标注的文件夹
if not os.path.exists(YOLO_FOLDER):
os.makedirs(YOLO_FOLDER)
# 载入所有的ISAT的JSON文件
for filename in os.listdir(ISAT_FOLDER):
if not filename.endswith(".json"):
# 不是json格式, 跳过
continue
# 载入ISAT的JSON文件
with open(os.path.join(ISAT_FOLDER, filename), "r") as f:
isat = json.load(f)
# 提取文件名(不带文件后缀)
image_name = filename.split(".")[0]
# Yolo格式的标注文件名, 后缀是txt
yolo_filename = f"{image_name}.txt"
# 写入信息
with open(os.path.join(YOLO_FOLDER, yolo_filename), "w") as f:
# 获取图像信息
# - 图像宽度
image_width = isat["info"]["width"]
# - 图像高度
image_height = isat["info"]["height"]
# 获取实例标注数据
for annotation in isat["objects"]:
# 获取类别名称
category_name = annotation["category"]
# 如果不在类别名称字典里面,跳过
if category_name not in category_mapping:
continue
# 从字典里面查询类别ID
category_id = category_mapping[category_name]
# 提取分割信息
segmentation = annotation["segmentation"]
segmentation_yolo = []
# 遍历所有的轮廓点
for segment in segmentation:
# 提取轮廓点的像素坐标 x, y
x, y = segment
# 归一化处理
x_center = x/image_width
y_center = y/image_height
# 添加到segmentation_yolo里面
segmentation_yolo.append(f"{x_center:.4f} {y_center:.4f}")
segmentation_yolo_str = " ".join(segmentation_yolo)
# 添加一行Yolo格式的实例分割数据
# 格式如下: class_id x1 y1 x2 y2 ... xn yn\n
f.write(f"{category_id} {segmentation_yolo_str}\n")
操作の結果は、すべてのラベルに対応する txt ファイルです。
最初の数値はラベル番号で、次の数値はラベル点の正規化された座標です。
3. トレーニング セット、検証セット、テスト セットを分割します。
コード: main 関数のデフォルトに対応するカスタム パスを入力します。
# 将图片和标注数据按比例切分为 训练集和测试集
import shutil
import random
import os
import argparse
# 检查文件夹是否存在
def mkdir(path):
if not os.path.exists(path):
os.makedirs(path)
def main(image_dir, txt_dir, save_dir):
# 创建文件夹
mkdir(save_dir)
images_dir = os.path.join(save_dir, 'images')
labels_dir = os.path.join(save_dir, 'labels')
img_train_path = os.path.join(images_dir, 'train')
img_test_path = os.path.join(images_dir, 'test')
img_val_path = os.path.join(images_dir, 'val')
label_train_path = os.path.join(labels_dir, 'train')
label_test_path = os.path.join(labels_dir, 'test')
label_val_path = os.path.join(labels_dir, 'val')
mkdir(images_dir);mkdir(labels_dir);mkdir(img_train_path);mkdir(img_test_path);mkdir(img_val_path);mkdir(label_train_path);mkdir(label_test_path);mkdir(label_val_path);
# 数据集划分比例,训练集75%,验证集15%,测试集15%,按需修改
train_percent = 0.8
val_percent = 0.1
test_percent = 0.1
total_txt = os.listdir(txt_dir)
num_txt = len(total_txt)
list_all_txt = range(num_txt) # 范围 range(0, num)
num_train = int(num_txt * train_percent)
num_val = int(num_txt * val_percent)
num_test = num_txt - num_train - num_val
train = random.sample(list_all_txt, num_train)
# 在全部数据集中取出train
val_test = [i for i in list_all_txt if not i in train]
# 再从val_test取出num_val个元素,val_test剩下的元素就是test
val = random.sample(val_test, num_val)
print("训练集数目:{}, 验证集数目:{},测试集数目:{}".format(len(train), len(val), len(val_test) - len(val)))
for i in list_all_txt:
name = total_txt[i][:-4]
srcImage = os.path.join(image_dir, name+'.jpg')
srcLabel = os.path.join(txt_dir, name + '.txt')
if i in train:
dst_train_Image = os.path.join(img_train_path, name + '.jpg')
dst_train_Label = os.path.join(label_train_path, name + '.txt')
shutil.copyfile(srcImage, dst_train_Image)
shutil.copyfile(srcLabel, dst_train_Label)
elif i in val:
dst_val_Image = os.path.join(img_val_path, name + '.jpg')
dst_val_Label = os.path.join(label_val_path, name + '.txt')
shutil.copyfile(srcImage, dst_val_Image)
shutil.copyfile(srcLabel, dst_val_Label)
else:
dst_test_Image = os.path.join(img_test_path, name + '.jpg')
dst_test_Label = os.path.join(label_test_path, name + '.txt')
shutil.copyfile(srcImage, dst_test_Image)
shutil.copyfile(srcLabel, dst_test_Label)
if __name__ == '__main__':
"""
python split_datasets.py --image-dir my_datasets/color_rings/imgs --txt-dir my_datasets/color_rings/txts --save-dir my_datasets/color_rings/train_data
"""
parser = argparse.ArgumentParser(description='split datasets to train,val,test params')
parser.add_argument('--image-dir', default = '', type=str, help='image path dir')
parser.add_argument('--txt-dir', default = '', type=str, help='txt path dir')
parser.add_argument('--save-dir', default = '', type=str, help='save dir')
args = parser.parse_args()
image_dir = args.image_dir
txt_dir = args.txt_dir
save_dir = args.save_dir
main(image_dir, txt_dir, save_dir)
完全なデータセットのパーティショニング
4.yolov5 モデルのトレーニング
dataフォルダー内にyamlファイルが入っており、以下の図がdata/coco128-seg.yamlの内容です。
path: --save-dir セグメント化されたイメージが上に保存されているパスです。
Train、val、test はそれぞれ画像内のフォルダーの実際の条件に応じて入力されます。
names: txt に変換するときに上記と同じ順序で、カテゴリ名と割り当てられた添え字です。
データセットの作成を完了する
電車:
python segment/train.py --epochs 300 --data coco128-seg.yaml --weights yolov5m-seg.pt --img 640 --cfg models/segment/yolov5m-seg.yaml --batch-size 16 --device 2