目錄
參考視頻:
標注
JSON轉為TXT
訓練
驗證
參考視頻:
使用 Yolov8 自定義數據集進行圖像分割_嗶哩嗶哩_bilibili
標注
?數據集:
我使用的是一些蘋果數據集,可以在我的csdn資源中下載:
https://download.csdn.net/download/2403_83182682/90405543?spm=1001.2014.3001.5503
這里標注使用的 labelme 標注軟件,下載和使用都很簡單,下載需要打開 cmd 控制臺,輸入下載代碼:
pip install labelme
labelme
輸入 labelme 打開軟件頁面:
中文版的自己玩玩就會了,常用按鈕如下
點擊【打開目錄】,輸入蘋果文件夾,點擊【創建多邊形】,繪制多邊形,點擊保存,然后繼續繪制下一張
標記完成后是這樣的
需要將 jpg 文件和 json文件分別放入文件夾中,方便后面數據轉換。
JSON轉為TXT
通過 LabelMe 工具繪制多邊形標注后生成的 JSON 文件是一種結構化的數據文件,它遵循了一定的格式來存儲圖像標注信息。但是 yolov8 官方規定需要的是標注文件,即 .txt 文件,我這里提供一段 json 轉 txt? 文件的 Python 代碼:
# -*- coding: utf-8 -*-
from tqdm import tqdm
import shutil
import random
import os
import argparse
from collections import Counter
import yaml
import jsondef mkdir(path):if not os.path.exists(path):os.makedirs(path)def convert_label_json(json_dir, save_dir, classes):json_paths = os.listdir(json_dir)classes = classes.split(',')mkdir(save_dir)for json_path in tqdm(json_paths):# for json_path in json_paths:path = os.path.join(json_dir, json_path)with open(path, 'r') as load_f:json_dict = json.load(load_f)h, w = json_dict['imageHeight'], json_dict['imageWidth']# save txt pathtxt_path = os.path.join(save_dir, json_path.replace('json', 'txt'))txt_file = open(txt_path, 'w')for shape_dict in json_dict['shapes']:label = shape_dict['label']label_index = classes.index(label)points = shape_dict['points']points_nor_list = []for point in points:points_nor_list.append(point[0] / w)points_nor_list.append(point[1] / h)points_nor_list = list(map(lambda x: str(x), points_nor_list))points_nor_str = ' '.join(points_nor_list)label_str = str(label_index) + ' ' + points_nor_str + '\n'txt_file.writelines(label_str)def get_classes(json_dir):'''統計路徑下 JSON 文件里的各類別標簽數量'''names = []json_files = [os.path.join(json_dir, f) for f in os.listdir(json_dir) if f.endswith('.json')]for json_path in json_files:with open(json_path, 'r') as f:data = json.load(f)for shape in data['shapes']:name = shape['label']names.append(name)result = Counter(names)return resultdef main(image_dir, json_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_val_path = os.path.join(images_dir, 'val')label_train_path = os.path.join(labels_dir, 'train')label_val_path = os.path.join(labels_dir, 'val')mkdir(images_dir)mkdir(labels_dir)mkdir(img_train_path)mkdir(img_val_path)mkdir(label_train_path)mkdir(label_val_path)# 數據集劃分比例,訓練集75%,驗證集15%,測試集15%,按需修改train_percent = 0.90val_percent = 0.10total_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)train = random.sample(list_all_txt, num_train)# 在全部數據集中取出trainval = [i for i in list_all_txt if not i in train]# 再從val_test取出num_val個元素,val_test剩下的元素就是test# val = random.sample(list_all_txt, num_val)print("訓練集數目:{}, 驗證集數目:{}".format(len(train), 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)obj_classes = get_classes(json_dir)classes = list(obj_classes.keys())# 編寫yaml文件classes_txt = {i: classes[i] for i in range(len(classes))} # 標簽類別data = {'path': os.path.join(os.getcwd(), save_dir),'train': "images/train",'val': "images/val",'names': classes_txt,'nc': len(classes)}with open(save_dir + '/segment.yaml', 'w', encoding="utf-8") as file:yaml.dump(data, file, allow_unicode=True)print("標簽:", dict(obj_classes))if __name__ == "__main__":"""python json2txt_nomalize.py --json-dir my_datasets/color_rings/jsons --save-dir my_datasets/color_rings/txts --classes "cat,dogs""""classes_list = 'apple' # 類名parser = argparse.ArgumentParser(description='json convert to txt params')parser.add_argument('--image-dir', type=str, default='D:\OneDrive\桌面\yolov8-segment\datasets\segment\images', help='圖片地址')parser.add_argument('--json-dir', type=str, default='D:\OneDrive\桌面\yolov8-segment\datasets\segment\json', help='json地址')parser.add_argument('--txt-dir', type=str, default='D:\OneDrive\桌面\yolov8-segment\datasets\segment\\txt', help='保存txt文件地址')parser.add_argument('--save-dir', default='D:\OneDrive\桌面\yolov8-segment\datasets\segment\seg', type=str, help='保存最終分割好的數據集地址')parser.add_argument('--classes', type=str, default=classes_list, help='classes')args = parser.parse_args()json_dir = args.json_dirtxt_dir = args.txt_dirimage_dir = args.image_dirsave_dir = args.save_dirclasses = args.classes# json格式轉txt格式convert_label_json(json_dir, txt_dir, classes)# 劃分數據集,生成yaml訓練文件main(image_dir, json_dir, txt_dir, save_dir)
第 90 行左右可以修改數據集劃分比例,默認是 90% 訓練集,10%驗證集。
第147到150行依次為圖片地址、json文件地址、保存txt文件地址、分割好的數據集地址
我的項目創建目錄如下(都是可以自己修改的):
運行代碼
運行成功,訓練集是19張圖片,驗證集是3張,檢測到的標簽總數量為 53。
運行成功后會幫您創建 Yolov8 訓練所需的文件格式,并且將 txt文件放入正確的位置:
訓練
訓練環境使用的GPU,需要配置的可以看我前文:
CV -- 基于GPU版顯卡CUDA環境+Pycharm YOLOv8 檢測-CSDN博客
訓練代碼如下:
from torch.cuda import devicefrom ultralytics import YOLOmodel = YOLO('D:\OneDrive\桌面\yolov8-segment\weights\yolov8n-seg.pt')model.train(data='D:\OneDrive\桌面\yolov8-segment\datasets\segment\seg\segment.yaml',epochs=300, #訓練輪次imgsz=640, #輸入圖片尺寸(會轉換為該尺寸)batch=4, #每次訓練的批量device='cuda:0', #使用GPU訓練workers=0 #windows GPU訓練需加上該參數,否則會報錯)
print("訓練結束!")
這里使用的是預訓練模型,yolov8n-seg.pt,大家可以在我的資源中獲取到:
https://download.csdn.net/download/2403_83182682/90405472?spm=1001.2014.3001.5503
訓練結束后會生成一些圖表:
輸出文件說明:
F1-置信度曲線 (BoxF1_curve.png)
觀察方法: F1分數是模型準確度的度量,結合了精確度和召回率。在這個圖表中,您應該尋找F1分數最高的點,該點對應的置信度閾值通常是模型最佳的工作點。
精確度-置信度曲線 (BoxP_curve.png)
觀察方法: 精確度代表了模型預測為正類的樣本中實際為正類的比例。在該曲線中,應關注隨置信度增加,精確度如何提高,以及在哪個置信度水平上精確度開始下降,這有助于確定閾值設定。
精確度-召回率曲線 (BoxPR_curve.png)
觀察方法: 該曲線展示了精確度與召回率之間的權衡。理想的模型應在高精確度和高召回率處達到平衡。通常查看曲線下面積來評估模型整體性能。
召回率-置信度曲線 (BoxR_curve.png)
觀察方法: 召回率是指所有正類中模型正確預測的比例。這個圖表中,召回率通常隨著置信度閾值的降低而增加。理想的置信度閾值通常是召回率較高,但置信度不過低的點。
混淆矩陣 (confusion_matrix.png)
觀察方法: 查看矩陣的對角線,對角線上的數值越高表示分類結果越準確。同時觀察非對角線元素,了解哪些類別容易被誤分類。
標準化混淆矩陣 (confusion_matrix_normalized.png)
觀察方法: 與非標準化混淆矩陣類似,但通過標準化可以更容易地比較不同類別之間的分類準確率,特別是在類別樣本量不均勻的情況下。
標簽分布 (labels.jpg)
觀察方法: 柱狀圖部分顯示了每個類別的實例數量,有助于了解數據集中各類別的分布情況。散點圖部分可以顯示樣本的位置分布,有助于了解樣本在輸入空間的分布特性。
標簽相關圖 (labels_correlogram.jpg)
觀察方法: 相關圖顯示了數據標簽之間的相關性,深色的格子表示較高的正相關,淺色表示較低的相關或負相關。這有助于了解不同類別之間的關系。
掩膜F1-置信度曲線 (MaskF1_curve.png)
觀察方法: 類似于F1-置信度曲線,但特別用于評估模型在像素級分類或分割任務中的性能。尋找曲線中F1得分最高的點來確定最佳的置信度閾值。
精確度-召回率曲線 (Precision-Recall Curve) (MaskPR_curve.png)
如何觀察理解: 此圖表展示了在不同召回率水平上模型精確度的變化。藍色的線表示所有類別的平均精確度。曲線下的面積(AUC)越大,模型性能越好。理想狀態是曲線靠近右上角,即高召回率和高精確度。
召回率-置信度曲線 (Recall-Confidence Curve) (MaskR_curve.png)
如何觀察理解: 該圖標展示了模型在不同置信度閾值下召回率的變化。您應關注在召回率保持高的同時,置信度閾值的選擇。最佳操作點通常是召回率開始顯著下降之前的置信度值。
訓練和驗證指標圖 (results.png)
如何觀察理解: 這張圖顯示了多個指標的訓練和驗證過程,其中包括損失函數的變化和性能指標如精確度和mAP。下降的損失和上升的性能指標通常表明模型在學習過程中正在改進。平滑的曲線有助于識別趨勢。
損失和性能指標圖
如何觀察理解: 類似于上一個圖表,這個可能包含了不同的損失和性能指標。每個小圖標展示了訓練過程中的具體方面,如框體損失、分割損失、分類損失等。這有助于診斷模型在哪些方面表現良好,在哪些方面可能需要進一步優化。
Weights文件:這是一個模型權重文件,通常以.pt(PyTorch模型)格式保存。它包含了經過訓練的神經網絡的所有參數和權重。這個文件是模型訓練過程的直接產物,用于后續的圖像識別和分析任務。
Args.yaml文件:這個文件通常包含了模型訓練時使用的配置參數。它詳細記錄了訓練過程中使用的所有設置,如學習率、批大小、訓練輪數等。這個文件的目的是為了提供一個清晰的訓練配置概覽,使得訓練過程可以被復現或調整。
左半部分是損失函數圖,損失函數下降,可能并不能說明訓練結果很好,但如果損失函數上升,那訓練結果一定不好,說明你的數據可能出現了很大的錯誤,或者一些其他不好的事情正在發生。
通過對測試數據的詳細分析,我們可以觀察到原始標注與模型預測的掩膜之間存在差異很小,這實際上體現了模型具備出色的語義分割能力。具體而言,盡管在某些局部細節上可能存在細微偏差,但模型整體能夠準確捕捉并區分不同對象的邊界和區域,展示了其在復雜場景下對圖像內容的強大理解能力。這種高水平的分割精度不僅驗證了模型訓練的有效性,還為其在實際應用中的性能提供了有力保障。
驗證
可以使用我們訓練好的模型,對蘋果圖片進行圖像分割,訓練好的模型的權重文件中有兩個模型文件,第一個是最好的模型,第二個是最后一次訓練的模型,一般使用第一個模型。
訓練代碼:
from ultralytics import YOLO
import cv2
import numpy as np# 模型路徑和圖像路徑
model_path = 'D:/OneDrive/桌面/yolov8-segment/runs/segment/train8/weights/best.pt'
image_path = 'D:/OneDrive/桌面/apple2.jpg'# 加載模型并進行預測
model = YOLO(model_path,task='segment')
results = model.predict(source=image_path,save=True,show=True)
成功運行后就能得到分割圖片:
可以看見分割效果是有的,但也有點瑕疵,把杯子也識別成 apple 了,可能是因為數據集太少或者標注問題。
當然,也可使用代碼將各個 apple 的掩膜提取出來:
from ultralytics import YOLO
import cv2
import numpy as np# 模型路徑和圖像路徑
model_path = 'D:/OneDrive/桌面/yolov8-segment/runs/segment/train8/weights/best.pt'
image_path = 'D:/OneDrive/桌面/apple2.jpg'# 加載模型并進行預測
model = YOLO(model_path,task='segment')
results = model.predict(source=image_path,save=True,show=True)# 讀取圖像
img = cv2.imread(image_path)
H, W, _ = img.shape
print(img.shape)# # 遍歷每個結果中的掩碼
for i, result in enumerate(results):for j, mask in enumerate(result.masks.data):# 將mask從GPU移動到CPU,并轉換為numpy數組mask = mask.cpu().numpy()# 如果mask是多維的,選擇第一個通道(假設單通道)if len(mask.shape) > 2:mask = mask[0]# 歸一化到0-255范圍,并轉換為uint8類型mask = (mask * 255).astype(np.uint8)# 調整大小以匹配原圖尺寸mask_resized = cv2.resize(mask, (W, H))# 保存掩碼圖像output_path = f'./mask_{i}_{j}.png'cv2.imwrite(output_path, mask_resized)print(f"Saved {output_path}")
得到各個掩膜的 png 文件:
這些掩膜文件在某些時候非常有用。
感謝您的三連!!!