wangwenguang commited on
Commit
dac1ae0
·
verified ·
1 Parent(s): d6001fd

Delete utils

Browse files
utils/__init__.py DELETED
@@ -1 +0,0 @@
1
- #
 
 
utils/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (149 Bytes)
 
utils/__pycache__/__init__.cpython-311.pyc DELETED
Binary file (150 Bytes)
 
utils/__pycache__/__init__.cpython-38.pyc DELETED
Binary file (149 Bytes)
 
utils/__pycache__/create_exp_folder.cpython-310.pyc DELETED
Binary file (989 Bytes)
 
utils/__pycache__/create_exp_folder.cpython-311.pyc DELETED
Binary file (2.79 kB)
 
utils/__pycache__/create_exp_folder.cpython-38.pyc DELETED
Binary file (971 Bytes)
 
utils/__pycache__/dataloader.cpython-310.pyc DELETED
Binary file (3.84 kB)
 
utils/__pycache__/dataloader.cpython-311.pyc DELETED
Binary file (8.99 kB)
 
utils/__pycache__/dataloader.cpython-38.pyc DELETED
Binary file (3.81 kB)
 
utils/__pycache__/plot_results.cpython-310.pyc DELETED
Binary file (1.82 kB)
 
utils/__pycache__/plot_results.cpython-311.pyc DELETED
Binary file (3.83 kB)
 
utils/__pycache__/plot_results.cpython-38.pyc DELETED
Binary file (1.89 kB)
 
utils/__pycache__/train_and_eval.cpython-310.pyc DELETED
Binary file (6.73 kB)
 
utils/__pycache__/train_and_eval.cpython-311.pyc DELETED
Binary file (16.2 kB)
 
utils/__pycache__/utils.cpython-310.pyc DELETED
Binary file (1.61 kB)
 
utils/__pycache__/utils.cpython-311.pyc DELETED
Binary file (2.94 kB)
 
utils/__pycache__/utils.cpython-38.pyc DELETED
Binary file (1.98 kB)
 
