鏈接:https://pan.baidu.com/s/1KkkM1rLfyiMPtYLycpnxmg?pwd=j2rd
提取碼:j2rd
--來自百度網盤超級會員V2的分享
采用數據集: https://aistudio.baidu.com/datasetdetail/130647
采用代碼:
https://github.com/jfzhang95/pytorch-deeplab-xception
本文會講解兩種方法:一種是使用開源數據集(不是deeplab支持的數據集)完成在deeplab上訓練,另一種是通過標注自定義數據集來完成訓練
第一種用開源數據集訓練
將carvana image masking challenge數據集轉化為Pascal VOC 格式
以下講述你需要更改的一些地方以及怎么更改
首先要看你的模型支持什么樣的數據集,一般通過train.py文件或者通過github文檔查看
或者通過train.py
可見deeplab支持這4種VOC, SBD, Cityscapes and COCO datasets
要自定義數據集就需要把你的數據集轉化以上4種即可,本文是將數據集轉化為VOC 格式
下載數據集https://aistudio.baidu.com/datasetdetail/130647后,結構如下
也就是這個數據集只有train_mask和train_hq,test一般都是沒有模板的,在語義分割中test是不需要模板的,其他的數據集也是這樣,test只用來看最后的效果。
VOC 格式如下:
也就是需要提供JPEGImages、SegmentationClass、ImageSets
或者你可以查看代碼中
通過這個代碼也大概知道需要提供什么文件夾
整理好需求,然后直接跟deepseek進行溝通:
將需求拆解開來一步一步來,
1、首先將carvana image masking challenge的train_mask中的git后綴改成png
2、因為VOC 格式的標簽和圖像的名稱是對應的,所以需要將carvana image masking challenge的_mask.gif改為.png
然后他會生成一個代碼,你可以跑一下,看是否解決問題了,如果這個問題解決了就解決下一個需求
也就是目前解決了SegmentationClass,接下來需要解決JPEGImages
?
因為carvana image masking challenge的train_hq的圖片格式也是jpg,所以你只需要將train_hq文件夾的名稱改成JPEGImages即可
也就是解決了SegmentationClass、JPEGImages,接下來解決ImageSets
然后生成新的代碼,就完成了數據集的格式轉化。
也就是這個過程你主要做的是怎么將需求轉化成多個步驟,一步一步完成需求就可以,以后不管是要轉化成coco格式還是Cityscapes 都可以用這種方法。
比如Cityscapes 我可能先將train_hq拆分成train文件夾、val文件夾、test文件夾,train_masks也是拆分成3個文件夾,并且圖片和標簽的路徑需要一致,名稱需要根據Cityscapes 定義。然后我們知道Cityscapes 的train、val下還會有城市名稱,我們隨便起一個城市名稱北京,路徑格式對應就可以
最后我的carvana image masking challenge就轉化成這種Cityscapes 風格
說話carvana image masking challenge轉化為VOC 格式,我們已經完成了所有步驟,我把這個程序命名為了拆分.py,運行我們的程序“拆分.py”
第二種方法通過標注訓練自定數據集
一、準備數據集
1. 數據集文件夾目錄
-
ImageSets?文件夾內還有一個文件夾Segmentation,里面存放的是訓練集、測試集txt文件
-
JPEGImages?存放原圖image
-
SegmentationClass?存放原圖對應的mask,要和JPEGImages里的圖片一一對應
2. yolo格式轉json格式
我使用的標注方式是:?Sign in to Roboflow?,大大減少了標注時間,推薦!!!
導出yolo格式之后進行轉json格式,代碼如下
import json
import os# 輸入TXT標注文件夾路徑
txt_folder_path = "E:/VScode project/pytorch-deeplab-xception-master111/Seg552/txt/"
# 輸出JSON文件夾路徑
json_folder_path = "E:/VScode project/pytorch-deeplab-xception-master111/Seg552/json/"# 確保輸出文件夾存在
os.makedirs(json_folder_path, exist_ok=True)# 類別ID映射(如有需要可以修改)
label_mapping = {1: "ripe fruits", 0: "branch"}# 遍歷TXT文件夾
for txt_file in os.listdir(txt_folder_path):if txt_file.endswith('.txt'):txt_file_path = os.path.join(txt_folder_path, txt_file)json_file_path = os.path.join(json_folder_path, txt_file.replace('.txt', '.json'))# JSON模板json_data = {"version": "5.2.1","flags": {},"shapes": [],"imagePath": txt_file.replace('.txt', '.jpg'), # 假設圖像名與TXT文件名相同"imageHeight": 1080,"imageWidth": 1920}# 解析TXT文件并構造JSON結構with open(txt_file_path, 'r') as file:for line in file:# 分割類別ID和坐標數據parts = line.strip().split()class_id = int(parts[0]) # 類別IDlabel = label_mapping.get(class_id, "unknown") # 類別名稱coordinates = list(map(float, parts[1:])) # 坐標數據# 將坐標數據轉換為(x, y)點對,并按比例轉換為實際像素位置points = []for i in range(0, len(coordinates), 2):x = coordinates[i] * json_data["imageWidth"]y = coordinates[i + 1] * json_data["imageHeight"]points.append([x, y])# 添加標注信息到JSONshape_data = {"label": label,"points": points,"group_id": None,"description": "","shape_type": "polygon","flags": {}}json_data["shapes"].append(shape_data)# 保存為JSON文件with open(json_file_path, 'w') as json_file:json.dump(json_data, json_file, indent=2)print(f"已成功將 {txt_file} 轉換為 JSON 文件:{json_file_path}")
3. 文件夾重命名
雖然用網頁標注導出來的image和TXT文件的名稱是一致的,但為了避免在后續格式轉換中出現沖突,現在需要將image圖片和txt文件重新命名。相應代碼:
import os# 文件夾路徑
folder1 = 'E:/VScode project/pytorch-deeplab-xception-master111/Seg552/txt/' # 替換為您的文件夾路徑
folder2 = 'E:/VScode project/pytorch-deeplab-xception-master111/Seg552/img/' # 替換為您的文件夾路徑# 獲取文件名列表
files1 = os.listdir(folder1)
files2 = os.listdir(folder2)# 對文件進行排序,確保順序一致
files1.sort()
files2.sort()# 確保兩個文件夾的文件數相同
if len(files1) != len(files2):print("警告:兩個文件夾的文件數量不同!")# 重命名第一個文件夾的文件
for idx, filename in enumerate(files1):new_name = f"{idx:03d}{os.path.splitext(filename)[1]}" # 保留后綴os.rename(os.path.join(folder1, filename), os.path.join(folder1, new_name))# 重命名第二個文件夾的文件
for idx, filename in enumerate(files2):new_name = f"{idx:03d}{os.path.splitext(filename)[1]}"os.rename(os.path.join(folder2, filename), os.path.join(folder2, new_name))print("重命名完成。")
4. json格式轉mask圖片
import argparse
import base64
import json
import os
import os.path as osp
import imgviz
import PIL.Image
from labelme.logger import logger
from labelme import utils
import glob
import yamldef main():logger.warning("This script is aimed to demonstrate how to convert the ""JSON file to a single image dataset.")logger.warning("It will handle multiple JSON files to generate a ""real-use dataset.")parser = argparse.ArgumentParser()parser.add_argument("--json_dir", required=True)parser.add_argument("-o", "--out", required=True)args = parser.parse_args()json_dir = args.json_diroutput_dir = args.outif osp.isfile(json_dir):json_list = [json_dir] if json_dir.endswith('.json') else []else:json_list = glob.glob(os.path.join(json_dir, '*.json'))for json_file in json_list:logger.info(f"Processing file: {json_file}")json_name = osp.basename(json_file).split('.')[0]out_dir = osp.join(output_dir, json_name)if not osp.exists(out_dir):os.makedirs(out_dir)try:data = json.load(open(json_file))except Exception as e:logger.error(f"Error loading JSON file {json_file}: {e}")continue # Skip to the next fileimageData = data.get("imageData")if not imageData:image_filename = osp.basename(data["imagePath"])imagePath = osp.join("E:/VScode project/pytorch-deeplab-xception-master111/Seg552/JPEGImages", image_filename)try:with open(imagePath, "rb") as f:imageData = f.read()imageData = base64.b64encode(imageData).decode("utf-8")except FileNotFoundError:logger.error(f"File not found: {imagePath}")continue # Skip to the next JSON fileexcept Exception as e:logger.error(f"Error reading image file {imagePath}: {e}")continuetry:img = utils.img_b64_to_arr(imageData)label_name_to_value = {"_background_": 0}for shape in sorted(data["shapes"], key=lambda x: x["label"]):label_name = shape["label"]if label_name in label_name_to_value:label_value = label_name_to_value[label_name]else:label_value = len(label_name_to_value)label_name_to_value[label_name] = label_valuelbl, _ = utils.shapes_to_label(img.shape, data["shapes"], label_name_to_value)label_names = [None] * (max(label_name_to_value.values()) + 1)for name, value in label_name_to_value.items():label_names[value] = namelbl_viz = imgviz.label2rgb(lbl, imgviz.asgray(img), label_names=label_names, loc="rb")# Save files to corresponding subdirectoryPIL.Image.fromarray(img).save(osp.join(out_dir, "img.png"))utils.lblsave(osp.join(out_dir, "label.png"), lbl)PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, "label_viz.png"))with open(osp.join(out_dir, "label_names.txt"), "w") as f:for lbl_name in label_names:f.write(str(lbl_name if lbl_name is not None else "unknown") + "\n")yaml_data = {"label_name_to_value": label_name_to_value,"label_names": label_names}with open(osp.join(out_dir, "labels.yaml"), "w") as yaml_file:yaml.dump(yaml_data, yaml_file)logger.info(f"Saved to: {out_dir}")except Exception as e:logger.error(f"Error processing file {json_file}: {e}")if __name__ == "__main__":main()
運行指令為:
python My_json_to_dataset.py --json_dir "E:/VScode project/pytorch-deeplab-xception-master111/Seg552/json" -o "E:/VScode project/pytorch-deeplab-xception-master111/Seg552/labelme_json"
生成的文件為
需要將labelme_json文件下的每個文件中的label.png文件重新命名,相應代碼:
import os# 替換為你的json文件夾所在的目錄
json_dir = "E:/VScode project/pytorch-deeplab-xception-master111/Seg552/labelme_json"for root, dirs, files in os.walk(json_dir):for dr in dirs:file_dir = os.path.join(root, dr)# 確認label.png文件存在label_file = os.path.join(file_dir, 'label.png')if os.path.exists(label_file):# 將文件夾名分割,得到原圖名original_name = dr.split('_')[0] + '.png'new_file_name = os.path.join(file_dir, original_name)# 執行重命名操作os.rename(label_file, new_file_name)print(f"Renamed '{label_file}' to '{new_file_name}'")
最后將提取出所有文件夾中的000.png?,?
并放在指定目錄中,相應代碼:
import os
from shutil import copyfilefor root, dirs, names in os.walk("E:/VScode project/pytorch-deeplab-xception-master111/Seg552/labelme_json"): # 改成你自己的json文件夾所在的目錄for dr in dirs:file_dir = os.path.join(root, dr)print(dr)file = os.path.join(file_dir, dr + '.png')print(file)new_name = dr.split('_')[0] + '.png'new_file_name = os.path.join(file_dir, new_name)print(new_file_name)tar_root = 'E:/VScode project/pytorch-deeplab-xception-master111/Seg552/Segmentationclass' # 目標路徑tar_file = os.path.join(tar_root, new_name)copyfile(new_file_name, tar_file)
該代碼運行得到的文件,就是我們所需要的SegmentationClass
5. 生成txt文件
生成的訓練集txt和驗證集txt,里面的圖片名稱(?去掉后綴?)的分配是隨機的。相應代碼:
import os
import random# 設置圖像目錄和輸出目錄
image_dir = 'E:/VScode project/pytorch-deeplab-xception-master111/Seg552/JPEGImages' # 替換為你的圖像目錄
output_dir = 'E:/VScode project/pytorch-deeplab-xception-master111/Seg552/ImageSets/Segmentation' # 替換為輸出目錄# 獲取所有圖像文件名(去掉后綴)
image_files = [f.split('.')[0] for f in os.listdir(image_dir) if f.endswith(('.jpg', '.png'))]# 隨機打亂圖像文件名
random.shuffle(image_files)# 分割訓練集和驗證集
train_files = image_files[:int(len(image_files) * 0.8)] # 80% 為訓練集
val_files = image_files[int(len(image_files) * 0.8):] # 20% 為驗證集# 寫入 train.txt
with open(os.path.join(output_dir, 'train.txt'), 'w') as f:for file in train_files:f.write(f"{file}\n")# 寫入 val.txt
with open(os.path.join(output_dir, 'val.txt'), 'w') as f:for file in val_files:f.write(f"{file}\n")print("train.txt 和 val.txt 已生成。")
?
修改deeplab代碼
然后就是改代碼,train.py中的dataset改成我們自定義的llmCver數據集,名稱隨便起
修改訓練層數和學習率,當然這個可能不需要修改,因為在上面parser.add_argument也有個層數和學習率,但是我看到了以防萬一也改動了。
修改mypath.py,加入你的路徑
修改dataloaders\utils.py,因為我自定義的數據集只有兩個類別
?
def decode_segmap(label_mask, dataset, plot=False):我們加入了label_colours = get_llmCver_labels(),
也就是我們調用了get_llmCver_labels()函數,我們需要在下面把get_llmCver_labels()的定義寫清楚。
怎么定義你可以get_cityscapes_labels和get_pascal_labels函數怎么寫的,你模仿著寫。
我看到carvana image masking challenge只有兩個類別,車和背景,也就是模板里面只有[0, 0, 0]和[255, 255, 255]兩個顏色,所以我的get_llmCver_labels函數里就定義了這兩個顏色,[0, 0, 0]存儲第一位,所以他是對應類別0,[255, 255, 255]存儲第二位對應類別1。這樣代碼就區分出了兩種類別。
carvana image masking challenge數據集有多少個類別可以直接問ai,這種信息類的ai一般通過網絡信息整理會找的比人快。或者根據經驗判斷。
dataloaders\datasets\llmCver.py在這個路徑下加入llmCver.py,用于代碼解析自定義數據集路徑信息。這個代碼怎么寫?可以參考pascal.py(因為我們模仿的是pascal voc,所以voc有什么代碼我們跟著做就可以)
復制pascal.py后將改一下文件名稱,我改成了llmCver.py
下面的if name == '__main__':里的函數也需要改一下,但是不改也可以。
順便講一下if name == '__main__',假如我們在做一個機器人項目,那么我們會將這個項目手.py、腳.py、頭.py等等還有一個整體調用.py。我做到最后肯定是調用整體調用.py就能完成整個機器人的運動,這時候只會用到整體調用.py里的if name == '__main__',而手.py、腳.py、頭.py里面的if name == '__main__'是不會執行,因為此時整體調用.py是主函數。
當我們的手部出現異常了,為了調試手部的一些功能我們就只會調用手.py,這個時候我們的手.py是主函數,所以手.py里面的if name == '__main__'會生效,也就是我們每次運行程序最多只有一個程序能調用if name == '__main__'。你運行哪個程序,哪個程序里的if name == '__main__'就會生效。
dataloaders\__init__.py中加入
修改完基本就差不多了
然后修改train.py里面的參數,根據自己想要用什么主干網,多少學習率這些都不是固定,可以根據自己想法來,配置完直接按下右上角的運行鍵就可以跑起來了。
我的程序有個bug,不知道是本來就有的還是,就是訓練的時候看不到進度,但是不影響訓練。
File "d:\YOLO\pytorch-deeplab-xception-master\train.py", line 305, in <module> main() File "d:\YOLO\pytorch-deeplab-xception-master\train.py", line 298, in main trainer.training(epoch) File "d:\YOLO\pytorch-deeplab-xception-master\train.py", line 115, in training self.summary.visualize_image(self.writer, self.args.dataset, image, target, output, global_step) File "d:\YOLO\pytorch-deeplab-xception-master\utils\summaries.py", line 18, in visualize_image grid_image = make_grid(decode_seg_map_sequence(torch.max(output[:3], 1)[1].detach().cpu().numpy(), File "D:\APP\conda\envs\yolov5prune\lib\site-packages\torch\utils\_contextlib.py", line 115, in decorate_context return func(*args, **kwargs) TypeError: make_grid() got an unexpected keyword argument 'range'
如果報了這個錯誤是因為你的torch版本太高,修改一下程序就可以了
修改pytorch-deeplab-xception-master\utils\summaries.py
把range去掉就可以了
# 處理輸出圖像grid_image = make_grid(decode_seg_map_sequence(torch.max(output[:3], 1)[1].detach().cpu().numpy(), dataset=dataset),nrow=3, normalize=False, scale_each=False)writer.add_image('Predicted label', grid_image, global_step)# 處理目標圖像grid_image = make_grid(decode_seg_map_sequence(torch.squeeze(target[:3], 1).detach().cpu().numpy(), dataset=dataset),nrow=3, normalize=False, scale_each=False)