utils/create_exp_folder.py DELETED
@@ -1,59 +0,0 @@
1
- import os
2
-
3
- def create_exp_folder():
4
- # Step 1: 创建run文件夹(如果不存在)
5
- if not os.path.exists("run"):
6
- os.mkdir("run")
7
-
8
- # Step 2: 创建train文件夹(如果不存在)
9
- train_folder = os.path.join("run", "train")
10
- if not os.path.exists(train_folder):
11
- os.mkdir(train_folder)
12
-
13
- # Step 3: 创建exp文件夹(检查是否存在)
14
- exp_folder = os.path.join(train_folder, "exp")
15
- if not os.path.exists(exp_folder):
16
- os.mkdir(exp_folder)
17
- os.mkdir(os.path.join(exp_folder, "weights")) # 创建weights文件夹
18
- return exp_folder, os.path.join(exp_folder, "weights") # 返回exp和weights文件夹路径
19
-
20
- # 如果exp文件夹已存在,则查找exp1, exp2, 等
21
- exp_num = 1
22
- while True:
23
- # 动态命名exp1, exp2, ...
24
- exp_folder_name = f"exp{exp_num}"
25
- exp_folder = os.path.join(train_folder, exp_folder_name)
26
- if not os.path.exists(exp_folder):
27
- os.mkdir(exp_folder) # 创建新的exp文件夹
28
- os.mkdir(os.path.join(exp_folder, "weights")) # 创建weights文件夹
29
- return exp_folder, os.path.join(exp_folder, "weights") # 返回exp和weights文件夹路径
30
- exp_num += 1 # 如果文件夹已存在,增加数字,继续查找下一个文件夹
31
-
32
-
33
- def create_val_exp_folder():
34
- # Step 1: 创建run文件夹(如果不存在)
35
- if not os.path.exists("run"):
36
- os.mkdir("run")
37
-
38
- # Step 2: 创建train文件夹(如果不存在)
39
- train_folder = os.path.join("run", "predict")
40
- if not os.path.exists(train_folder):
41
- os.mkdir(train_folder)
42
-
43
- # Step 3: 创建exp文件夹(检查是否存在)
44
- exp_folder = os.path.join(train_folder, "exp")
45
- if not os.path.exists(exp_folder):
46
- os.mkdir(exp_folder)
47
-
48
- # 如果exp文件夹已存在,则查找exp1, exp2, 等
49
- exp_num = 1
50
- while True:
51
- # 动态命名exp1, exp2, ...
52
- exp_folder_name = f"exp{exp_num}"
53
- exp_folder = os.path.join(train_folder, exp_folder_name)
54
- if not os.path.exists(exp_folder):
55
- os.mkdir(exp_folder) # 创建新的exp文件夹
56
- return exp_folder # 返回新创建的文件夹路径
57
- exp_num += 1 # 如果文件夹已存在,增加数字,继续查找下一个文件夹
58
-
59
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils/dataloader.py DELETED
@@ -1,157 +0,0 @@
1
- import torch
2
- from utils.utils import cvtColor, preprocess_input
3
- import os
4
- from PIL import Image
5
- import numpy as np
6
- from torch.utils.data import Dataset, DataLoader
7
- import cv2
8
-
9
- class UnetDataset(Dataset):
10
- def __init__(self, data_path, input_shape, num_classes, augmentation=True ,txt_name: str = "train.txt"):
11
- # 读取train.txt和val.txt,test.txt文件,获取训练和验证集的图像ID
12
- with open(os.path.join(data_path, "VOC2012/ImageSets/Segmentation", txt_name), "r") as f:
13
- self.annotation_lines = f.readlines()
14
-
15
- # 初始化其他参数
16
- self.length = len(self.annotation_lines) # 数据集的长度
17
- self.input_shape = input_shape # 输入图像的形状(宽和高)
18
- self.num_classes = num_classes # 类别数目
19
- self.augmentation = augmentation # 是否在训练阶段,用来控制是否使用数据增强
20
- self.data_path = data_path # 数据集路径
21
-
22
- def __len__(self):
23
- # 返回数据集的大小
24
- return self.length
25
-
26
- def __getitem__(self, index):
27
- # 读取单个样本
28
- annotation_line = self.annotation_lines[index] # 获取对应的annotation
29
- name = annotation_line.split()[0] # 获取文件名,通常是图像文件的名称
30
-
31
- # 读取JPEG图像
32
- jpg = Image.open(os.path.join(self.data_path, "VOC2012/JPEGImages", name + ".png"))
33
- # 读取PNG标签图像
34
- png = Image.open(os.path.join(self.data_path, "VOC2012/SegmentationClass", name + ".png"))
35
-
36
- # 如果是训练阶段,进行随机数据增强
37
- jpg, png = self.get_random_data(jpg, png, self.input_shape, random=self.augmentation)
38
-
39
- # 图像预处理
40
- jpg = np.transpose(preprocess_input(np.array(jpg, np.float64)), [2, 0, 1])
41
- # 标签转换成numpy数组
42
- png = np.array(png)
43
-
44
- # 将标签值大于类别数的部分设置为类别数(忽略这些区域)
45
- png[png >= self.num_classes] = self.num_classes
46
-
47
- # 将标签转换为one-hot编码
48
- seg_labels = np.eye(self.num_classes + 1)[png.reshape([-1])]
49
-
50
- # 重塑标签为目标形状
51
- seg_labels = seg_labels.reshape((int(self.input_shape[0]), int(self.input_shape[1]), self.num_classes + 1))
52
-
53
- # 返回图像、标签以及one-hot编码的标签
54
- return jpg, png, seg_labels
55
-
56
- # 生成随机数的函数
57
- def rand(self, a=0, b=1):
58
- return np.random.rand() * (b - a) + a
59
-
60
- # 对图像和标签进行随机数据增强的函数
61
- def get_random_data(self, image, label, input_shape, jitter=.3, hue=.1, sat=0.7, val=0.3, random=True):
62
- # 将图像转为RGB格式
63
- image = cvtColor(image)
64
- label = Image.fromarray(np.array(label))
65
-
66
- iw, ih = image.size # 获取图像的宽和高
67
- h, w = input_shape # 获取目标图像的高和宽
68
-
69
- if not random:
70
- # 如果不进行随机增强(例如在验证阶段)
71
- iw, ih = image.size
72
- scale = min(w / iw, h / ih) # 计算缩放比例
73
- nw = int(iw * scale) # 根据比例计算缩放后的宽
74
- nh = int(ih * scale) # 根据比例计算缩放后的高
75
-
76
- # 缩放图像并进行中心裁剪
77
- image = image.resize((nw, nh), Image.BICUBIC)
78
- new_image = Image.new('RGB', [w, h], (128, 128, 128)) # 创建一个灰色背景的图像
79
- new_image.paste(image, ((w - nw) // 2, (h - nh) // 2)) # 将缩放后的图像粘贴到目标图像中
80
-
81
- # 缩放标签图像并进行中心裁剪
82
- label = label.resize((nw, nh), Image.NEAREST) # 标签使用最近邻插值
83
- new_label = Image.new('L', [w, h], (0)) # 创建一个空白标签图像
84
- new_label.paste(label, ((w - nw) // 2, (h - nh) // 2)) # 将缩放后的标签图像粘贴到目标图像中
85
- return new_image, new_label
86
-
87
- # 获取一个新的宽高比(通过调整宽和高的比例)
88
- new_ar = iw / ih * self.rand(1 - jitter, 1 + jitter) / self.rand(1 - jitter, 1 + jitter)
89
- scale = self.rand(0.25, 2) # 随机缩放比例
90
- if new_ar < 1:
91
- nh = int(scale * h)
92
- nw = int(nh * new_ar)
93
- else:
94
- nw = int(scale * w)
95
- nh = int(nw / new_ar)
96
- image = image.resize((nw, nh), Image.BICUBIC)
97
- label = label.resize((nw, nh), Image.NEAREST)
98
-
99
- # 随机翻转图像
100
- flip = self.rand() < .5
101
- if flip:
102
- image = image.transpose(Image.FLIP_LEFT_RIGHT)
103
- label = label.transpose(Image.FLIP_LEFT_RIGHT)
104
-
105
- # 在图像周围随机添加灰色边框
106
- dx = int(self.rand(0, w - nw))
107
- dy = int(self.rand(0, h - nh))
108
- new_image = Image.new('RGB', (w, h), (128, 128, 128))
109
- new_label = Image.new('L', (w, h), (0))
110
- new_image.paste(image, (dx, dy)) # 将图像粘贴到新图像中
111
- new_label.paste(label, (dx, dy)) # 将标签粘贴到新标签中
112
- image = new_image
113
- label = new_label
114
-
115
- # 转换图像为数组
116
- image_data = np.array(image, np.uint8)
117
-
118
-
119
- r = np.random.uniform(-1, 1, 3) * [hue, sat, val] + 1 # 随机调整色调、饱和度和亮度
120
- hue, sat, val = cv2.split(cv2.cvtColor(image_data, cv2.COLOR_RGB2HSV)) # 转为HSV色域
121
- dtype = image_data.dtype # 获取数据类型
122
- x = np.arange(0, 256, dtype=r.dtype) # 获取颜色值范围
123
- lut_hue = ((x * r[0]) % 180).astype(dtype) # 应用色调变换
124
- lut_sat = np.clip(x * r[1], 0, 255).astype(dtype) # 应用饱和度变换
125
- lut_val = np.clip(x * r[2], 0, 255).astype(dtype) # 应用亮度变换
126
-
127
- # 使用查找表(LUT)应用变换
128
- image_data = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))
129
- image_data = cv2.cvtColor(image_data, cv2.COLOR_HSV2RGB) # 转换回RGB色域
130
-
131
- return image_data, label # 返回经过增强的图像和标签
132
-
133
-
134
- # DataLoader中collate_fn使用
135
- def unet_dataset_collate(batch):
136
- # 初始化三个列表,用于存储每个批次中的图像、标签和one-hot编码标签
137
- images = [] # 用来存储图像数据
138
- pngs = [] # 用来存储原始标签(通常是类别标签)
139
- seg_labels = [] # 用来存储one-hot编码的标签
140
-
141
- # 遍历当前批次中的每个样本(img, png, labels)
142
- for img, png, labels in batch:
143
- images.append(img) # 将图像添加到images列表中
144
- pngs.append(png) # 将原始标签添加到pngs列表中
145
- seg_labels.append(labels) # 将one-hot标签添加到seg_labels列表中
146
-
147
- # 将列表转换为NumPy数组,然后转换为torch张量
148
- # images的张量需要是float类型,通常用于输入图像
149
- images = torch.from_numpy(np.array(images)).type(torch.FloatTensor)
150
- # pngs的张量需要是long类型,通常用于标签索引
151
- pngs = torch.from_numpy(np.array(pngs)).long()
152
- # seg_labels的张量需要是float类型,通常用于标签的one-hot编码
153
- seg_labels = torch.from_numpy(np.array(seg_labels)).type(torch.FloatTensor)
154
-
155
- # 返回三个张量,分别对应图像、原始标签和one-hot标签
156
- return images, pngs, seg_labels
157
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils/plot_results.py DELETED
@@ -1,46 +0,0 @@
1
- import matplotlib.pyplot as plt
2
- import os
3
-
4
- def plot_training_curves(train_losses, val_losses, val_metrics_history, weights_folder):
5
- # 准备数据
6
- epochs = range(1, len(train_losses) + 1)
7
- pixel_acc_list = [m["Pixel Accuracy"] for m in val_metrics_history]
8
- mean_acc_list = [m["Mean Accuracy"] for m in val_metrics_history]
9
- mean_iou_list = [m["Mean IoU"] for m in val_metrics_history]
10
- fw_iou_list = [m["Frequency Weighted IoU"] for m in val_metrics_history]
11
-
12
- # ========================
13
- # 📈 绘制 Loss 曲线
14
- # ========================
15
- plt.figure(figsize=(8,6))
16
- plt.plot(epochs, train_losses, label="Train Loss", linewidth=2)
17
- plt.plot(epochs, val_losses, label="Val Loss", linewidth=2)
18
-
19
- plt.xlabel("Epoch", fontsize=14, fontname='Times New Roman')
20
- plt.ylabel("Loss", fontsize=14, fontname='Times New Roman')
21
- plt.xticks(fontsize=12, fontname='Times New Roman')
22
- plt.yticks(fontsize=12, fontname='Times New Roman')
23
- plt.grid(True, which='both', linestyle='--', alpha=0.5)
24
- plt.legend(prop={'family':'Times New Roman', 'size':12})
25
- plt.tight_layout()
26
- plt.savefig(os.path.join(weights_folder, "loss_curve.png"), dpi=300)
27
- plt.close()
28
-
29
- # =========================
30
- # 📈 绘制指标曲线
31
- # =========================
32
- plt.figure(figsize=(8,6))
33
- plt.plot(epochs, pixel_acc_list, label="Pixel Accuracy", linewidth=2)
34
- plt.plot(epochs, mean_acc_list, label="Mean Accuracy", linewidth=2)
35
- plt.plot(epochs, mean_iou_list, label="Mean IoU", linewidth=2)
36
- plt.plot(epochs, fw_iou_list, label="FWIoU", linewidth=2)
37
-
38
- plt.xlabel("Epoch", fontsize=14, fontname='Times New Roman')
39
- plt.ylabel("Score", fontsize=14, fontname='Times New Roman')
40
- plt.xticks(fontsize=12, fontname='Times New Roman')
41
- plt.yticks(fontsize=12, fontname='Times New Roman')
42
- plt.grid(True, which='both', linestyle='--', alpha=0.5)
43
- plt.legend(prop={'family':'Times New Roman', 'size':12})
44
- plt.tight_layout()
45
- plt.savefig(os.path.join(weights_folder, "metrics_curve.png"), dpi=300)
46
- plt.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils/train_and_eval.py DELETED
@@ -1,312 +0,0 @@
1
- import os
2
-
3
- import torch
4
- import numpy as np
5
- from model.unet_training import CE_Loss, Dice_loss, Focal_Loss
6
-
7
- from utils.utils import get_lr
8
- from torch.cuda.amp import autocast, GradScaler
9
- import time
10
-
11
-
12
- class LogColor:
13
- GREEN = "\033[1;32m"
14
- YELLOW = "\033[1;33m"
15
- RED = "\033[1;31m"
16
- RESET = "\033[0m"
17
- BLUE = "\033[1;34m"
18
-
19
-
20
- def pixel_accuracy(output, target):
21
- with torch.no_grad():
22
- _, predicted = torch.max(output, 1)
23
- correct = (predicted == target).float()
24
- correct_pixels = correct.sum().item()
25
- total_pixels = target.numel()
26
- return correct_pixels / total_pixels
27
-
28
- def mean_accuracy(output, target, num_classes):
29
- """
30
- 计算 Mean Pixel Accuracy (MPA).
31
- :param output: torch.Tensor, shape [N, C, H, W]
32
- :param target: torch.Tensor, shape [N, H, W]
33
- :param num_classes: int
34
- :return: float, mean pixel accuracy over valid classes
35
- """
36
- with torch.no_grad():
37
- # 取出每个像素的预测类别索引
38
- _, predicted = torch.max(output, dim=1) # shape [N, H, W]
39
-
40
- accuracies = []
41
- for i in range(num_classes):
42
- # 找到该类别在标签和预测中的位置
43
- target_mask = (target == i)
44
- predicted_mask = (predicted == i)
45
-
46
- # 交集:预测正确的像素数(即 TP)
47
- intersection = torch.logical_and(target_mask, predicted_mask).sum().item()
48
- total = target_mask.sum().item() # 标签中该类的总像素数
49
-
50
- if total > 0:
51
- acc = intersection / total
52
- accuracies.append(acc)
53
- # 如果该类别在 GT 中没有出现,则跳过,不计入平均
54
-
55
- # 防止所有类别都未出现
56
- if len(accuracies) == 0:
57
- return 0.0
58
- else:
59
- return sum(accuracies) / len(accuracies)
60
-
61
-
62
- # 计算Mean IoU
63
- def mean_iou(output, target, num_classes):
64
- """
65
- 计算 mean IoU,只在 target 出现的类别中取平均
66
- """
67
- with torch.no_grad():
68
- _, predicted = torch.max(output, dim=1) # (N, H, W)
69
- ious = []
70
- for i in range(num_classes):
71
- target_mask = (target == i)
72
- pred_mask = (predicted == i)
73
-
74
- intersection = torch.logical_and(target_mask, pred_mask).sum().item()
75
- union = torch.logical_or(target_mask, pred_mask).sum().item()
76
-
77
- if target_mask.sum().item() > 0: # 只对 target 中存在的类求 IoU
78
- ious.append(intersection / union if union > 0 else 0.0)
79
- if len(ious) == 0:
80
- return 0.0
81
- return sum(ious) / len(ious)
82
-
83
-
84
- # 计算Frequency Weighted IoU
85
- def frequency_weighted_iou(output, target, num_classes):
86
- with torch.no_grad():
87
- _, predicted = torch.max(output, 1)
88
- ious = []
89
- frequencies = []
90
- for i in range(num_classes):
91
- target_mask = (target == i)
92
- pred_mask = (predicted == i)
93
- intersection = torch.logical_and(target_mask, pred_mask).sum().item()
94
- union = torch.logical_or(target_mask, pred_mask).sum().item()
95
- freq = target_mask.sum().item()
96
- frequencies.append(freq)
97
- ious.append((intersection / union) if union > 0 else 0.0)
98
-
99
- total = sum(frequencies)
100
- if total == 0:
101
- return 0.0
102
- fw_iou = sum(f * iou for f, iou in zip(frequencies, ious)) / total
103
- return fw_iou
104
-
105
-
106
- def train_one_epoch(model, optimizer, train_loader, device, dice_loss, focal_loss,
107
- gpu_used, num_classes, scaler, epoch, train_epoch):
108
-
109
- # 设置类别权重参数。它是用来处理类别不平衡的问题的
110
- cls_weights = np.ones([num_classes], np.float32)
111
- epoch_loss = 0.0 # 总的训练损失
112
-
113
- # 设置模型为训练模式
114
- model_train = model.train()
115
- model_train = model_train.cuda()
116
-
117
- # 遍历训练数据
118
- for iteration, batch in enumerate(train_loader):
119
- imgs, pngs, labels = batch # 获取输入图像、标签和分割目标
120
- print(f"图像像素值范围: {pngs.min().item()} ~ {pngs.max().item()}") # 仍为0/1/2/3
121
-
122
- # 数据准备阶段:使用 `.to(device)` 自动将数据移到设备上
123
- weights = torch.tensor(cls_weights).to(device) # 转换类别权重并移动到GPU
124
- imgs = imgs.to(device)
125
- pngs = pngs.to(device)
126
- labels = labels.to(device)
127
-
128
- optimizer.zero_grad() # 清除之前的梯度
129
-
130
- # 混合精度训练
131
- if scaler is None:
132
- # 前向传播
133
- outputs = model_train(imgs)
134
-
135
- # 损失计算
136
- if focal_loss:
137
- loss = Focal_Loss(outputs, pngs, weights, num_classes=num_classes)
138
- else:
139
- loss = CE_Loss(outputs, pngs, weights, num_classes=num_classes)
140
-
141
- if dice_loss:
142
- # 如果使用Dice Loss,则加上Dice损失
143
- main_dice = Dice_loss(outputs, labels)
144
- # main_dice = Dice_loss(outputs, pngs)
145
- loss = loss + main_dice
146
-
147
- # 反向传播
148
- loss.backward()
149
- optimizer.step() # 更新模型参数
150
- else:
151
- with autocast():
152
- outputs = model_train(imgs) # 通过模型获取预测结果
153
-
154
- # 损失计算
155
- if focal_loss:
156
- loss = Focal_Loss(outputs, pngs, weights, num_classes=num_classes)
157
- else:
158
- loss = CE_Loss(outputs, pngs, weights, num_classes=num_classes)
159
-
160
- if dice_loss:
161
- main_dice = Dice_loss(outputs, labels)
162
- loss = loss + main_dice
163
-
164
- # 反向传播
165
- scaler.scale(loss).backward()
166
- scaler.step(optimizer) # 使用混合精度更新梯度
167
- scaler.update() # 更新scaler
168
-
169
- # 累加训练损失和F-score
170
- epoch_loss += loss.item()
171
-
172
- # 打印标题(每个epoch开始时打印一次)
173
- if iteration == 0: # 只在第一个 batch 打印标题
174
- print(f"{LogColor.GREEN}Epoch{LogColor.RESET}{' ' * 12}"
175
- f"{LogColor.YELLOW}data_num{LogColor.RESET}{' ' * 12}"
176
- f"{LogColor.YELLOW}GPU Mem{LogColor.RESET}{' ' * 12}"
177
- f"{LogColor.YELLOW}Loss{LogColor.RESET}{' ' * 12}"
178
- f"{LogColor.YELLOW}LR{LogColor.RESET}{' ' * 12}"
179
- f"{LogColor.YELLOW}Image_size{LogColor.RESET}{' ' * 12}"
180
- )
181
-
182
- # 每10个batch打印一次信息
183
- if iteration % 1 == 0:
184
- if len(train_loader) < 1:
185
- a = len(train_loader)
186
- else:
187
- a = 1
188
-
189
- Epoch_len = len("Epoch") + 12 - len(str(f"{epoch + 1}/{train_epoch}"))
190
- batch_len = len("data_num") + 12 - len(str(f"{iteration + a}/{len(train_loader)}"))
191
- GPU_len = len("GPU Mem") + 12 - len(str(f"{gpu_used:.2f} MB"))
192
- Loss_len = len("Loss") + 12 - len(str(f"{loss.item():.8f}"))
193
- LR_len = len("LR") + 12 - len(str(f"{get_lr(optimizer):.8f}"))
194
-
195
- # 使用 \r 在同一行更新输出
196
- print(f"\r{epoch + 1}/{train_epoch}{' ' * Epoch_len}"
197
- f"{iteration + a}/{len(train_loader)}{' ' * batch_len}"
198
- f"{gpu_used:.2f} MB{' ' * GPU_len}"
199
- f"{loss.item():.8f}{' ' * Loss_len}"
200
- f"{get_lr(optimizer):.8f}{' ' * LR_len}"
201
- f"{imgs.shape[2]}", end='', flush=True)
202
-
203
- # 每个epoch结束后打印一次
204
- print(f"{LogColor.GREEN}")
205
- time.sleep(1) # 加一点延迟,防止输出闪烁过快
206
-
207
- # ➕ 返回平均loss
208
- return epoch_loss / len(train_loader)
209
-
210
- def evaluate(model, val_loader, device, dice_loss, focal_loss, num_classes):
211
-
212
- cls_weights = np.ones([num_classes], np.float32)
213
- val_loss = 0 # 记录总的验证损失
214
-
215
- # 设置模型为验证模式
216
- model_eval = model.eval()
217
- model_eval = model_eval.cuda()
218
-
219
- # 初始化累积变量
220
- total_pixel_acc = 0
221
- total_mean_acc = 0
222
- total_mean_iou = 0
223
- total_fw_iou = 0
224
- num_batches = len(val_loader)
225
-
226
- # 遍历验证数据,前向传播
227
- with torch.no_grad():
228
- for iteration, batch in enumerate(val_loader):
229
- imgs, pngs, labels = batch # 获取验证数据
230
-
231
- # 数据准备阶段
232
- weights = torch.tensor(cls_weights).to(device) # 转换类别权重并移动到GPU
233
- imgs = imgs.to(device)
234
- pngs = pngs.to(device)
235
- labels = labels.to(device)
236
- outputs = model_eval(imgs)
237
-
238
- # print("outputs", outputs.shape)
239
- # print("pngs", pngs.shape)
240
- # 损失计算
241
- if focal_loss:
242
- loss = Focal_Loss(outputs, pngs, weights, num_classes=num_classes)
243
- else:
244
- loss = CE_Loss(outputs, pngs, weights, num_classes=num_classes)
245
-
246
- if dice_loss:
247
- main_dice = Dice_loss(outputs, labels)
248
- # main_dice = Dice_loss(outputs, pngs)
249
- loss = loss + main_dice
250
-
251
- # 计算各个指标
252
- pixel_acc = pixel_accuracy(outputs, pngs)
253
- mean_acc = mean_accuracy(outputs, pngs, num_classes)
254
- mean_iou_value = mean_iou(outputs, pngs, num_classes)
255
- fw_iou = frequency_weighted_iou(outputs, pngs, num_classes)
256
-
257
- # 累加到总结果
258
- total_pixel_acc += pixel_acc
259
- total_mean_acc += mean_acc
260
- total_mean_iou += mean_iou_value
261
- total_fw_iou += fw_iou
262
- val_loss += loss.item()
263
-
264
- # 打印标题(每个epoch开始时打印一次)
265
- if iteration == 0: # 只在第一个 batch 打印标题
266
- epoch_len = len("Epoch") + 12
267
- data_num_len = len("data_num") - len("data_num") + 12
268
- Pixelacc_len = len("GPU Mem") - len("Pixelacc") + 12
269
- Meanacc_len = len("Loss") - len("Meanacc") + 12
270
- Meaniou_len = len("LR") - len("Meaniou") + 12
271
-
272
- print(f"{' ' * epoch_len}"
273
- f"{LogColor.RED}data_num{LogColor.RESET}{' ' * data_num_len}"
274
- f"{LogColor.RED}Pixelacc{LogColor.RESET}{' ' * Pixelacc_len}"
275
- f"{LogColor.RED}Meanacc{LogColor.RESET}{' ' * Meanacc_len}"
276
- f"{LogColor.RED}Meaniou{LogColor.RESET}{' ' * Meaniou_len}"
277
- f"{LogColor.RED}Fwiou{LogColor.RESET}")
278
-
279
- # 计算平均值
280
- avg_pixel_acc = total_pixel_acc / num_batches
281
- avg_mean_acc = total_mean_acc / num_batches
282
- avg_mean_iou = total_mean_iou / num_batches
283
- avg_fw_iou = total_fw_iou / num_batches
284
- avg_loss = val_loss / num_batches # ➕ 平均 loss
285
-
286
-
287
- # 将结果保存到字典中
288
- metrics = {
289
- 'Pixel Accuracy': avg_pixel_acc,
290
- 'Mean Accuracy': avg_mean_acc,
291
- 'Mean IoU': avg_mean_iou,
292
- 'Frequency Weighted IoU': avg_fw_iou,
293
- 'Loss': avg_loss # ➕ 加入字典
294
- }
295
-
296
- epoch_len = len("Epoch") + 12
297
- batch_len = data_num_len + len("data_num") - len(str(f"{iteration + 1}/{len(val_loader)}"))
298
- avg_pixel_acc_len = Pixelacc_len + len("Pixelacc") - len(str(f"{avg_pixel_acc:.2f}"))
299
- avg_mean_acc_len = Meanacc_len + len("Meanacc") - len(str(f"{avg_mean_acc:.2f}"))
300
- avg_Mean_iou_len = Meaniou_len + len("Meaniou") - len(str(f"{avg_mean_iou:.2f}"))
301
-
302
- # 使用 \r 在同一行更新输出
303
- print(f"{' ' * (epoch_len)}"
304
- f"{iteration + 1}/{len(val_loader)}{' ' * batch_len}"
305
- f"{avg_pixel_acc:.2f}{' ' * avg_pixel_acc_len}"
306
- f"{avg_mean_acc:.2f}{' ' * avg_mean_acc_len}"
307
- f"{avg_mean_iou:.2f}{' ' * avg_Mean_iou_len}"
308
- f"{avg_fw_iou:.2f}", end='', flush=True)
309
- print(f"\n{LogColor.GREEN}")
310
- time.sleep(1) # 加一点延迟,防止输出闪烁过快
311
-
312
- return metrics
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils/utils.py DELETED
@@ -1,72 +0,0 @@
1
- import random
2
-
3
- import numpy as np
4
- import torch
5
- from PIL import Image
6
-
7
-
8
- # ---------------------------------------------------------#
9
- # 将图像转换成RGB图像,防止灰度图在预测时报错。
10
- # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB
11
- # ---------------------------------------------------------#
12
- def cvtColor(image):
13
- if len(np.shape(image)) == 3 and np.shape(image)[2] == 3:
14
- return image
15
- else:
16
- image = image.convert('RGB')
17
- return image
18
-
19
- # ---------------------------------------------------#
20
-
21
-
22
- # 对输入图像进行resize
23
- # ---------------------------------------------------#
24
- def resize_image(image, size):
25
- iw, ih = image.size
26
- w, h = size
27
-
28
- scale = min(w / iw, h / ih)
29
- nw = int(iw * scale)
30
- nh = int(ih * scale)
31
-
32
- image = image.resize((nw, nh), Image.BICUBIC)
33
- new_image = Image.new('RGB', size, (128, 128, 128))
34
- new_image.paste(image, ((w - nw) // 2, (h - nh) // 2))
35
-
36
- return new_image, nw, nh
37
-
38
-
39
- # ---------------------------------------------------#
40
- # 获得学习率
41
- # ---------------------------------------------------#
42
- def get_lr(optimizer):
43
- for param_group in optimizer.param_groups:
44
- return param_group['lr']
45
-
46
-
47
- # ---------------------------------------------------#
48
- # 设置种子
49
- # ---------------------------------------------------#
50
- def seed_everything(seed=11):
51
- random.seed(seed)
52
- np.random.seed(seed)
53
- torch.manual_seed(seed)
54
- torch.cuda.manual_seed(seed)
55
- torch.cuda.manual_seed_all(seed)
56
- torch.backends.cudnn.deterministic = True
57
- torch.backends.cudnn.benchmark = False
58
-
59
-
60
- # ---------------------------------------------------#
61
- # 设置Dataloader的种子
62
- # ---------------------------------------------------#
63
- def worker_init_fn(rank, seed):
64
- worker_seed = rank + seed
65
- random.seed(worker_seed)
66
- np.random.seed(worker_seed)
67
- torch.manual_seed(worker_seed)
68
-
69
-
70
- def preprocess_input(image):
71
- image /= 255.0
72
- return image